qi 6.3.1 → 9.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
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