qi 6.3.1 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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