qi 6.0.0 → 6.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.md +1 -1
  3. data/README.md +302 -37
  4. data/lib/qi.rb +1 -1
  5. data/lib/qi/position.rb +131 -38
  6. metadata +2 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ea547ce94942f087213cf6a85465a231347e2cbd1805b5be7ebbe108d5cdd304
4
- data.tar.gz: '00529deb18f2658355d4bf8dd6955e3bf4469047164b872dea91ff3ea61f1841'
3
+ metadata.gz: e72084022a3eaca3cec4ff33c2106c99e2b5da61412049b37180f0ddddd87793
4
+ data.tar.gz: 56082cc561866965d8f37178f8e6710d54593e4d67f6a24bd63ec23e5c183094
5
5
  SHA512:
6
- metadata.gz: d46dd80b15e18ba16a3c4ea6c9d8a479e9f2f044e5b776c34c5152e80903c9e4aa806ee603c448334fdc10fcea995a710b33b30488ebf199a1c56e559065e95b
7
- data.tar.gz: f905c0750db19eda558dae0e8bcab054f180f4f21997f6896426330d64f10404c8cc63b08557a4e9c0d5fd264861fe3e933a6b23be3d418851f31abe1787481d
6
+ metadata.gz: f12a76bed9d203b56fcadf2133ec8e985494827d8e5533a7c4bcbe2c350de43d9ff8254c5b9f66c862ddb137f74fb4522b0f88ccd1d29fbec7d972a3acea5984
7
+ data.tar.gz: 057b02a60a91a292bce36d173128e8a22117211d2afc32d5155ff81e289ebe7fb501b31a1c03c148c335c4ed61c3819f7b47fb8a51b0804f6098f3926b7c6a6c
data/LICENSE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2015-2020 Cyril Kato
3
+ Copyright (c) 2015-2020 Sashite
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,18 +1,18 @@
1
- # Qi
1
+ # Qi.rb
2
2
 
