qi 6.3.1 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +15 -307
  3. data/lib/qi.rb +80 -3
  4. metadata +4 -5
  5. data/lib/qi/position.rb +0 -182
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e72084022a3eaca3cec4ff33c2106c99e2b5da61412049b37180f0ddddd87793
4
- data.tar.gz: 56082cc561866965d8f37178f8e6710d54593e4d67f6a24bd63ec23e5c183094
3
+ metadata.gz: 18ca7bb7fca15a8ce854e8a205160b05a14127970039a904c3ce8ad6f8baf8fc
4
+ data.tar.gz: fc4b915c93488f39835f762b1f89fb44a74ab25ca3faa47fb4a6ea846db66c77
5
5
  SHA512:
6
- metadata.gz: f12a76bed9d203b56fcadf2133ec8e985494827d8e5533a7c4bcbe2c350de43d9ff8254c5b9f66c862ddb137f74fb4522b0f88ccd1d29fbec7d972a3acea5984
7
- data.tar.gz: 057b02a60a91a292bce36d173128e8a22117211d2afc32d5155ff81e289ebe7fb501b31a1c03c148c335c4ed61c3819f7b47fb8a51b0804f6098f3926b7c6a6c
6
+ metadata.gz: 6079a5cb3f9b5b83a5bbc0db41694e6037a46a8e5ef6ce8bd4e65f36863d17008cb5d08d806681c4b17daff89ef2d43abd4e260c4ba068b7ac499fe51a36053c
7
+ data.tar.gz: 164bb2211d24d9119061a9c771438d0b77f96c0e2d51d4f18c3cfe3f82acdeaf50bd532d4eafb071441a64a18be68cefc9bae9a6dcfcf81b633cf3498912e76c
data/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![Inline docs](https://inch-ci.org/github/sashite/qi.rb.svg?branch=master)][inchpages]
6
6
  [![Documentation](https://img.shields.io/:yard-docs-38c800.svg)][rubydoc]
7
7
 
8
- > `Qi` (棋) is an abstraction for initializing and updating positions of chess variants (including Chess, Janggi, Markruk, Shogi, Xiangqi).
8
+ > `Qi` (棋) is an abstraction for updating positions of chess variants (including Chess, Janggi, Markruk, Shogi, Xiangqi), with a move.
9
9
 
10
10
  ## Installation
11
11
 
@@ -25,317 +25,25 @@ Or install it yourself as:
25
25
 
26
26
  ## Examples
27
27
 
28
- Let's replay [The Shortest Possible Game](https://userpages.monmouth.com/~colonel/shortshogi.html) of [Shogi](https://en.wikipedia.org/wiki/Shogi):
29
-
30
- ```ruby
31
- require "qi"
32
-
33
- starting_position = Qi::Position.new(
34
- "l", "n", "s", "g", "k", "g", "s", "n", "l",
35
- nil, "r", nil, nil, nil, nil, nil, "b", nil,
36
- "p", "p", "p", "p", "p", "p", "p", "p", "p",
37
- nil, nil, nil, nil, nil, nil, nil, nil, nil,
38
- nil, nil, nil, nil, nil, nil, nil, nil, nil,
39
- nil, nil, nil, nil, nil, nil, nil, nil, nil,
40
- "P", "P", "P", "P", "P", "P", "P", "P", "P",
41
- nil, "B", nil, nil, nil, nil, nil, "R", nil,
42
- "L", "N", "S", "G", "K", "G", "S", "N", "L"
43
- )
44
-
45
- starting_position.in_hand_pieces
46
- # => []
47
-
48
- starting_position.squares
49
- # => ["l", "n", "s", "g", "k", "g", "s", "n", "l",
50
- # nil, "r", nil, nil, nil, nil, nil, "b", nil,
51
- # "p", "p", "p", "p", "p", "p", "p", "p", "p",
52
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
53
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
54
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
55
- # "P", "P", "P", "P", "P", "P", "P", "P", "P",
56
- # nil, "B", nil, nil, nil, nil, nil, "R", nil,
57
- # "L", "N", "S", "G", "K", "G", "S", "N", "L"]
58
-
59
- starting_position.pieces_in_hand_grouped_by_sides
60
- # => [[], []]
61
-
62
- starting_position.active_side_id
63
- # => 0
64
-
65
- # List of moves (see https://github.com/sashite/pmn.rb)
66
- moves = [
67
- [ 56, 47, "P" ],
68
- [ 3, 11, "g" ],
69
- [ 64, 24, "+B", "P" ],
70
- [ 5, 14, "g" ],
71
- [ 24, 14, "+B", "G" ],
72
- [ 4, 3, "k" ],
73
- [ nil, 13, "G" ]
74
- ]
75
-
76
- last_position = moves.reduce(starting_position) do |position, move|
77
- position.call(move)
78
- end
79
-
80
- last_position.in_hand_pieces
81
- # => []
82
-
83
- last_position.squares
84
- # => ["l", "n", "s", "k", nil, nil, "s", "n", "l",
85
- # nil, "r", "g", nil, "G", "+B", nil, "b", nil,
86
- # "p", "p", "p", "p", "p", "p", nil, "p", "p",
87
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
88
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
89
- # nil, nil, "P", nil, nil, nil, nil, nil, nil,
90
- # "P", "P", nil, "P", "P", "P", "P", "P", "P",
91
- # nil, nil, nil, nil, nil, nil, nil, "R", nil,
92
- # "L", "N", "S", "G", "K", "G", "S", "N", "L"]
93
-
94
- last_position.pieces_in_hand_grouped_by_sides
95
- # => [["P"], []]
96
-
97
- last_position.active_side_id
98
- # => 1
99
- ```
100
-
101
- A classic [Tsume Shogi](https://en.wikipedia.org/wiki/Tsume_shogi) problem:
102
-
103
28
  ```ruby
104
29
  require "qi"
105
30
 
106
- starting_position = Qi::Position.new(
107
- nil, nil, nil, "s", "k", "s", nil, nil, nil,
108
- nil, nil, nil, nil, nil, nil, nil, nil, nil,
109
- nil, nil, nil, nil, "+P", nil, nil, nil, nil,
110
- nil, nil, nil, nil, nil, nil, nil, nil, nil,
111
- nil, nil, nil, nil, nil, nil, nil, "+B", nil,
112
- nil, nil, nil, nil, nil, nil, nil, nil, nil,
113
- nil, nil, nil, nil, nil, nil, nil, nil, nil,
114
- nil, nil, nil, nil, nil, nil, nil, nil, nil,
115
- nil, nil, nil, nil, nil, nil, nil, nil, nil,
116
- pieces_in_hand_grouped_by_sides: [
117
- ["S"],
118
- ["b", "g", "g", "g", "g", "n", "n", "n", "n", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "r", "r", "s"]
31
+ Qi.call(
32
+ [43, 13, "+B"],
33
+ "side_id": 0,
34
+ "board": {
35
+ 3 => "s",
36
+ 4 => "k",
37
+ 5 => "s",
38
+ 22 => "+P",
39
+ 43 => "+B"
40
+ },
41
+ "hands": [
42
+ %w[S],
43
+ %w[r r b g g g g s n n n n p p p p p p p p p p p p p p p p p]
119
44
  ]
120
45
  )
121
-
122
- starting_position.in_hand_pieces
123
- # => ["S"]
124
-
125
- starting_position.squares
126
- # => [nil, nil, nil, "s", "k", "s", nil, nil, nil,
127
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
128
- # nil, nil, nil, nil, "+P", nil, nil, nil, nil,
129
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
130
- # nil, nil, nil, nil, nil, nil, nil, "+B", nil,
131
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
132
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
133
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
134
- # nil, nil, nil, nil, nil, nil, nil, nil, nil]
135
-
136
- starting_position.pieces_in_hand_grouped_by_sides
137
- # => [["S"], ["b", "g", "g", "g", "g", "n", "n", "n", "n", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "r", "r", "s"]]
138
-
139
- starting_position.active_side_id
140
- # => 0
141
-
142
- # List of moves (see https://github.com/sashite/pmn.rb)
143
- moves = [
144
- [ 43, 13, "+B" ], [ 5, 13, "s", "b" ],
145
- [ nil, 14, "S" ]
146
- ]
147
-
148
- last_position = moves.reduce(starting_position) do |position, move|
149
- position.call(move)
150
- end
151
-
152
- last_position.in_hand_pieces
153
- # => ["b", "g", "g", "g", "g", "n", "n", "n", "n", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "r", "r", "s", "b"]
154
-
155
- last_position.squares
156
- # => [nil, nil, nil, "s", "k", nil, nil, nil, nil,
157
- # nil, nil, nil, nil, "s", "S", nil, nil, nil,
158
- # nil, nil, nil, nil, "+P", nil, nil, nil, nil,
159
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
160
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
161
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
162
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
163
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
164
- # nil, nil, nil, nil, nil, nil, nil, nil, nil]
165
-
166
- last_position.pieces_in_hand_grouped_by_sides
167
- # => [[], ["b", "g", "g", "g", "g", "n", "n", "n", "n", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "r", "r", "s", "b"]]
168
-
169
- last_position.active_side_id
170
- # => 1
171
- ```
172
-
173
- Another example with [Xiangqi](https://en.wikipedia.org/wiki/Xiangqi)'s Short Double Cannons Checkmate:
174
-
175
- ```ruby
176
- require "qi"
177
-
178
- starting_position = Qi::Position.new(
179
- "車", "馬", "象", "士", "將", "士", "象", "馬", "車",
180
- nil, nil, nil, nil, nil, nil, nil, nil, nil,
181
- nil, "砲", nil, nil, nil, nil, nil, "砲", nil,
182
- "卒", nil, "卒", nil, "卒", nil, "卒", nil, "卒",
183
- nil, nil, nil, nil, nil, nil, nil, nil, nil,
184
- nil, nil, nil, nil, nil, nil, nil, nil, nil,
185
- "兵", nil, "兵", nil, "兵", nil, "兵", nil, "兵",
186
- nil, "炮", nil, nil, nil, nil, nil, "炮", nil,
187
- nil, nil, nil, nil, nil, nil, nil, nil, nil,
188
- "俥", "傌", "相", "仕", "帥", "仕", "相", "傌", "俥"
189
- )
190
-
191
- starting_position.in_hand_pieces
192
- # => []
193
-
194
- starting_position.squares
195
- # => ["車", "馬", "象", "士", "將", "士", "象", "馬", "車",
196
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
197
- # nil, "砲", nil, nil, nil, nil, nil, "砲", nil,
198
- # "卒", nil, "卒", nil, "卒", nil, "卒", nil, "卒",
199
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
200
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
201
- # "兵", nil, "兵", nil, "兵", nil, "兵", nil, "兵",
202
- # nil, "炮", nil, nil, nil, nil, nil, "炮", nil,
203
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
204
- # "俥", "傌", "相", "仕", "帥", "仕", "相", "傌", "俥"]
205
-
206
- starting_position.pieces_in_hand_grouped_by_sides
207
- # => [[], []]
208
-
209
- starting_position.active_side_id
210
- # => 0
211
-
212
- # List of moves (see https://github.com/sashite/pmn.rb)
213
- moves = [
214
- [ 64, 67, "炮" ],
215
- [ 25, 22, "砲" ],
216
- [ 70, 52, "炮" ],
217
- [ 19, 55, "砲" ],
218
- [ 67, 31, "炮" ],
219
- [ 22, 58, "砲" ],
220
- [ 52, 49, "炮" ]
221
- ]
222
-
223
- last_position = moves.reduce(starting_position) do |position, move|
224
- position.call(move)
225
- end
226
-
227
- last_position.in_hand_pieces
228
- # => []
229
-
230
- last_position.squares
231
- # => ["車", "馬", "象", "士", "將", "士", "象", "馬", "車",
232
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
233
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
234
- # "卒", nil, "卒", nil, "炮", nil, "卒", nil, "卒",
235
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
236
- # nil, nil, nil, nil, "炮", nil, nil, nil, nil,
237
- # "兵", "砲", "兵", nil, "砲", nil, "兵", nil, "兵",
238
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
239
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
240
- # "俥", "傌", "相", "仕", "帥", "仕", "相", "傌", "俥"]
241
-
242
- last_position.pieces_in_hand_grouped_by_sides
243
- # => [[], []]
244
-
245
- last_position.active_side_id
246
- # => 1
247
- ```
248
-
249
- Let's do some moves on a [Four-player chess](https://en.wikipedia.org/wiki/Four-player_chess) board:
250
-
251
- ```ruby
252
- require "qi"
253
-
254
- starting_position = Qi::Position.new(
255
- nil , nil , nil , "yR", "yN", "yB", "yK", "yQ", "yB", "yN", "yR", nil , nil , nil ,
256
- nil , nil , nil , "yP", "yP", "yP", "yP", "yP", "yP", "yP", "yP", nil , nil , nil ,
257
- nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil ,
258
- "bR", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gR",
259
- "bN", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gN",
260
- "bB", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gB",
261
- "bK", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gQ",
262
- "bQ", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gK",
263
- "bB", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gB",
264
- "bN", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gN",
265
- "bR", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gR",
266
- nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil ,
267
- nil , nil , nil , "rP", "rP", "rP", "rP", "rP", "rP", "rP", "rP", nil , nil , nil ,
268
- nil , nil , nil , "rR", "rN", "rB", "rQ", "rK", "rB", "rN", "rR", nil , nil , nil ,
269
- pieces_in_hand_grouped_by_sides: [
270
- [],
271
- [],
272
- [],
273
- []
274
- ]
275
- )
276
-
277
- starting_position.in_hand_pieces
278
- # => []
279
-
280
- starting_position.squares
281
- # => [nil , nil , nil , "yR", "yN", "yB", "yK", "yQ", "yB", "yN", "yR", nil , nil , nil ,
282
- # nil , nil , nil , "yP", "yP", "yP", "yP", "yP", "yP", "yP", "yP", nil , nil , nil ,
283
- # nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil ,
284
- # "bR", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gR",
285
- # "bN", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gN",
286
- # "bB", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gB",
287
- # "bK", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gQ",
288
- # "bQ", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gK",
289
- # "bB", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gB",
290
- # "bN", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gN",
291
- # "bR", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gR",
292
- # nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil ,
293
- # nil , nil , nil , "rP", "rP", "rP", "rP", "rP", "rP", "rP", "rP", nil , nil , nil ,
294
- # nil , nil , nil , "rR", "rN", "rB", "rQ", "rK", "rB", "rN", "rR", nil , nil , nil]
295
-
296
- starting_position.pieces_in_hand_grouped_by_sides
297
- # => [[], [], [], []]
298
-
299
- starting_position.active_side_id
300
- # => 0
301
-
302
- # List of moves (see https://github.com/sashite/pmn.rb)
303
- moves = [
304
- [ 175, 147, "rP" ],
305
- [ 85, 87, "bP" ],
306
- [ 20, 48, "yP" ],
307
- [ 110, 108, "gP" ],
308
- [ 191, 162, "rN" ]
309
- ]
310
-
311
- last_position = moves.reduce(starting_position) do |position, move|
312
- position.call(move)
313
- end
314
-
315
- last_position.in_hand_pieces
316
- # => []
317
-
318
- last_position.squares
319
- # => [nil , nil , nil , "yR", "yN", "yB", "yK", "yQ", "yB", "yN", "yR", nil , nil , nil ,
320
- # nil , nil , nil , "yP", "yP", "yP", nil , "yP", "yP", "yP", "yP", nil , nil , nil ,
321
- # nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil ,
322
- # "bR", "bP", nil , nil , nil , nil , "yP", nil , nil , nil , nil , nil , "gP", "gR",
323
- # "bN", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gN",
324
- # "bB", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gB",
325
- # "bK", nil , nil , "bP", nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gQ",
326
- # "bQ", "bP", nil , nil , nil , nil , nil , nil , nil , nil , "gP", nil , nil , "gK",
327
- # "bB", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gB",
328
- # "bN", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gN",
329
- # "bR", "bP", nil , nil , nil , nil , nil , "rP", nil , nil , nil , nil , "gP", "gR",
330
- # nil , nil , nil , nil , nil , nil , nil , nil , "rN", nil , nil , nil , nil , nil ,
331
- # nil , nil , nil , "rP", "rP", "rP", "rP", nil , "rP", "rP", "rP", nil , nil , nil ,
332
- # nil , nil , nil , "rR", "rN", "rB", "rQ", "rK", "rB", nil , "rR", nil , nil , nil]
333
-
334
- last_position.pieces_in_hand_grouped_by_sides
335
- # => [[], [], [], []]
336
-
337
- last_position.active_side_id
338
- # => 1
46
+ # => {:side_id=>1, :board=>{3=>"s", 4=>"k", 5=>"s", 22=>"+P", 13=>"+B"}, :hands=>[["S"], ["r", "r", "b", "g", "g", "g", "g", "s", "n", "n", "n", "n", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p"]]}
339
47
  ```
340
48
 
341
49
  ## License
data/lib/qi.rb CHANGED
@@ -1,7 +1,84 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # The Qi module.
3
+ # The Qi abstraction.
4
+ #
5
+ # @example
6
+ # Qi.call(
7
+ # [43, 13, "+B"],
8
+ # "side_id": 0,
9
+ # "board": {
10
+ # 3 => "s",
11
+ # 4 => "k",
12
+ # 5 => "s",
13
+ # 22 => "+P",
14
+ # 43 => "+B"
15
+ # },
16
+ # "hands": [
17
+ # %w[S],
18
+ # %w[r r b g g g g s n n n n p p p p p p p p p p p p p p p p p]
19
+ # ]
20
+ # )
21
+ # # => {:side_id=>1, :board=>{3=>"s", 4=>"k", 5=>"s", 22=>"+P", 13=>"+B"}, :hands=>[["S"], ["r", "r", "b", "g", "g", "g", "g", "s", "n", "n", "n", "n", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p"]]}
4
22
  module Qi
5
- end
23
+ # Apply a move to the position.
24
+ #
25
+ # @param move [Array] The move to play.
26
+ # @param side_id [Integer] The identifier of the player who must play.
27
+ # @param board [Hash] The indexes of each piece on the board.
28
+ # @param hands [Array] The list of pieces in hand grouped by players.
29
+ #
30
+ # @see https://developer.sashite.com/specs/portable-chess-notation
31
+ # @see https://developer.sashite.com/specs/portable-move-notation
32
+ #
33
+ # @example
34
+ # call(
35
+ # [43, 13, "+B"],
36
+ # "side_id": 0,
37
+ # "board": {
38
+ # 3 => "s",
39
+ # 4 => "k",
40
+ # 5 => "s",
41
+ # 22 => "+P",
42
+ # 43 => "+B"
43
+ # },
44
+ # "hands": [
45
+ # %w[S],
46
+ # %w[r r b g g g g s n n n n p p p p p p p p p p p p p p p p p]
47
+ # ]
48
+ # )
49
+ # # => {:side_id=>1, :board=>{3=>"s", 4=>"k", 5=>"s", 22=>"+P", 13=>"+B"}, :hands=>[["S"], ["r", "r", "b", "g", "g", "g", "g", "s", "n", "n", "n", "n", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p"]]}
50
+ #
51
+ # @return [Hash] The next position.
52
+ def self.call(move, side_id:, board:, hands:)
53
+ updated_board = board.dup
54
+ updated_in_hand_pieces = hands.fetch(side_id).dup
55
+
56
+ actions = move.each_slice(4)
57
+
58
+ actions.each do |action|
59
+ src_square_id = action.fetch(0)
60
+ dst_square_id = action.fetch(1)
61
+ moved_piece_name = action.fetch(2)
62
+ captured_piece_name = action.fetch(3, nil)
63
+
64
+ if src_square_id.nil?
65
+ piece_in_hand_id = updated_in_hand_pieces.index(moved_piece_name)
66
+ updated_in_hand_pieces.delete_at(piece_in_hand_id) unless piece_in_hand_id.nil?
67
+ else
68
+ updated_board.delete(src_square_id)
69
+ end
6
70
 
7
- require_relative "qi/position"
71
+ updated_board[dst_square_id] = moved_piece_name
72
+ updated_in_hand_pieces.push(captured_piece_name) unless captured_piece_name.nil?
73
+ end
74
+
75
+ updated_hands = hands.dup
76
+ updated_hands[side_id] = updated_in_hand_pieces
77
+
78
+ {
79
+ side_id: side_id.succ % hands.length,
80
+ board: updated_board,
81
+ hands: updated_hands
82
+ }
83
+ end
84
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qi
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.3.1
4
+ version: 7.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyril Kato
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-26 00:00:00.000000000 Z
11
+ date: 2020-09-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: brutal
@@ -117,7 +117,6 @@ files:
117
117
  - LICENSE.md
118
118
  - README.md
119
119
  - lib/qi.rb
120
- - lib/qi/position.rb
121
120
  homepage: https://developer.sashite.com/specs/
122
121
  licenses:
123
122
  - MIT
@@ -133,7 +132,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
133
132
  requirements:
134
133
  - - ">="
135
134
  - !ruby/object:Gem::Version
136
- version: '0'
135
+ version: 2.7.0
137
136
  required_rubygems_version: !ruby/object:Gem::Requirement
138
137
  requirements:
139
138
  - - ">="
@@ -143,5 +142,5 @@ requirements: []
143
142
  rubygems_version: 3.1.2
144
143
  signing_key:
145
144
  specification_version: 4
146
- summary: Represent positions and play moves.
145
+ summary: Update positions with a move.
147
146
  test_files: []
@@ -1,182 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Qi
4
- # The position class.
5
- #
6
- # @see https://developer.sashite.com/specs/portable-chess-notation
7
- class Position
8
- # Players are identified by a number according to the order in which they
9
- # traditionally play from the starting position.
10
- #
11
- # @!attribute [r] active_side_id
12
- # @return [Integer] The identifier of the player who must play.
13
- attr_reader :active_side_id
14
-
15
- # The list of pieces in hand owned by players.
16
- #
17
- # @!attribute [r] pieces_in_hand_grouped_by_sides
18
- # @return [Array] The list of pieces in hand for each side.
19
- attr_reader :pieces_in_hand_grouped_by_sides
20
-
21
- # The list of squares on the board.
22
- #
23
- # @!attribute [r] squares
24
- # @return [Array] The list of squares on the board.
25
- attr_reader :squares
26
-
27
- # Initialize a position.
28
- #
29
- # @param squares [Array] The list of squares on the board.
30
- # @param active_side_id [Integer] The identifier of the player who must play.
31
- # @param pieces_in_hand_grouped_by_sides [Array] The list of pieces in hand
32
- # grouped by players.
33
- #
34
- # @example Chess's starting position
35
- # Position.new(
36
- # "♜", "♞", "♝", "♛", "♚", "♝", "♞", "♜",
37
- # "♟", "♟", "♟", "♟", "♟", "♟", "♟", "♟",
38
- # nil, nil, nil, nil, nil, nil, nil, nil,
39
- # nil, nil, nil, nil, nil, nil, nil, nil,
40
- # nil, nil, nil, nil, nil, nil, nil, nil,
41
- # nil, nil, nil, nil, nil, nil, nil, nil,
42
- # "♙", "♙", "♙", "♙", "♙", "♙", "♙", "♙",
43
- # "♖", "♘", "♗", "♕", "♔", "♗", "♘", "♖"
44
- # )
45
- #
46
- # @example Four-player chess's starting position
47
- # Position.new(
48
- # nil , nil , nil , "yR", "yN", "yB", "yK", "yQ", "yB", "yN", "yR", nil , nil , nil ,
49
- # nil , nil , nil , "yP", "yP", "yP", "yP", "yP", "yP", "yP", "yP", nil , nil , nil ,
50
- # nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil ,
51
- # "bR", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gR",
52
- # "bN", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gN",
53
- # "bB", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gB",
54
- # "bK", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gQ",
55
- # "bQ", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gK",
56
- # "bB", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gB",
57
- # "bN", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gN",
58
- # "bR", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gR",
59
- # nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil ,
60
- # nil , nil , nil , "rP", "rP", "rP", "rP", "rP", "rP", "rP", "rP", nil , nil , nil ,
61
- # nil , nil , nil , "rR", "rN", "rB", "rQ", "rK", "rB", "rN", "rR", nil , nil , nil ,
62
- # pieces_in_hand_grouped_by_sides: [
63
- # [],
64
- # [],
65
- # [],
66
- # []
67
- # ]
68
- # )
69
- #
70
- # @example Makruk's starting position
71
- # Position.new(
72
- # "♜", "♞", "♝", "♛", "♚", "♝", "♞", "♜",
73
- # nil, nil, nil, nil, nil, nil, nil, nil,
74
- # "♟", "♟", "♟", "♟", "♟", "♟", "♟", "♟",
75
- # nil, nil, nil, nil, nil, nil, nil, nil,
76
- # nil, nil, nil, nil, nil, nil, nil, nil,
77
- # "♙", "♙", "♙", "♙", "♙", "♙", "♙", "♙",
78
- # nil, nil, nil, nil, nil, nil, nil, nil,
79
- # "♖", "♘", "♗", "♔", "♕", "♗", "♘", "♖"
80
- # )
81
- #
82
- # @example Shogi's starting position
83
- # Position.new(
84
- # "l", "n", "s", "g", "k", "g", "s", "n", "l",
85
- # nil, "r", nil, nil, nil, nil, nil, "b", nil,
86
- # "p", "p", "p", "p", "p", "p", "p", "p", "p",
87
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
88
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
89
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
90
- # "P", "P", "P", "P", "P", "P", "P", "P", "P",
91
- # nil, "B", nil, nil, nil, nil, nil, "R", nil,
92
- # "L", "N", "S", "G", "K", "G", "S", "N", "L"
93
- # )
94
- #
95
- # @example A classic Tsume Shogi problem
96
- # Position.new(
97
- # nil, nil, nil, "s", "k", "s", nil, nil, nil,
98
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
99
- # nil, nil, nil, nil, "+P", nil, nil, nil, nil,
100
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
101
- # nil, nil, nil, nil, nil, nil, nil, "+B", nil,
102
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
103
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
104
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
105
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
106
- # pieces_in_hand_grouped_by_sides: [
107
- # ["S"],
108
- # ["b", "g", "g", "g", "g", "n", "n", "n", "n", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "r", "r", "s"]
109
- # ]
110
- # )
111
- #
112
- # @example Xiangqi's starting position
113
- # Position.new(
114
- # "車", "馬", "象", "士", "將", "士", "象", "馬", "車",
115
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
116
- # nil, "砲", nil, nil, nil, nil, nil, "砲", nil,
117
- # "卒", nil, "卒", nil, "卒", nil, "卒", nil, "卒",
118
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
119
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
120
- # "兵", nil, "兵", nil, "兵", nil, "兵", nil, "兵",
121
- # nil, "炮", nil, nil, nil, nil, nil, "炮", nil,
122
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
123
- # "俥", "傌", "相", "仕", "帥", "仕", "相", "傌", "俥"
124
- # )
125
- def initialize(*squares, active_side_id: 0, pieces_in_hand_grouped_by_sides: [[], []])
126
- @squares = squares
127
- @active_side_id = active_side_id % pieces_in_hand_grouped_by_sides.size
128
- @pieces_in_hand_grouped_by_sides = pieces_in_hand_grouped_by_sides
129
-
130
- freeze
131
- end
132
-
133
- # Apply a move to the position.
134
- #
135
- # @param move [Array] The move to play.
136
- # @see https://developer.sashite.com/specs/portable-move-notation
137
- # @see https://github.com/sashite/pmn.rb
138
- #
139
- # @return [Position] The new position.
140
- def call(move)
141
- updated_squares = squares.dup
142
- updated_in_hand_pieces = in_hand_pieces.dup
143
-
144
- actions = move.each_slice(4)
145
-
146
- actions.each do |action|
147
- src_square_id = action.fetch(0)
148
- dst_square_id = action.fetch(1)
149
- moved_piece_name = action.fetch(2)
150
- captured_piece_name = action.fetch(3, nil)
151
-
152
- if src_square_id.nil?
153
- piece_in_hand_id = updated_in_hand_pieces.index(moved_piece_name)
154
- updated_in_hand_pieces.delete_at(piece_in_hand_id) unless piece_in_hand_id.nil?
155
- else
156
- updated_squares[src_square_id] = nil
157
- end
158
-
159
- updated_squares[dst_square_id] = moved_piece_name
160
-
161
- unless captured_piece_name.nil?
162
- updated_in_hand_pieces.push(captured_piece_name)
163
- end
164
- end
165
-
166
- updated_pieces_in_hand_grouped_by_sides = pieces_in_hand_grouped_by_sides.dup
167
- updated_pieces_in_hand_grouped_by_sides[active_side_id] = updated_in_hand_pieces
168
-
169
- self.class.new(*updated_squares,
170
- active_side_id: active_side_id.succ,
171
- pieces_in_hand_grouped_by_sides: updated_pieces_in_hand_grouped_by_sides
172
- )
173
- end
174
-
175
- # The list of pieces in hand owned by the current player.
176
- #
177
- # @return [Array] The list of pieces in hand of the active side.
178
- def in_hand_pieces
179
- pieces_in_hand_grouped_by_sides.fetch(active_side_id)
180
- end
181
- end
182
- end