OSDN Git Service

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