3
- [![Build Status](https://api.travis-ci.org/sashite/qi.rb.svg?branch=master)][travis]
3
+ [![Build Status](https://travis-ci.org/sashite/qi.rb.svg?branch=master)](https://travis-ci.org/sashite/qi.rb)
4
4
  [![Gem Version](https://badge.fury.io/rb/qi.svg)][gem]
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
- > Instantiate [Portable Chess Notation](https://developer.sashite.com/specs/portable-chess-notation)'s positions and apply [Portable Move Notation](https://developer.sashite.com/specs/portable-move-notation)'s moves.
8
+ > `Qi` () is an abstraction for initializing and updating positions of chess variants (including Chess, Janggi, Markruk, Shogi, Xiangqi).
9
9
 
10
10
  ## Installation
11
11
 
12
12
  Add this line to your application's Gemfile:
13
13
 
14
14
  ```ruby
15
- gem 'qi'
15
+ gem "qi"
16
16
  ```
17
17
 
18
18
  And then execute:
@@ -23,66 +23,331 @@ Or install it yourself as:
23
23
 
24
24
  $ gem install qi
25
25
 
26
- ## Example
26
+ ## Examples
27
27
 
28
- Let's replay [The Shortest Possible Game of Shogi](https://userpages.monmouth.com/~colonel/shortshogi.html):
28
+ Let's replay [The Shortest Possible Game](https://userpages.monmouth.com/~colonel/shortshogi.html) of [Shogi](https://en.wikipedia.org/wiki/Shogi):
29
29
 
30
30
  ```ruby
31
- require 'qi'
31
+ require "qi"
32
32
 
33
- shogi_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',
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
37
  nil, nil, nil, nil, nil, nil, nil, nil, nil,
38
38
  nil, nil, nil, nil, nil, nil, nil, nil, nil,
39
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'
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
43
  )
44
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
+ ```ruby
104
+ require "qi"
105
+
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"]
119
+ ]
120
+ )
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)
45
143
  moves = [
46
- [ 56, 47, 'P' ],
47
- [ 3, 11, 'g' ],
48
- [ 64, 24, '+B', 'P' ],
49
- [ 5, 14, 'g' ],
50
- [ 24, 14, '+B', 'G' ],
51
- [ 4, 3, 'k' ],
52
- [ nil, 13, 'G' ]
144
+ [ 43, 13, "+B" ], [ 5, 13, "s", "b" ],
145
+ [ nil, 14, "S" ]
53
146
  ]
54
147
 
55
- last_position = moves.reduce(shogi_starting_position) do |position, move|
148
+ last_position = moves.reduce(starting_position) do |position, move|
56
149
  position.call(move)
57
150
  end
58
151
 
59
- last_position.topside_in_hand_pieces # => []
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]
60
333
 
61
- last_position.squares # => ["l", "n", "s", "k", nil, nil, "s", "n", "l",
62
- # nil, "r", "g", nil, "G", "+B", nil, "b", nil,
63
- # "p", "p", "p", "p", "p", "p", nil, "p", "p",
64
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
65
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
66
- # nil, nil, "P", nil, nil, nil, nil, nil, nil,
67
- # "P", "P", nil, "P", "P", "P", "P", "P", "P",
68
- # nil, nil, nil, nil, nil, nil, nil, "R", nil,
69
- # "L", "N", "S", "G", "K", "G", "S", "N", "L"]
334
+ last_position.pieces_in_hand_grouped_by_sides
335
+ # => [[], [], [], []]
70
336
 
71
- last_position.bottomside_in_hand_pieces # => ["P"]
72
- last_position.turn_to_topside? # => true
337
+ last_position.active_side_id
338
+ # => 1
73
339
  ```
74
340
 
75
341
  ## License
76
342
 
77
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
343
+ The code is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
78
344
 
79
345
  ## About Sashite
80
346
 
81
- The `qi` gem is maintained by [Sashite](https://sashite.com/).
347
+ This [gem](https://rubygems.org/gems/qi) is maintained by [Sashite](https://sashite.com/).
82
348
 
83
349
  With some [lines of code](https://github.com/sashite/), let's share the beauty of Chinese, Japanese and Western cultures through the game of chess!
84
350
 
85
351
  [gem]: https://rubygems.org/gems/qi
86
- [travis]: https://travis-ci.org/sashite/qi.rb
87
352
  [inchpages]: https://inch-ci.org/github/sashite/qi.rb
88
353
  [rubydoc]: https://rubydoc.info/gems/qi/frames
data/lib/qi.rb CHANGED
@@ -4,4 +4,4 @@
4
4
  module Qi
5
5
  end
6
6
 
7
- require_relative 'qi/position'
7
+ require_relative "qi/position"
@@ -5,43 +5,141 @@ module Qi
5
5
  #
6
6
  # @see https://developer.sashite.com/specs/portable-chess-notation
7
7
  class Position
8
- attr_reader :squares, :bottomside_in_hand_pieces, :topside_in_hand_pieces
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
9
26
 
10
27
  # Initialize a position.
11
28
  #
12
- # @param is_turn_to_topside [Boolean] The player who must play.
13
- # @param bottomside_in_hand_pieces [Array] The list of bottom-side's pieces in hand.
14
- # @param topside_in_hand_pieces [Array] The list of top-side's pieces in hand.
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
+ # )
15
111
  #
16
- # @example The Shogi's starting position
112
+ # @example Xiangqi's starting position
17
113
  # Position.new(
18
- # 'l', 'n', 's', 'g', 'k', 'g', 's', 'n', 'l',
19
- # nil, 'r', nil, nil, nil, nil, nil, 'b', nil,
20
- # 'p', 'p', 'p', 'p', 'p', 'p', 'p', 'p', 'p',
114
+ # "車", "馬", "象", "士", "將", "士", "象", "馬", "車",
115
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
116
+ # nil, "砲", nil, nil, nil, nil, nil, "砲", nil,
117
+ # "卒", nil, "卒", nil, "卒", nil, "卒", nil, "卒",
21
118
  # nil, nil, nil, nil, nil, nil, nil, nil, nil,
22
119
  # nil, nil, nil, nil, nil, nil, nil, nil, nil,
120
+ # "兵", nil, "兵", nil, "兵", nil, "兵", nil, "兵",
121
+ # nil, "炮", nil, nil, nil, nil, nil, "炮", nil,
23
122
  # nil, nil, nil, nil, nil, nil, nil, nil, nil,
24
- # 'P', 'P', 'P', 'P', 'P', 'P', 'P', 'P', 'P',
25
- # nil, 'B', nil, nil, nil, nil, nil, 'R', nil,
26
- # 'L', 'N', 'S', 'G', 'K', 'G', 'S', 'N', 'L'
123
+ # "俥", "傌", "相", "仕", "帥", "仕", "相", "傌", "俥"
27
124
  # )
28
- def initialize(*squares, is_turn_to_topside: false, bottomside_in_hand_pieces: [], topside_in_hand_pieces: [])
29
- @squares = squares
30
- @is_turn_to_topside = is_turn_to_topside
31
- @bottomside_in_hand_pieces = bottomside_in_hand_pieces
32
- @topside_in_hand_pieces = topside_in_hand_pieces
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
33
129
 
34
130
  freeze
35
131
  end
36
132
 
37
- # Apply a move in PMN (Portable Move Notation) format.
133
+ # Apply a move to the position.
38
134
  #
135
+ # @param move [Array] The move to play.
39
136
  # @see https://developer.sashite.com/specs/portable-move-notation
137
+ # @see https://github.com/sashite/pmn.rb
138
+ #
40
139
  # @return [Position] The new position.
41
140
  def call(move)
42
141
  updated_squares = squares.dup
43
- updated_bottomside_in_hand_pieces = bottomside_in_hand_pieces.dup
44
- updated_topside_in_hand_pieces = topside_in_hand_pieces.dup
142
+ updated_in_hand_pieces = in_hand_pieces.dup
45
143
 
46
144
  actions = move.each_slice(4)
47
145
 
@@ -52,13 +150,8 @@ module Qi
52
150
  captured_piece_name = action.fetch(3, nil)
53
151
 
54
152
  if src_square_id.nil?
55
- if turn_to_topside?
56
- piece_in_hand_id = updated_topside_in_hand_pieces.index(moved_piece_name)
57
- updated_topside_in_hand_pieces.delete_at(piece_in_hand_id)
58
- else
59
- piece_in_hand_id = updated_bottomside_in_hand_pieces.index(moved_piece_name)
60
- updated_bottomside_in_hand_pieces.delete_at(piece_in_hand_id)
61
- end
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?
62
155
  else
63
156
  updated_squares[src_square_id] = nil
64
157
  end
@@ -66,24 +159,24 @@ module Qi
66
159
  updated_squares[dst_square_id] = moved_piece_name
67
160
 
68
161
  unless captured_piece_name.nil?
69
- if turn_to_topside?
70
- updated_topside_in_hand_pieces.push(captured_piece_name)
71
- else
72
- updated_bottomside_in_hand_pieces.push(captured_piece_name)
73
- end
162
+ updated_in_hand_pieces.push(captured_piece_name)
74
163
  end
75
164
  end
76
165
 
77
- self.class.new(*updated_squares, is_turn_to_topside: !turn_to_topside?,
78
- bottomside_in_hand_pieces: updated_bottomside_in_hand_pieces,
79
- topside_in_hand_pieces: updated_topside_in_hand_pieces)
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
+ )
80
173
  end
81
174
 
82
- # The player who must play.
175
+ # The list of pieces in hand owned by the current player.
83
176
  #
84
- # @return [Boolean] True if it is turn to topside, false otherwise.
85
- def turn_to_topside?
86
- @is_turn_to_topside
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)
87
180
  end
88
181
  end
89
182
  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.0.0
4
+ version: 6.3.1
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-07-09 00:00:00.000000000 Z
11
+ date: 2020-08-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: brutal
@@ -52,20 +52,6 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: rubocop
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - '='
60
- - !ruby/object:Gem::Version
61
- version: 0.86.0
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - '='
67
- - !ruby/object:Gem::Version
68
- version: 0.86.0
69
55
  - !ruby/object:Gem::Dependency
70
56
  name: rubocop-performance
71
57
  requirement: !ruby/object:Gem::Requirement