qi 6.3.1 → 9.0.0.beta2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e72084022a3eaca3cec4ff33c2106c99e2b5da61412049b37180f0ddddd87793
4
- data.tar.gz: 56082cc561866965d8f37178f8e6710d54593e4d67f6a24bd63ec23e5c183094
3
+ metadata.gz: f66c9affa38d8e5dadfc596d5df81b47839187830d834600d624558d3643077e
4
+ data.tar.gz: fd345a4baeacbf22a0a898731dc26b19063727190a497a6d4cf779ca6275bf96
5
5
  SHA512:
6
- metadata.gz: f12a76bed9d203b56fcadf2133ec8e985494827d8e5533a7c4bcbe2c350de43d9ff8254c5b9f66c862ddb137f74fb4522b0f88ccd1d29fbec7d972a3acea5984
7
- data.tar.gz: 057b02a60a91a292bce36d173128e8a22117211d2afc32d5155ff81e289ebe7fb501b31a1c03c148c335c4ed61c3819f7b47fb8a51b0804f6098f3926b7c6a6c
6
+ metadata.gz: f5ef7c30f20b03fb3a6712c8b14824b1878a0f706159973b9d5c0c6270092042e12a4ecc50254e99f8d6107466367f58d530d77489370b3a54437e8f31bf42bb
7
+ data.tar.gz: 9364870e925d1769e5c9d233bcc8acb626a0a9d70d62725eafda311a926bd6ed398cb5e0efbff71a09ca659bcfc5e67037126ee4c4cffaa7b79e2687dcc1f470
data/LICENSE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2015-2020 Sashite
3
+ Copyright (c) 2015-2021 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,341 +1,50 @@
1
1
  # Qi.rb
2
2
 
