OSDN Git Service

39ca24ecb62fbd4af5ec30d130c91421ed000aa9
[shogi-server/shogi-server.git] / shogi_server / usi.rb
1 module ShogiServer # for a namespace
2
3   class Usi
4     class << Usi
5       def escape(str)
6         str.gsub("/", "_").
7             gsub("+", "@").
8             gsub(" ", ".")
9       end
10
11       def unescape(str)
12         str.gsub("_", "/").
13             gsub("@", "+").
14             gsub(".", " ")
15       end
16
17       # 1 -> a
18       # 2 -> b
19       # ...
20       # 9 -> i
21       def danToAlphabet(int)
22         return (int+96).chr
23       end
24
25       # a -> 1
26       # b -> 2
27       # ...
28       # i -> 9
29       def alphabetToDan(s)
30         if RUBY_VERSION >= "1.9.1"
31           return s.bytes[0]-96
32         else
33           return s[0]-96
34         end
35       end
36
37       def csaPieceToUsi(csa, sente)
38         str = ""
39         case csa
40         when "FU"
41           str = "p"
42         when "KY"
43           str = "l"
44         when "KE"
45           str = "n"
46         when "GI"
47           str = "s"
48         when "KI"
49           str = "g"
50         when "KA"
51           str = "b"
52         when "HI"
53           str = "r"
54         when "OU"
55           str = "k"
56         when "TO"
57           str = "+p"
58         when "NY"
59           str = "+l"
60         when "NK"
61           str = "+n"
62         when "NG"
63           str = "+s"
64         when "UM"
65           str = "+b"
66         when "RY"
67           str = "+r"
68         end
69         return sente ? str.upcase : str
70       end
71
72       def usiPieceToCsa(str)
73         ret = ""
74         case str.downcase
75         when "p"
76           ret = "FU"
77         when "l"
78           ret = "KY"
79         when "n"
80           ret = "KE"
81         when "s"
82           ret = "GI"
83         when "g"
84           ret = "KI"
85         when "b"
86           ret = "KA"
87         when "r"
88           ret = "HI"
89         when "+p"
90           ret = "TO"
91         when "+l"
92           ret = "NY"
93         when "+n"
94           ret = "NK"
95         when "+s"
96           ret = "NG"
97         when "+b"
98           ret = "UM"
99         when "+r"
100           ret = "RY"
101         when "k"
102           ret = "OU"
103         end
104         return ret
105       end
106
107       def moveToUsi(move)
108         str = ""
109         if move.is_drop?
110           str += "%s*%s%s" % [csaPieceToUsi(move.name, move.sente).upcase, move.x1, danToAlphabet(move.y1)]
111         else
112           str += "%s%s%s%s" % [move.x0, danToAlphabet(move.y0), move.x1, danToAlphabet(move.y1)]
113           str += "+" if move.promotion
114         end
115
116         return str
117       end
118
119       def usiToCsa(str, board, sente)
120         ret = ""
121         if str[1..1] == "*" 
122           # drop
123           ret += "00%s%s%s" % [str[2..2], alphabetToDan(str[3..3]), usiPieceToCsa(str[0..0])]
124         else
125           from_x = str[0..0]
126           from_y = alphabetToDan(str[1..1])
127           ret += "%s%s%s%s" % [from_x, from_y, str[2..2], alphabetToDan(str[3..3])]
128           csa_piece = board.array[from_x.to_i][from_y.to_i]
129           if str.size == 5 && str[4..4] == "+"
130             # Promoting move
131             ret += csa_piece.promoted_name
132           else
133             ret += csa_piece.current_name
134           end
135         end
136         return (sente ? "+" : "-") + ret
137       end
138     end # class methods
139
140     # Convert USI moves to CSA one by one from the initial position
141     #
142     class UsiToCsa
143       attr_reader :board, :csa_moves, :usi_moves
144
145       # Constructor
146       #
147       def initialize
148         @board = ShogiServer::Board.new
149         @board.initial
150         @sente = true
151         @csa_moves = []
152         @usi_moves = []
153       end
154
155       def deep_copy
156         return Marshal.load(Marshal.dump(self))
157       end
158
159       # Parses a usi move string and returns an array of [move_result_state,
160       # csa_move_string]
161       #
162       def next(usi)
163         usi_moves << usi
164         csa = Usi.usiToCsa(usi, @board, @sente)
165         state = @board.handle_one_move(csa, @sente)
166         @sente = !@sente
167         @csa_moves << csa
168         return [state, csa]
169       end
170
171     end # class UsiToCsa
172
173     # Convert CSA moves to USI one by one from the initial position
174     #
175     class CsaToUsi
176       attr_reader :board, :csa_moves, :usi_moves
177
178       # Constructor
179       #
180       def initialize
181         @board = ShogiServer::Board.new
182         @board.initial
183         @sente = true
184         @csa_moves = []
185         @usi_moves = []
186       end
187
188       def deep_copy
189         return Marshal.load(Marshal.dump(self))
190       end
191       
192       # Parses a csa move string and returns an array of [move_result_state,
193       # usi_move_string]
194       #
195       def next(csa)
196         csa_moves << csa
197         state = @board.handle_one_move(csa, @sente)
198         @sente = !@sente
199         usi = Usi.moveToUsi(@board.move)
200         @usi_moves << usi
201         return [state, usi]
202       end
203     end # class CsaToUsi
204
205     def charToPiece(c)
206       player = nil
207       case c
208       when /[A-Z]/
209         player = true
210       when /[a-z]/
211         player = false
212       end
213
214       piece = nil
215       case c.upcase
216       when 'P' 
217         piece = PieceFU
218       when 'L' 
219         piece = PieceKY
220       when 'N' 
221         piece = PieceKE
222       when 'S' 
223         piece = PieceGI
224       when 'G' 
225         piece = PieceKI
226       when 'B' 
227         piece = PieceKA
228       when 'R' 
229         piece = PieceHI
230       when 'K' 
231         piece = PieceOU
232       end
233       return [piece, player]
234     end
235
236     def piece2char(piece)
237       s = ""
238       case piece
239       when PieceFU
240         s = 'P'
241       when PieceKY
242         s = 'L'
243       when PieceKE
244         s = 'N'
245       when PieceGI
246         s = 'S'
247       when PieceKI
248         s = 'G'
249       when PieceKA
250         s = 'B'
251       when PieceHI
252         s = 'R'
253       when PieceOU
254         s = 'K'
255       end
256       s.downcase! if !piece.sente
257       if piece.promoted
258         s = "+%s" % [s]
259       end
260       return s
261     end
262
263     def parseBoard(word, board)
264       x=9; y=1
265       i = 0
266       while (i < word.length)
267         c = word[i,1]
268         case c
269         when /[a-zA-Z]/
270           piece, player = charToPiece(c)
271           piece.new(board, x, y, player)
272           x -= 1
273         when "+"
274           cc = word[i+i]
275           piece, player = charToPiece(cc)
276           piece.new(board, x, y, player, true)
277           x -= 1
278           i += 1
279         when /\d/
280           x -= c.to_i
281         when "/"
282           x = 9
283           y += 1
284         else
285           return 1
286         end
287         i += 1
288       end
289       return 0
290     end
291
292     def hands2usi(hands) 
293       return "" if hands.empty?
294       s = ""
295
296       mapping = [[ShogiServer::PieceHI, "R"],
297                  [ShogiServer::PieceKA, "B"],
298                  [ShogiServer::PieceKI, "G"],
299                  [ShogiServer::PieceGI, "S"],
300                  [ShogiServer::PieceKE, "N"],
301                  [ShogiServer::PieceKY, "L"],
302                  [ShogiServer::PieceFU, "P"]]
303
304       mapping.each do |klass, str|
305         pieces = hands.find_all {|piece| piece.class == klass}
306         unless pieces.empty?
307           if pieces.size > 1 
308             s += "%d" % [pieces.size]
309           end
310           s += str
311         end
312       end
313       return s
314     end
315
316     # "lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b -"
317     #
318     def board2usi(board, turn)
319       s = ""
320       for y in 1..9
321         skip = 0
322         9.downto(1) do |x| 
323           piece = board.array[x][y]
324           case piece 
325           when nil
326             skip += 1
327           when ShogiServer::Piece
328             if skip > 0
329               s += skip.to_s
330               skip = 0
331             end
332             s += piece2char(piece)
333           end
334         end
335         if skip > 0
336           s += skip.to_s
337         end
338         s += "/" if y < 9
339       end
340       s += " "
341       if turn
342         s += "b"
343       else
344         s += "w"
345       end
346       s += " "
347       if board.sente_hands.empty? && board.gote_hands.empty?
348         return s += "-"
349       end
350       s += hands2usi(board.sente_hands).upcase
351       s += hands2usi(board.gote_hands).downcase
352       return s
353     end
354
355   end # class
356
357 end # module