shogi-server source
Revisão | 48caf92ecf5d5669a5ccef83bf351f612e8aab70 (tree) |
---|---|
Hora | 2013-12-13 15:09:35 |
Autor | Daigo Moriwaki <daigo@debi...> |
Commiter | Daigo Moriwaki |
[shogi-server] - Implemented conversion of move representation between CSA format and USI one.
@@ -1,3 +1,9 @@ | ||
1 | +2013-12-13 Daigo Moriwaki <daigo at debian dot org> | |
2 | + | |
3 | + * [shogi-server] | |
4 | + - Implemented conversion of move representation between CSA format | |
5 | + and USI one. | |
6 | + | |
1 | 7 | 2013-11-24 Daigo Moriwaki <daigo at debian dot org> |
2 | 8 | |
3 | 9 | * [shogi-server] |
@@ -94,6 +94,7 @@ EOF | ||
94 | 94 | @move_count = move_count |
95 | 95 | @teban = nil # black => true, white => false |
96 | 96 | @initial_moves = [] |
97 | + @move = nil | |
97 | 98 | @ous = [nil, nil] # keep OU pieces of Sente and Gote |
98 | 99 | end |
99 | 100 | attr_accessor :array, :sente_hands, :gote_hands, :history, :sente_history, :gote_history, :teban |
@@ -104,6 +105,11 @@ EOF | ||
104 | 105 | # moves. |
105 | 106 | attr_reader :initial_moves |
106 | 107 | |
108 | + # A move parsed by handle_one_move. If the move is not :normal, the board | |
109 | + # position may or may not be rolled back. | |
110 | + # | |
111 | + attr_reader :move | |
112 | + | |
107 | 113 | # See if self equals rhs, including a logical board position (i.e. |
108 | 114 | # not see object IDs) and sennichite stuff. |
109 | 115 | # |
@@ -678,18 +684,18 @@ EOF | ||
678 | 684 | return :illegal # can't put on existing piece |
679 | 685 | end |
680 | 686 | |
681 | - move = Move.new(x0, y0, x1, y1, name, sente) | |
682 | - result = move_to(move) | |
687 | + @move = Move.new(x0, y0, x1, y1, name, sente) | |
688 | + result = move_to(@move) | |
683 | 689 | if (result == :illegal) |
684 | 690 | # self is unchanged |
685 | 691 | return :illegal |
686 | 692 | end |
687 | 693 | if (checkmated?(sente)) |
688 | - move_back(move) | |
694 | + move_back(@move) | |
689 | 695 | return :oute_kaihimore |
690 | 696 | end |
691 | 697 | if ((x0 == 0) && (y0 == 0) && (name == "FU") && uchifuzume?(sente)) |
692 | - move_back(move) | |
698 | + move_back(@move) | |
693 | 699 | return :uchifuzume |
694 | 700 | end |
695 | 701 |
@@ -697,12 +703,12 @@ EOF | ||
697 | 703 | update_sennichite(sente) |
698 | 704 | os_result = oute_sennichite?(sente) |
699 | 705 | if os_result # :oute_sennichite_sente_lose or :oute_sennichite_gote_lose |
700 | - move_back(move) | |
706 | + move_back(@move) | |
701 | 707 | restore_sennichite_stuff(*sennichite_stuff) |
702 | 708 | return os_result |
703 | 709 | end |
704 | 710 | if sennichite? |
705 | - move_back(move) | |
711 | + move_back(@move) | |
706 | 712 | restore_sennichite_stuff(*sennichite_stuff) |
707 | 713 | return :sennichite |
708 | 714 | end |
@@ -154,18 +154,17 @@ class Piece | ||
154 | 154 | @promoted_name |
155 | 155 | end |
156 | 156 | |
157 | + def current_name | |
158 | + return @promoted ? @promoted_name : @name | |
159 | + end | |
160 | + | |
157 | 161 | def to_s |
158 | 162 | if (@sente) |
159 | 163 | sg = "+" |
160 | 164 | else |
161 | 165 | sg = "-" |
162 | 166 | end |
163 | - if (@promoted) | |
164 | - n = @promoted_name | |
165 | - else | |
166 | - n = @name | |
167 | - end | |
168 | - return sg + n | |
167 | + return sg + current_name | |
169 | 168 | end |
170 | 169 | end |
171 | 170 |
@@ -13,7 +13,181 @@ module ShogiServer # for a namespace | ||
13 | 13 | gsub("@", "+"). |
14 | 14 | gsub(".", " ") |
15 | 15 | end |
16 | - 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 | |
17 | 191 | |
18 | 192 | def charToPiece(c) |
19 | 193 | player = nil |
@@ -164,6 +338,7 @@ module ShogiServer # for a namespace | ||
164 | 338 | s += hands2usi(board.gote_hands).downcase |
165 | 339 | return s |
166 | 340 | end |
341 | + | |
167 | 342 | end # class |
168 | 343 | |
169 | 344 | end # module |
@@ -84,4 +84,22 @@ P-00FU00FU | ||
84 | 84 | EOB |
85 | 85 | assert_equal "lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b 2p", @usi.board2usi(b, b.teban) |
86 | 86 | end |
87 | + | |
88 | + def test_usiToCsa | |
89 | + # 26th Ryuousen 5th match Moriuchi vs Watanabe on Nov 28th, 2013 | |
90 | + usi_moves = %w!7g7f 8c8d 7i6h 3c3d 6g6f 7a6b 5g5f 5c5d 3i4h 3a4b 4i5h 4a3b 6i7h 5a4a 5i6i 6a5b 6h7g 4b3c 8h7i 2b3a 3g3f 4c4d 4h3g 3a6d 5h6g 7c7d 7i6h 5b4c 6i7i 4a3a 7i8h 9c9d 3g4f 6b5c 2i3g 6d7c 1g1f 1c1d 2g2f 3c2d 2h3h 9d9e 1i1h 3a2b 3g2e 4d4e 4f4e 7c1i+ 6h4f 1i4f 4g4f B*5i B*3g 5i3g+ 3h3g B*1i 3g3h 1i4f+ P*4d 4c4d B*7a 4d4c 6g5g 4f5g 7a8b+ P*4d 4e3d 4c3d R*5a 5c4b 5a8a+ S*6i 7h6h 5g6h 3h6h G*5h 8b4f 5h6h 4f6h R*4h G*7i 6i5h+ 6h7h G*6i 7i6i 5h6i B*5g 4h1h+ P*4h 1h2g 5g2d 3d2d 7h6i L*6g S*6h 6g6h+ 6i6h S*5g N*3c 4b3c 2e3c+ 2b3c L*3e P*3d 8a2a G*3a 2a1a 5g6h 7g6h N*6d S*6g 3d3e S*5c 2g3f N*2e 3c4c 5c6d+ 6c6d G*3c 4c5c 3c3b 3a3b N*5e 5d5e 1a5a L*5b L*5d 5c5d 5a5b 5d4e L*4g 3f4g 4h4g 4e3f G*3h! | |
91 | + uc = ShogiServer::Usi::UsiToCsa.new | |
92 | + usi_moves.each do |m| | |
93 | + state, csa = uc.next(m) | |
94 | + assert_equal(:normal, state) | |
95 | + end | |
96 | + | |
97 | + cu = ShogiServer::Usi::CsaToUsi.new | |
98 | + uc.csa_moves.each do |m| | |
99 | + state, usi = cu.next(m) | |
100 | + assert_equal(:normal, state) | |
101 | + end | |
102 | + | |
103 | + assert_equal(usi_moves, cu.usi_moves) | |
104 | + end | |
87 | 105 | end |