3
- [![Build Status](https://travis-ci.org/sashite/qi.rb.svg?branch=master)](https://travis-ci.org/sashite/qi.rb)
4
- [![Gem Version](https://badge.fury.io/rb/qi.svg)][gem]
5
- [![Inline docs](https://inch-ci.org/github/sashite/qi.rb.svg?branch=master)][inchpages]
6
- [![Documentation](https://img.shields.io/:yard-docs-38c800.svg)][rubydoc]
3
+ [![Version](https://img.shields.io/github/v/tag/sashite/qi.rb?label=Version&logo=github)](https://github.com/sashite/qi.rb/releases)
4
+ [![Yard documentation](https://img.shields.io/badge/Yard-documentation-blue.svg?logo=github)](https://rubydoc.info/github/sashite/qi.rb/main)
5
+ [![CI](https://github.com/sashite/qi.rb/workflows/CI/badge.svg?branch=main)](https://github.com/sashite/qi.rb/actions?query=workflow%3Aci+branch%3Amain)
6
+ [![RuboCop](https://github.com/sashite/qi.rb/workflows/RuboCop/badge.svg?branch=main)](https://github.com/sashite/qi.rb/actions?query=workflow%3Arubocop+branch%3Amain)
7
+ [![License](https://img.shields.io/github/license/sashite/qi.rb?label=License&logo=github)](https://github.com/sashite/qi.rb/raw/main/LICENSE.md)
7
8
 
8
- > `Qi` (棋) is an abstraction for initializing and updating positions of chess variants (including Chess, Janggi, Markruk, Shogi, Xiangqi).
9
+ > `Qi` (棋) is an abstraction for updating positions of chess variants (including Chess, Janggi, Markruk, Shogi, Xiangqi), with a move.
9
10
 
10
11
  ## Installation
11
12
 
12
13
  Add this line to your application's Gemfile:
13
14
 
14
15
  ```ruby
15
- gem "qi"
16
+ gem "qi", ">= 9.0.0.beta2"
16
17
  ```
17
18
 
18
19
  And then execute:
19
20
 
20
- $ bundle
21
-
22
- Or install it yourself as:
23
-
24
- $ gem install qi
25
-
26
- ## Examples
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
21
+ ```sh
22
+ bundle
99
23
  ```
100
24
 
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)
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
- # => [[], []]
25
+ Or install it yourself as:
244
26
 
245
- last_position.active_side_id
246
- # => 1
27
+ ```sh
28
+ gem install qi --pre
247
29
  ```
248
30
 
249
- Let's do some moves on a [Four-player chess](https://en.wikipedia.org/wiki/Four-player_chess) board:
31
+ ## Examples
250
32
 
251
33
  ```ruby
252
34
  require "qi"
253
35
 
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
- ]
36
+ Qi.call(
37
+ [43, 13, "+B"],
38
+ in_hand: %w[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],
39
+ square: {
40
+ 3 => "s",
41
+ 4 => "k",
42
+ 5 => "s",
43
+ 22 => "+P",
44
+ 43 => "+B"
45
+ }
275
46
  )
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
47
+ # => {:square=>{3=>"s", 4=>"k", 5=>"s", 22=>"+P", 13=>"+B"}, :in_hand=>["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
48
  ```
340
49
 
341
50
  ## License
@@ -347,7 +56,3 @@ The code is available as open source under the terms of the [MIT License](https:
347
56
  This [gem](https://rubygems.org/gems/qi) is maintained by [Sashite](https://sashite.com/).
348
57
 
349
58
  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!
350
-
351
- [gem]: https://rubygems.org/gems/qi
352
- [inchpages]: https://inch-ci.org/github/sashite/qi.rb
353
- [rubydoc]: https://rubydoc.info/gems/qi/frames
data/lib/qi.rb CHANGED
@@ -1,7 +1,48 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # The Qi module.
3
+ require_relative File.join("qi", "move")
4
+
5
+ # The Qi abstraction.
6
+ #
7
+ # @example Apply a move to a classic Shogi problem
8
+ # Qi.call(
9
+ # [43, 13, "+B"],
10
+ # in_hand: %w[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],
11
+ # square: {
12
+ # 3 => "s",
13
+ # 4 => "k",
14
+ # 5 => "s",
15
+ # 22 => "+P",
16
+ # 43 => "+B"
17
+ # }
18
+ # )
19
+ # # => {:in_hand=>["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"], :square=>{3=>"s", 4=>"k", 5=>"s", 22=>"+P", 13=>"+B"}}
4
20
  module Qi
21
+ # Apply a move to the position.
22
+ #
23
+ # @param move [Array] The move to play.
24
+ # @param in_hand [Array] The list of pieces in hand.
25
+ # @param square [Hash] The index of each piece on the board.
26
+ #
27
+ # @see https://developer.sashite.com/specs/portable-chess-notation
28
+ # @see https://developer.sashite.com/specs/portable-move-notation
29
+ #
30
+ # @example Apply a move to a classic Shogi problem
31
+ # call(
32
+ # [43, 13, "+B"],
33
+ # in_hand: %w[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],
34
+ # square: {
35
+ # 3 => "s",
36
+ # 4 => "k",
37
+ # 5 => "s",
38
+ # 22 => "+P",
39
+ # 43 => "+B"
40
+ # }
41
+ # )
42
+ # # => {:in_hand=>["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"], :square=>{3=>"s", 4=>"k", 5=>"s", 22=>"+P", 13=>"+B"}}
43
+ #
44
+ # @return [Hash] The piece set of the next position.
45
+ def self.call(move, in_hand:, square:)
46
+ Move.new(move).call(in_hand: in_hand, square: square)
47
+ end
5
48
  end
6
-
7
- require_relative "qi/position"
data/lib/qi/action.rb ADDED
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Qi
4
+ # The Action abstraction.
5
+ class Action
6
+ # Initialize an action instance.
7
+ #
8
+ # @param src_square_id [Integer, nil] The source square ID.
9
+ # @param dst_square_id [Integer] The target square ID.
10
+ # @param moved_piece_name [String] The moved piece name.
11
+ # @param captured_piece_name [String, nil] The captured piece name.
12
+ #
13
+ # @example Initialize a promoted bishop action from 43 to 13
14
+ # new(43, 13, "+B", nil)
15
+ #
16
+ # @see https://developer.sashite.com/specs/portable-action-notation
17
+ def initialize(src_square_id, dst_square_id, moved_piece_name, captured_piece_name = nil)
18
+ @src_square_id = src_square_id
19
+ @dst_square_id = dst_square_id
20
+ @moved_piece_name = moved_piece_name
21
+ @captured_piece_name = captured_piece_name
22
+ end
23
+
24
+ # Commit an action to the position.
25
+ #
26
+ # @param in_hand [Array] The list of pieces in hand.
27
+ # @param square [Hash] The index of each piece on the board.
28
+ #
29
+ # @example Commit a Shogi action to the piece set of a position
30
+ # call(
31
+ # 43, 13, "+B", nil,
32
+ # in_hand: %w[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],
33
+ # square: {
34
+ # 3 => "s",
35
+ # 4 => "k",
36
+ # 5 => "s",
37
+ # 22 => "+P",
38
+ # 43 => "+B"
39
+ # }
40
+ # )
41
+ # # => {:in_hand=>["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"], :square=>{3=>"s", 4=>"k", 5=>"s", 22=>"+P", 13=>"+B"}}
42
+ #
43
+ # @return [Hash] The next piece set.
44
+ def call(in_hand:, square:)
45
+ in_hand = in_hand.dup
46
+ square = square.dup
47
+
48
+ if @src_square_id.nil?
49
+ piece_in_hand_id = in_hand.index(@moved_piece_name)
50
+ in_hand.delete_at(piece_in_hand_id) unless piece_in_hand_id.nil?
51
+ else
52
+ square.delete(@src_square_id)
53
+ end
54
+
55
+ square[@dst_square_id] = @moved_piece_name
56
+ in_hand.push(@captured_piece_name) unless @captured_piece_name.nil?
57
+
58
+ { in_hand: in_hand, square: square }
59
+ end
60
+ end
61
+ end
data/lib/qi/move.rb ADDED
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "action"
4
+
5
+ module Qi
6
+ # The Move abstraction.
7
+ class Move
8
+ # Initialize a move instance.
9
+ #
10
+ # @param move [Array] The move to apply.
11
+ #
12
+ # @example Initialize a promoted bishop move from 43 to 13
13
+ # new([43, 13, "+B"])
14
+ #
15
+ # @see https://developer.sashite.com/specs/portable-move-notation
16
+ def initialize(move)
17
+ @actions = move.each_slice(4)
18
+ end
19
+
20
+ # Apply a move to the piece set of a position.
21
+ #
22
+ # @param in_hand [Array] The list of pieces in hand.
23
+ # @param square [Hash] The index of each piece on the board.
24
+ #
25
+ # @example Apply a move to a classic Shogi problem
26
+ # call(
27
+ # [43, 13, "+B"],
28
+ # in_hand: %w[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],
29
+ # square: {
30
+ # 3 => "s",
31
+ # 4 => "k",
32
+ # 5 => "s",
33
+ # 22 => "+P",
34
+ # 43 => "+B"
35
+ # }
36
+ # )
37
+ # # => {:in_hand=>["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"], :square=>{3=>"s", 4=>"k", 5=>"s", 22=>"+P", 13=>"+B"}}
38
+ #
39
+ # @return [Hash] The piece set of the next position.
40
+ def call(in_hand:, square:)
41
+ @actions.inject(in_hand: in_hand, square: square) do |piece_set, attrs|
42
+ Action.new(*attrs).call(**piece_set)
43
+ end
44
+ end
45
+ end
46
+ 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: 9.0.0.beta2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyril Kato
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-26 00:00:00.000000000 Z
11
+ date: 2021-08-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: brutal
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: byebug
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rake
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +66,20 @@ dependencies:
52
66
  - - ">="
53
67
  - !ruby/object:Gem::Version
54
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop-md
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: rubocop-performance
57
85
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +94,20 @@ dependencies:
66
94
  - - ">="
67
95
  - !ruby/object:Gem::Version
68
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop-rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
69
111
  - !ruby/object:Gem::Dependency
70
112
  name: rubocop-thread_safety
71
113
  requirement: !ruby/object:Gem::Requirement
@@ -117,7 +159,8 @@ files:
117
159
  - LICENSE.md
118
160
  - README.md
119
161
  - lib/qi.rb
120
- - lib/qi/position.rb
162
+ - lib/qi/action.rb
163
+ - lib/qi/move.rb
121
164
  homepage: https://developer.sashite.com/specs/
122
165
  licenses:
123
166
  - MIT
@@ -125,7 +168,7 @@ metadata:
125
168
  bug_tracker_uri: https://github.com/sashite/qi.rb/issues
126
169
  documentation_uri: https://rubydoc.info/gems/qi/index
127
170
  source_code_uri: https://github.com/sashite/qi.rb
128
- post_install_message:
171
+ post_install_message:
129
172
  rdoc_options: []
130
173
  require_paths:
131
174
  - lib
@@ -133,15 +176,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
133
176
  requirements:
134
177
  - - ">="
135
178
  - !ruby/object:Gem::Version
136
- version: '0'
179
+ version: 3.0.0
137
180
  required_rubygems_version: !ruby/object:Gem::Requirement
138
181
  requirements:
139
- - - ">="
182
+ - - ">"
140
183
  - !ruby/object:Gem::Version
141
- version: '0'
184
+ version: 1.3.1
142
185
  requirements: []
143
- rubygems_version: 3.1.2
144
- signing_key:
186
+ rubygems_version: 3.2.22
187
+ signing_key:
145
188
  specification_version: 4
146
- summary: Represent positions and play moves.
189
+ summary: Update positions with a move.
147
190
  test_files: []
data/lib/qi/position.rb DELETED
@@ -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