OSDN Git Service

f18f8c342a9a82b2ba16a13f98185db14d20edbd
[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
144
145       # Constructor
146       #
147       def initialize
148         @board = ShogiServer::Board.new
149         @board.initial
150         @sente = true
151         @csa_moves = []
152       end
153
154       # Parses a usi move string and returns an array of [move_result_state,
155       # csa_move_string]
156       #
157       def next(usi)
158         csa = Usi.usiToCsa(usi, @board, @sente)
159         state = @board.handle_one_move(csa, @sente)
160         @sente = !@sente
161         @csa_moves << csa
162         return [state, csa]
163       end
164     end # class UsiToCsa
165
166     # Convert CSA moves to USI one by one from the initial position
167     #
168     class CsaToUsi
169       attr_reader :board, :usi_moves
170
171       # Constructor
172       #
173       def initialize
174         @board = ShogiServer::Board.new
175         @board.initial
176         @sente = true
177         @usi_moves = []
178       end
179
180       # Parses a csa move string and returns an array of [move_result_state,
181       # usi_move_string]
182       #
183       def next(csa)
184         state = @board.handle_one_move(csa, @sente)
185         @sente = !@sente
186         usi = Usi.moveToUsi(@board.move)
187         @usi_moves << usi
188         return [state, usi]
189       end
190     end # class CsaToUsi
191
192     def charToPiece(c)
193       player = nil
194       case c
195       when /[A-Z]/
196         player = true
197       when /[a-z]/
198         player = false
199       end
200
201       piece = nil
202       case c.upcase
203       when 'P' 
204         piece = PieceFU
205       when 'L' 
206         piece = PieceKY
207       when 'N' 
208         piece = PieceKE
209       when 'S' 
210         piece = PieceGI
211       when 'G' 
212         piece = PieceKI
213       when 'B' 
214         piece = PieceKA
215       when 'R' 
216         piece = PieceHI
217       when 'K' 
218         piece = PieceOU
219       end
220       return [piece, player]
221     end
222
223     def piece2char(piece)
224       s = ""
225       case piece
226       when PieceFU
227         s = 'P'
228       when PieceKY
229         s = 'L'
230       when PieceKE
231         s = 'N'
232       when PieceGI
233         s = 'S'
234       when PieceKI
235         s = 'G'
236       when PieceKA
237         s = 'B'
238       when PieceHI
239         s = 'R'
240       when PieceOU
241         s = 'K'
242       end
243       s.downcase! if !piece.sente
244       if piece.promoted
245         s = "+%s" % [s]
246       end
247       return s
248     end
249
250     def parseBoard(word, board)
251       x=9; y=1
252       i = 0
253       while (i < word.length)
254         c = word[i,1]
255         case c
256         when /[a-zA-Z]/
257           piece, player = charToPiece(c)
258           piece.new(board, x, y, player)
259           x -= 1
260         when "+"
261           cc = word[i+i]
262           piece, player = charToPiece(cc)
263           piece.new(board, x, y, player, true)
264           x -= 1
265           i += 1
266         when /\d/
267           x -= c.to_i
268         when "/"
269           x = 9
270           y += 1
271         else
272           return 1
273         end
274         i += 1
275       end
276       return 0
277     end
278
279     def hands2usi(hands) 
280       return "" if hands.empty?
281       s = ""
282
283       mapping = [[ShogiServer::PieceHI, "R"],
284                  [ShogiServer::PieceKA, "B"],
285                  [ShogiServer::PieceKI, "G"],
286                  [ShogiServer::PieceGI, "S"],
287                  [ShogiServer::PieceKE, "N"],
288                  [ShogiServer::PieceKY, "L"],
289                  [ShogiServer::PieceFU, "P"]]
290
291       mapping.each do |klass, str|
292         pieces = hands.find_all {|piece| piece.class == klass}
293         unless pieces.empty?
294           if pieces.size > 1 
295             s += "%d" % [pieces.size]
296           end
297           s += str
298         end
299       end
300       return s
301     end
302
303     # "lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b -"
304     #
305     def board2usi(board, turn)
306       s = ""
307       for y in 1..9
308         skip = 0
309         9.downto(1) do |x| 
310           piece = board.array[x][y]
311           case piece 
312           when nil
313             skip += 1
314           when ShogiServer::Piece
315             if skip > 0
316               s += skip.to_s
317               skip = 0
318             end
319             s += piece2char(piece)
320           end
321         end
322         if skip > 0
323           s += skip.to_s
324         end
325         s += "/" if y < 9
326       end
327       s += " "
328       if turn
329         s += "b"
330       else
331         s += "w"
332       end
333       s += " "
334       if board.sente_hands.empty? && board.gote_hands.empty?
335         return s += "-"
336       end
337       s += hands2usi(board.sente_hands).upcase
338       s += hands2usi(board.gote_hands).downcase
339       return s
340     end
341
342   end # class
343
344 end # module