feen 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 75953a4a09b217cf1b947bf2c417413bdcb6548a52c7e75a3d6b24ce22c36f41
4
+ data.tar.gz: 23bb86786007ba293891244e87fb14a8eb8e881b9175d29d1fb2ad0e5086163f
5
+ SHA512:
6
+ metadata.gz: d13fdf2c1daa8348580397e66263ccc40eaf1ea01172a587f010b6aa709335ffe7c809fcda1bb93e2178717b7ecad7458d8eb2d86a6fbb2a607dd8a6d3762d05
7
+ data.tar.gz: 7f9abb25e79785377e53875de7e84f05956acee0004703c2c7110ae2ebc44c3d0ebf435d8c81ce83d6d785829288c77100d7d467a7f49bfb6b12d5f08681a22e
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Sashite
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,388 @@
1
+ # FEEN.rb
2
+
3
+ [![Build Status](https://travis-ci.org/sashite/feen.rb.svg?branch=master)](https://travis-ci.org/sashite/feen.rb)
4
+ [![Documentation](https://img.shields.io/:yard-docs-38c800.svg)](https://rubydoc.info/gems/feen/frames)
5
+
6
+ > __FEEN__ (Forsythโ€“Edwards Expanded Notation) support for the Ruby language.
7
+
8
+ ## Overview
9
+
10
+ This is an implementation of [__FEEN__](https://developer.sashite.com/specs/forsyth-edwards-expanded-notation), a generic format that can be used for serializing and deserializing chess positions.
11
+
12
+ The main chess variants are supported, including _Chess_, _Makruk_, _Shogi_, _Xiangqi_.
13
+
14
+ This tool could also be used for more exotic variants like: _Dai dai shogi_, _Four-player chess_, _Three-dimensional chess_ ๐Ÿ––
15
+
16
+ ![3D chess on Star Trek (from the episode "Court Martial")](https://github.com/sashite/feen.rb/raw/master/star-trek-chess.jpg)
17
+
18
+ ## Installation
19
+
20
+ 1. Add the dependency to your `Gemfile`:
21
+
22
+ ```ruby
23
+ gem "feen"
24
+ ```
25
+
26
+ 2. Run `bundle install`
27
+
28
+ ## Usage
29
+
30
+ ```ruby
31
+ require "feen"
32
+
33
+ # Emit a FEEN representing the Shogi's starting position
34
+ FEEN.dump(
35
+ active_side: 0,
36
+ indexes: [9, 9],
37
+ pieces_in_hand_by_players: [
38
+ [],
39
+ []
40
+ ],
41
+ squares: [
42
+ "l", "n", "s", "g", "k", "g", "s", "n", "l",
43
+ nil, "r", nil, nil, nil, nil, nil, "b", nil,
44
+ "p", "p", "p", "p", "p", "p", "p", "p", "p",
45
+ nil, nil, nil, nil, nil, nil, nil, nil, nil,
46
+ nil, nil, nil, nil, nil, nil, nil, nil, nil,
47
+ nil, nil, nil, nil, nil, nil, nil, nil, nil,
48
+ "P", "P", "P", "P", "P", "P", "P", "P", "P",
49
+ nil, "B", nil, nil, nil, nil, nil, "R", nil,
50
+ "L", "N", "S", "G", "K", "G", "S", "N", "L"
51
+ ]
52
+ )
53
+ # => "l,n,s,g,k,g,s,n,l/1,r,5,b,1/p,p,p,p,p,p,p,p,p/9/9/9/P,P,P,P,P,P,P,P,P/1,B,5,R,1/L,N,S,G,K,G,S,N,L 0 /"
54
+
55
+ # Parse the Shogi's starting position FEEN
56
+ FEEN.parse("l,n,s,g,k,g,s,n,l/1,r,5,b,1/p,p,p,p,p,p,p,p,p/9/9/9/P,P,P,P,P,P,P,P,P/1,B,5,R,1/L,N,S,G,K,G,S,N,L 0 /")
57
+ # => {
58
+ # active_side: 0,
59
+ # indexes: [9, 9],
60
+ # pieces_in_hand_by_players: [
61
+ # [],
62
+ # []
63
+ # ],
64
+ # squares: [
65
+ # "l", "n", "s", "g", "k", "g", "s", "n", "l",
66
+ # nil, "r", nil, nil, nil, nil, nil, "b", nil,
67
+ # "p", "p", "p", "p", "p", "p", "p", "p", "p",
68
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
69
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
70
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
71
+ # "P", "P", "P", "P", "P", "P", "P", "P", "P",
72
+ # nil, "B", nil, nil, nil, nil, nil, "R", nil,
73
+ # "L", "N", "S", "G", "K", "G", "S", "N", "L"
74
+ # ]
75
+ # }
76
+ ```
77
+
78
+ ### More examples
79
+
80
+ ```ruby
81
+ # Dump an empty 3x8x8 board position
82
+ FEEN.dump(
83
+ active_side: 0,
84
+ indexes: [3, 8, 8],
85
+ pieces_in_hand_by_players: [
86
+ [],
87
+ []
88
+ ],
89
+ squares: Array.new(3 * 8 * 8)
90
+ )
91
+ # => "8/8/8/8/8/8/8/8//8/8/8/8/8/8/8/8//8/8/8/8/8/8/8/8 0 /"
92
+
93
+ # Dump Four-player chess's starting position
94
+ FEEN.dump(
95
+ active_side: 0,
96
+ indexes: [14, 14],
97
+ pieces_in_hand_by_players: [
98
+ [],
99
+ [],
100
+ [],
101
+ []
102
+ ],
103
+ squares: [
104
+ nil , nil , nil , "yR", "yN", "yB", "yK", "yQ", "yB", "yN", "yR", nil , nil , nil ,
105
+ nil , nil , nil , "yP", "yP", "yP", "yP", "yP", "yP", "yP", "yP", nil , nil , nil ,
106
+ nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil ,
107
+ "bR", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gR",
108
+ "bN", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gN",
109
+ "bB", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gB",
110
+ "bK", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gQ",
111
+ "bQ", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gK",
112
+ "bB", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gB",
113
+ "bN", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gN",
114
+ "bR", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gR",
115
+ nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil ,
116
+ nil , nil , nil , "rP", "rP", "rP", "rP", "rP", "rP", "rP", "rP", nil , nil , nil ,
117
+ nil , nil , nil , "rR", "rN", "rB", "rQ", "rK", "rB", "rN", "rR", nil , nil , nil
118
+ ]
119
+ )
120
+ # => "3,yR,yN,yB,yK,yQ,yB,yN,yR,3/3,yP,yP,yP,yP,yP,yP,yP,yP,3/14/bR,bP,10,gP,gR/bN,bP,10,gP,gN/bB,bP,10,gP,gB/bK,bP,10,gP,gQ/bQ,bP,10,gP,gK/bB,bP,10,gP,gB/bN,bP,10,gP,gN/bR,bP,10,gP,gR/14/3,rP,rP,rP,rP,rP,rP,rP,rP,3/3,rR,rN,rB,rQ,rK,rB,rN,rR,3 0 ///"
121
+
122
+ # Dump Chess's starting position
123
+ FEEN.dump(
124
+ active_side: 0,
125
+ indexes: [8, 8],
126
+ pieces_in_hand_by_players: [
127
+ [],
128
+ []
129
+ ],
130
+ squares: [
131
+ "โ™œ", "โ™ž", "โ™", "โ™›", "โ™š", "โ™", "โ™ž", "โ™œ",
132
+ "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ",
133
+ nil, nil, nil, nil, nil, nil, nil, nil,
134
+ nil, nil, nil, nil, nil, nil, nil, nil,
135
+ nil, nil, nil, nil, nil, nil, nil, nil,
136
+ nil, nil, nil, nil, nil, nil, nil, nil,
137
+ "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™",
138
+ "โ™–", "โ™˜", "โ™—", "โ™•", "โ™”", "โ™—", "โ™˜", "โ™–"
139
+ ]
140
+ )
141
+ # => "โ™œ,โ™ž,โ™,โ™›,โ™š,โ™,โ™ž,โ™œ/โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ/8/8/8/8/โ™™,โ™™,โ™™,โ™™,โ™™,โ™™,โ™™,โ™™/โ™–,โ™˜,โ™—,โ™•,โ™”,โ™—,โ™˜,โ™– 0 /"
142
+
143
+ # Dump Chess's position after the move 1. e4
144
+ FEEN.dump(
145
+ active_side: 1,
146
+ indexes: [8, 8],
147
+ pieces_in_hand_by_players: [
148
+ [],
149
+ []
150
+ ],
151
+ squares: [
152
+ "โ™œ", "โ™ž", "โ™", "โ™›", "โ™š", "โ™", "โ™ž", "โ™œ",
153
+ "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ",
154
+ nil, nil, nil, nil, nil, nil, nil, nil,
155
+ nil, nil, nil, nil, nil, nil, nil, nil,
156
+ nil, nil, nil, nil, "โ™™", nil, nil, nil,
157
+ nil, nil, nil, nil, nil, nil, nil, nil,
158
+ "โ™™", "โ™™", "โ™™", "โ™™", nil, "โ™™", "โ™™", "โ™™",
159
+ "โ™–", "โ™˜", "โ™—", "โ™•", "โ™”", "โ™—", "โ™˜", "โ™–"
160
+ ]
161
+ )
162
+ # => "โ™œ,โ™ž,โ™,โ™›,โ™š,โ™,โ™ž,โ™œ/โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ/8/8/4,โ™™,3/8/โ™™,โ™™,โ™™,โ™™,1,โ™™,โ™™,โ™™/โ™–,โ™˜,โ™—,โ™•,โ™”,โ™—,โ™˜,โ™– 1 /"
163
+
164
+ # Dump Chess's position after the moves 1. e4 c5
165
+ FEEN.dump(
166
+ active_side: 0,
167
+ indexes: [8, 8],
168
+ pieces_in_hand_by_players: [
169
+ [],
170
+ []
171
+ ],
172
+ squares: [
173
+ "โ™œ", "โ™ž", "โ™", "โ™›", "โ™š", "โ™", "โ™ž", "โ™œ",
174
+ "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", nil, "โ™Ÿ", "โ™Ÿ", "โ™Ÿ",
175
+ nil, nil, nil, nil, nil, nil, nil, nil,
176
+ nil, nil, nil, nil, "โ™Ÿ", nil, nil, nil,
177
+ nil, nil, nil, nil, "โ™™", nil, nil, nil,
178
+ nil, nil, nil, nil, nil, nil, nil, nil,
179
+ "โ™™", "โ™™", "โ™™", "โ™™", nil, "โ™™", "โ™™", "โ™™",
180
+ "โ™–", "โ™˜", "โ™—", "โ™•", "โ™”", "โ™—", "โ™˜", "โ™–"
181
+ ]
182
+ )
183
+ # => "โ™œ,โ™ž,โ™,โ™›,โ™š,โ™,โ™ž,โ™œ/โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,1,โ™Ÿ,โ™Ÿ,โ™Ÿ/8/4,โ™Ÿ,3/4,โ™™,3/8/โ™™,โ™™,โ™™,โ™™,1,โ™™,โ™™,โ™™/โ™–,โ™˜,โ™—,โ™•,โ™”,โ™—,โ™˜,โ™– 0 /"
184
+
185
+ # Dump Makruk's starting position
186
+ FEEN.dump(
187
+ active_side: 0,
188
+ indexes: [8, 8],
189
+ pieces_in_hand_by_players: [
190
+ [],
191
+ []
192
+ ],
193
+ squares: [
194
+ "โ™œ", "โ™ž", "โ™", "โ™›", "โ™š", "โ™", "โ™ž", "โ™œ",
195
+ nil, nil, nil, nil, nil, nil, nil, nil,
196
+ "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ",
197
+ nil, nil, nil, nil, nil, nil, nil, nil,
198
+ nil, nil, nil, nil, nil, nil, nil, nil,
199
+ "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™",
200
+ nil, nil, nil, nil, nil, nil, nil, nil,
201
+ "โ™–", "โ™˜", "โ™—", "โ™”", "โ™•", "โ™—", "โ™˜", "โ™–"
202
+ ]
203
+ )
204
+ # => "โ™œ,โ™ž,โ™,โ™›,โ™š,โ™,โ™ž,โ™œ/8/โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ/8/8/โ™™,โ™™,โ™™,โ™™,โ™™,โ™™,โ™™,โ™™/8/โ™–,โ™˜,โ™—,โ™”,โ™•,โ™—,โ™˜,โ™– 0 /"
205
+
206
+ # Dump a classic Tsume Shogi problem
207
+ FEEN.dump(
208
+ active_side: 0,
209
+ indexes: [9, 9],
210
+ pieces_in_hand_by_players: [
211
+ %w[S],
212
+ %w[r r b g g g g s n n n n p p p p p p p p p p p p p p p p p]
213
+ ],
214
+ squares: [
215
+ nil, nil, nil, "s", "k", "s", nil, nil, nil,
216
+ nil, nil, nil, nil, nil, nil, nil, nil, nil,
217
+ nil, nil, nil, nil, "+P", nil, nil, nil, nil,
218
+ nil, nil, nil, nil, nil, nil, nil, nil, nil,
219
+ nil, nil, nil, nil, nil, nil, nil, "+B", nil,
220
+ nil, nil, nil, nil, nil, nil, nil, nil, nil,
221
+ nil, nil, nil, nil, nil, nil, nil, nil, nil,
222
+ nil, nil, nil, nil, nil, nil, nil, nil, nil,
223
+ nil, nil, nil, nil, nil, nil, nil, nil, nil
224
+ ]
225
+ )
226
+ # => "3,s,k,s,3/9/4,+P,4/9/7,+B,1/9/9/9/9 0 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"
227
+
228
+ # Dump Xiangqi's starting position
229
+ FEEN.dump(
230
+ active_side: 0,
231
+ indexes: [10, 9],
232
+ pieces_in_hand_by_players: [
233
+ [],
234
+ []
235
+ ],
236
+ squares: [
237
+ "่ปŠ", "้ฆฌ", "่ฑก", "ๅฃซ", "ๅฐ‡", "ๅฃซ", "่ฑก", "้ฆฌ", "่ปŠ",
238
+ nil, nil, nil, nil, nil, nil, nil, nil, nil,
239
+ nil, "็ ฒ", nil, nil, nil, nil, nil, "็ ฒ", nil,
240
+ "ๅ’", nil, "ๅ’", nil, "ๅ’", nil, "ๅ’", nil, "ๅ’",
241
+ nil, nil, nil, nil, nil, nil, nil, nil, nil,
242
+ nil, nil, nil, nil, nil, nil, nil, nil, nil,
243
+ "ๅ…ต", nil, "ๅ…ต", nil, "ๅ…ต", nil, "ๅ…ต", nil, "ๅ…ต",
244
+ nil, "็‚ฎ", nil, nil, nil, nil, nil, "็‚ฎ", nil,
245
+ nil, nil, nil, nil, nil, nil, nil, nil, nil,
246
+ "ไฟฅ", "ๅ‚Œ", "็›ธ", "ไป•", "ๅธฅ", "ไป•", "็›ธ", "ๅ‚Œ", "ไฟฅ"
247
+ ]
248
+ )
249
+ # => "่ปŠ,้ฆฌ,่ฑก,ๅฃซ,ๅฐ‡,ๅฃซ,่ฑก,้ฆฌ,่ปŠ/9/1,็ ฒ,5,็ ฒ,1/ๅ’,1,ๅ’,1,ๅ’,1,ๅ’,1,ๅ’/9/9/ๅ…ต,1,ๅ…ต,1,ๅ…ต,1,ๅ…ต,1,ๅ…ต/1,็‚ฎ,5,็‚ฎ,1/9/ไฟฅ,ๅ‚Œ,็›ธ,ไป•,ๅธฅ,ไป•,็›ธ,ๅ‚Œ,ไฟฅ 0 /"
250
+
251
+ # Parse an empty 3x8x8 board position
252
+ FEEN.parse("8/8/8/8/8/8/8/8//8/8/8/8/8/8/8/8//8/8/8/8/8/8/8/8 0 /")
253
+ # => {
254
+ # active_side: 0,
255
+ # indexes: [3, 8, 8],
256
+ # pieces_in_hand_by_players: [
257
+ # [],
258
+ # []
259
+ # ],
260
+ # squares: Array.new(3 * 8 * 8)
261
+ # }
262
+
263
+ # Parse Four-player chess's starting position
264
+ FEEN.parse("3,yR,yN,yB,yK,yQ,yB,yN,yR,3/3,yP,yP,yP,yP,yP,yP,yP,yP,3/14/bR,bP,10,gP,gR/bN,bP,10,gP,gN/bB,bP,10,gP,gB/bK,bP,10,gP,gQ/bQ,bP,10,gP,gK/bB,bP,10,gP,gB/bN,bP,10,gP,gN/bR,bP,10,gP,gR/14/3,rP,rP,rP,rP,rP,rP,rP,rP,3/3,rR,rN,rB,rQ,rK,rB,rN,rR,3 0 ///")
265
+ # => {
266
+ # active_side: 0,
267
+ # indexes: [14, 14],
268
+ # pieces_in_hand_by_players: [
269
+ # [],
270
+ # [],
271
+ # [],
272
+ # []
273
+ # ],
274
+ # squares: [
275
+ # nil , nil , nil , "yR", "yN", "yB", "yK", "yQ", "yB", "yN", "yR", nil , nil , nil ,
276
+ # nil , nil , nil , "yP", "yP", "yP", "yP", "yP", "yP", "yP", "yP", nil , nil , nil ,
277
+ # nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil ,
278
+ # "bR", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gR",
279
+ # "bN", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gN",
280
+ # "bB", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gB",
281
+ # "bK", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gQ",
282
+ # "bQ", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gK",
283
+ # "bB", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gB",
284
+ # "bN", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gN",
285
+ # "bR", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gR",
286
+ # nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil ,
287
+ # nil , nil , nil , "rP", "rP", "rP", "rP", "rP", "rP", "rP", "rP", nil , nil , nil ,
288
+ # nil , nil , nil , "rR", "rN", "rB", "rQ", "rK", "rB", "rN", "rR", nil , nil , nil
289
+ # ]
290
+ # }
291
+
292
+ # Parse Chess's starting position
293
+ FEEN.parse("โ™œ,โ™ž,โ™,โ™›,โ™š,โ™,โ™ž,โ™œ/โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ/8/8/8/8/โ™™,โ™™,โ™™,โ™™,โ™™,โ™™,โ™™,โ™™/โ™–,โ™˜,โ™—,โ™•,โ™”,โ™—,โ™˜,โ™– 0 /")
294
+ # => {
295
+ # active_side: 0,
296
+ # indexes: [8, 8],
297
+ # pieces_in_hand_by_players: [
298
+ # [],
299
+ # []
300
+ # ],
301
+ # squares: [
302
+ # "โ™œ", "โ™ž", "โ™", "โ™›", "โ™š", "โ™", "โ™ž", "โ™œ",
303
+ # "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ",
304
+ # nil, nil, nil, nil, nil, nil, nil, nil,
305
+ # nil, nil, nil, nil, nil, nil, nil, nil,
306
+ # nil, nil, nil, nil, nil, nil, nil, nil,
307
+ # nil, nil, nil, nil, nil, nil, nil, nil,
308
+ # "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™",
309
+ # "โ™–", "โ™˜", "โ™—", "โ™•", "โ™”", "โ™—", "โ™˜", "โ™–"
310
+ # ]
311
+ # }
312
+
313
+ # Parse Makruk's starting position
314
+ FEEN.parse("โ™œ,โ™ž,โ™,โ™›,โ™š,โ™,โ™ž,โ™œ/8/โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ/8/8/โ™™,โ™™,โ™™,โ™™,โ™™,โ™™,โ™™,โ™™/8/โ™–,โ™˜,โ™—,โ™”,โ™•,โ™—,โ™˜,โ™– 0 /")
315
+ # => {
316
+ # active_side: 0,
317
+ # indexes: [8, 8],
318
+ # pieces_in_hand_by_players: [
319
+ # [],
320
+ # []
321
+ # ],
322
+ # squares: [
323
+ # "โ™œ", "โ™ž", "โ™", "โ™›", "โ™š", "โ™", "โ™ž", "โ™œ",
324
+ # nil, nil, nil, nil, nil, nil, nil, nil,
325
+ # "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ",
326
+ # nil, nil, nil, nil, nil, nil, nil, nil,
327
+ # nil, nil, nil, nil, nil, nil, nil, nil,
328
+ # "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™",
329
+ # nil, nil, nil, nil, nil, nil, nil, nil,
330
+ # "โ™–", "โ™˜", "โ™—", "โ™”", "โ™•", "โ™—", "โ™˜", "โ™–"
331
+ # ]
332
+ # }
333
+
334
+ # Parse a classic Tsume Shogi problem
335
+ FEEN.parse("3,s,k,s,3/9/4,+P,4/9/7,+B,1/9/9/9/9 0 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")
336
+ # => {
337
+ # active_side: 0,
338
+ # indexes: [9, 9],
339
+ # pieces_in_hand_by_players: [
340
+ # %w[S],
341
+ # %w[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]
342
+ # ],
343
+ # squares: [
344
+ # nil, nil, nil, "s", "k", "s", nil, nil, nil,
345
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
346
+ # nil, nil, nil, nil, "+P", nil, nil, nil, nil,
347
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
348
+ # nil, nil, nil, nil, nil, nil, nil, "+B", nil,
349
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
350
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
351
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
352
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil
353
+ # ]
354
+ # }
355
+
356
+ # Parse Xiangqi's starting position
357
+ FEEN.parse("่ปŠ,้ฆฌ,่ฑก,ๅฃซ,ๅฐ‡,ๅฃซ,่ฑก,้ฆฌ,่ปŠ/9/1,็ ฒ,5,็ ฒ,1/ๅ’,1,ๅ’,1,ๅ’,1,ๅ’,1,ๅ’/9/9/ๅ…ต,1,ๅ…ต,1,ๅ…ต,1,ๅ…ต,1,ๅ…ต/1,็‚ฎ,5,็‚ฎ,1/9/ไฟฅ,ๅ‚Œ,็›ธ,ไป•,ๅธฅ,ไป•,็›ธ,ๅ‚Œ,ไฟฅ 0 /")
358
+ # => {
359
+ # active_side: 0,
360
+ # indexes: [10, 9],
361
+ # pieces_in_hand_by_players: [
362
+ # [],
363
+ # []
364
+ # ],
365
+ # squares: [
366
+ # "่ปŠ", "้ฆฌ", "่ฑก", "ๅฃซ", "ๅฐ‡", "ๅฃซ", "่ฑก", "้ฆฌ", "่ปŠ",
367
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
368
+ # nil, "็ ฒ", nil, nil, nil, nil, nil, "็ ฒ", nil,
369
+ # "ๅ’", nil, "ๅ’", nil, "ๅ’", nil, "ๅ’", nil, "ๅ’",
370
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
371
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
372
+ # "ๅ…ต", nil, "ๅ…ต", nil, "ๅ…ต", nil, "ๅ…ต", nil, "ๅ…ต",
373
+ # nil, "็‚ฎ", nil, nil, nil, nil, nil, "็‚ฎ", nil,
374
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
375
+ # "ไฟฅ", "ๅ‚Œ", "็›ธ", "ไป•", "ๅธฅ", "ไป•", "็›ธ", "ๅ‚Œ", "ไฟฅ"
376
+ # ]
377
+ # }
378
+ ```
379
+
380
+ ## License
381
+
382
+ The code is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
383
+
384
+ ## About Sashite
385
+
386
+ This [gem](https://rubygems.org/gems/feen) is maintained by [Sashite](https://sashite.com/).
387
+
388
+ 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!
@@ -0,0 +1,720 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'feen/dumper'
4
+ require_relative 'feen/parser'
5
+
6
+ # This module provides a Ruby interface for data serialization and
7
+ # deserialization in FEEN format.
8
+ #
9
+ # @see https://developer.sashite.com/specs/forsyth-edwards-expanded-notation
10
+ #
11
+ # @example Dump an empty 3x8x8 board position
12
+ # FEEN.dump(
13
+ # active_side: 0,
14
+ # indexes: [3, 8, 8],
15
+ # pieces_in_hand_by_players: [
16
+ # [],
17
+ # []
18
+ # ],
19
+ # squares: Array.new(3 * 8 * 8)
20
+ # )
21
+ # # => "8/8/8/8/8/8/8/8//8/8/8/8/8/8/8/8//8/8/8/8/8/8/8/8 0 /"
22
+ #
23
+ # @example Dump Four-player chess's starting position
24
+ # FEEN.dump(
25
+ # active_side: 0,
26
+ # indexes: [14, 14],
27
+ # pieces_in_hand_by_players: [
28
+ # [],
29
+ # [],
30
+ # [],
31
+ # []
32
+ # ],
33
+ # squares: [
34
+ # nil , nil , nil , "yR", "yN", "yB", "yK", "yQ", "yB", "yN", "yR", nil , nil , nil ,
35
+ # nil , nil , nil , "yP", "yP", "yP", "yP", "yP", "yP", "yP", "yP", nil , nil , nil ,
36
+ # nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil ,
37
+ # "bR", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gR",
38
+ # "bN", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gN",
39
+ # "bB", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gB",
40
+ # "bK", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gQ",
41
+ # "bQ", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gK",
42
+ # "bB", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gB",
43
+ # "bN", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gN",
44
+ # "bR", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gR",
45
+ # nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil ,
46
+ # nil , nil , nil , "rP", "rP", "rP", "rP", "rP", "rP", "rP", "rP", nil , nil , nil ,
47
+ # nil , nil , nil , "rR", "rN", "rB", "rQ", "rK", "rB", "rN", "rR", nil , nil , nil
48
+ # ]
49
+ # )
50
+ # # => "3,yR,yN,yB,yK,yQ,yB,yN,yR,3/3,yP,yP,yP,yP,yP,yP,yP,yP,3/14/bR,bP,10,gP,gR/bN,bP,10,gP,gN/bB,bP,10,gP,gB/bK,bP,10,gP,gQ/bQ,bP,10,gP,gK/bB,bP,10,gP,gB/bN,bP,10,gP,gN/bR,bP,10,gP,gR/14/3,rP,rP,rP,rP,rP,rP,rP,rP,3/3,rR,rN,rB,rQ,rK,rB,rN,rR,3 0 ///"
51
+ #
52
+ # @example Dump Chess's starting position
53
+ # FEEN.dump(
54
+ # active_side: 0,
55
+ # indexes: [8, 8],
56
+ # pieces_in_hand_by_players: [
57
+ # [],
58
+ # []
59
+ # ],
60
+ # squares: [
61
+ # "โ™œ", "โ™ž", "โ™", "โ™›", "โ™š", "โ™", "โ™ž", "โ™œ",
62
+ # "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ",
63
+ # nil, nil, nil, nil, nil, nil, nil, nil,
64
+ # nil, nil, nil, nil, nil, nil, nil, nil,
65
+ # nil, nil, nil, nil, nil, nil, nil, nil,
66
+ # nil, nil, nil, nil, nil, nil, nil, nil,
67
+ # "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™",
68
+ # "โ™–", "โ™˜", "โ™—", "โ™•", "โ™”", "โ™—", "โ™˜", "โ™–"
69
+ # ]
70
+ # )
71
+ # # => "โ™œ,โ™ž,โ™,โ™›,โ™š,โ™,โ™ž,โ™œ/โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ/8/8/8/8/โ™™,โ™™,โ™™,โ™™,โ™™,โ™™,โ™™,โ™™/โ™–,โ™˜,โ™—,โ™•,โ™”,โ™—,โ™˜,โ™– 0 /"
72
+ #
73
+ # @example Dump Chess's position after the move 1. e4
74
+ # FEEN.dump(
75
+ # active_side: 1,
76
+ # indexes: [8, 8],
77
+ # pieces_in_hand_by_players: [
78
+ # [],
79
+ # []
80
+ # ],
81
+ # squares: [
82
+ # "โ™œ", "โ™ž", "โ™", "โ™›", "โ™š", "โ™", "โ™ž", "โ™œ",
83
+ # "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ",
84
+ # nil, nil, nil, nil, nil, nil, nil, nil,
85
+ # nil, nil, nil, nil, nil, nil, nil, nil,
86
+ # nil, nil, nil, nil, "โ™™", nil, nil, nil,
87
+ # nil, nil, nil, nil, nil, nil, nil, nil,
88
+ # "โ™™", "โ™™", "โ™™", "โ™™", nil, "โ™™", "โ™™", "โ™™",
89
+ # "โ™–", "โ™˜", "โ™—", "โ™•", "โ™”", "โ™—", "โ™˜", "โ™–"
90
+ # ]
91
+ # )
92
+ # # => "โ™œ,โ™ž,โ™,โ™›,โ™š,โ™,โ™ž,โ™œ/โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ/8/8/4,โ™™,3/8/โ™™,โ™™,โ™™,โ™™,1,โ™™,โ™™,โ™™/โ™–,โ™˜,โ™—,โ™•,โ™”,โ™—,โ™˜,โ™– 1 /"
93
+ #
94
+ # @example Dump Chess's position after the moves 1. e4 c5
95
+ # FEEN.dump(
96
+ # active_side: 0,
97
+ # indexes: [8, 8],
98
+ # pieces_in_hand_by_players: [
99
+ # [],
100
+ # []
101
+ # ],
102
+ # squares: [
103
+ # "โ™œ", "โ™ž", "โ™", "โ™›", "โ™š", "โ™", "โ™ž", "โ™œ",
104
+ # "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", nil, "โ™Ÿ", "โ™Ÿ", "โ™Ÿ",
105
+ # nil, nil, nil, nil, nil, nil, nil, nil,
106
+ # nil, nil, nil, nil, "โ™Ÿ", nil, nil, nil,
107
+ # nil, nil, nil, nil, "โ™™", nil, nil, nil,
108
+ # nil, nil, nil, nil, nil, nil, nil, nil,
109
+ # "โ™™", "โ™™", "โ™™", "โ™™", nil, "โ™™", "โ™™", "โ™™",
110
+ # "โ™–", "โ™˜", "โ™—", "โ™•", "โ™”", "โ™—", "โ™˜", "โ™–"
111
+ # ]
112
+ # )
113
+ # # => "โ™œ,โ™ž,โ™,โ™›,โ™š,โ™,โ™ž,โ™œ/โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,1,โ™Ÿ,โ™Ÿ,โ™Ÿ/8/4,โ™Ÿ,3/4,โ™™,3/8/โ™™,โ™™,โ™™,โ™™,1,โ™™,โ™™,โ™™/โ™–,โ™˜,โ™—,โ™•,โ™”,โ™—,โ™˜,โ™– 0 /"
114
+ #
115
+ # @example Dump Makruk's starting position
116
+ # FEEN.dump(
117
+ # active_side: 0,
118
+ # indexes: [8, 8],
119
+ # pieces_in_hand_by_players: [
120
+ # [],
121
+ # []
122
+ # ],
123
+ # squares: [
124
+ # "โ™œ", "โ™ž", "โ™", "โ™›", "โ™š", "โ™", "โ™ž", "โ™œ",
125
+ # nil, nil, nil, nil, nil, nil, nil, nil,
126
+ # "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ",
127
+ # nil, nil, nil, nil, nil, nil, nil, nil,
128
+ # nil, nil, nil, nil, nil, nil, nil, nil,
129
+ # "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™",
130
+ # nil, nil, nil, nil, nil, nil, nil, nil,
131
+ # "โ™–", "โ™˜", "โ™—", "โ™”", "โ™•", "โ™—", "โ™˜", "โ™–"
132
+ # ]
133
+ # )
134
+ # # => "โ™œ,โ™ž,โ™,โ™›,โ™š,โ™,โ™ž,โ™œ/8/โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ/8/8/โ™™,โ™™,โ™™,โ™™,โ™™,โ™™,โ™™,โ™™/8/โ™–,โ™˜,โ™—,โ™”,โ™•,โ™—,โ™˜,โ™– 0 /"
135
+ #
136
+ # @example Dump Shogi's starting position
137
+ # FEEN.dump(
138
+ # active_side: 0,
139
+ # indexes: [9, 9],
140
+ # pieces_in_hand_by_players: [
141
+ # [],
142
+ # []
143
+ # ],
144
+ # squares: [
145
+ # "l", "n", "s", "g", "k", "g", "s", "n", "l",
146
+ # nil, "r", nil, nil, nil, nil, nil, "b", nil,
147
+ # "p", "p", "p", "p", "p", "p", "p", "p", "p",
148
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
149
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
150
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
151
+ # "P", "P", "P", "P", "P", "P", "P", "P", "P",
152
+ # nil, "B", nil, nil, nil, nil, nil, "R", nil,
153
+ # "L", "N", "S", "G", "K", "G", "S", "N", "L"
154
+ # ]
155
+ # )
156
+ # # => "l,n,s,g,k,g,s,n,l/1,r,5,b,1/p,p,p,p,p,p,p,p,p/9/9/9/P,P,P,P,P,P,P,P,P/1,B,5,R,1/L,N,S,G,K,G,S,N,L 0 /"
157
+ #
158
+ # @example Dump a classic Tsume Shogi problem
159
+ # FEEN.dump(
160
+ # active_side: 0,
161
+ # indexes: [9, 9],
162
+ # pieces_in_hand_by_players: [
163
+ # %w[S],
164
+ # %w[r r b g g g g s n n n n p p p p p p p p p p p p p p p p p]
165
+ # ],
166
+ # squares: [
167
+ # nil, nil, nil, "s", "k", "s", nil, nil, nil,
168
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
169
+ # nil, nil, nil, nil, "+P", nil, nil, nil, nil,
170
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
171
+ # nil, nil, nil, nil, nil, nil, nil, "+B", nil,
172
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
173
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
174
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
175
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil
176
+ # ]
177
+ # )
178
+ # # => "3,s,k,s,3/9/4,+P,4/9/7,+B,1/9/9/9/9 0 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"
179
+ #
180
+ # @example Dump Xiangqi's starting position
181
+ # FEEN.dump(
182
+ # active_side: 0,
183
+ # indexes: [10, 9],
184
+ # pieces_in_hand_by_players: [
185
+ # [],
186
+ # []
187
+ # ],
188
+ # squares: [
189
+ # "่ปŠ", "้ฆฌ", "่ฑก", "ๅฃซ", "ๅฐ‡", "ๅฃซ", "่ฑก", "้ฆฌ", "่ปŠ",
190
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
191
+ # nil, "็ ฒ", nil, nil, nil, nil, nil, "็ ฒ", nil,
192
+ # "ๅ’", nil, "ๅ’", nil, "ๅ’", nil, "ๅ’", nil, "ๅ’",
193
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
194
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
195
+ # "ๅ…ต", nil, "ๅ…ต", nil, "ๅ…ต", nil, "ๅ…ต", nil, "ๅ…ต",
196
+ # nil, "็‚ฎ", nil, nil, nil, nil, nil, "็‚ฎ", nil,
197
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
198
+ # "ไฟฅ", "ๅ‚Œ", "็›ธ", "ไป•", "ๅธฅ", "ไป•", "็›ธ", "ๅ‚Œ", "ไฟฅ"
199
+ # ]
200
+ # )
201
+ # # => "่ปŠ,้ฆฌ,่ฑก,ๅฃซ,ๅฐ‡,ๅฃซ,่ฑก,้ฆฌ,่ปŠ/9/1,็ ฒ,5,็ ฒ,1/ๅ’,1,ๅ’,1,ๅ’,1,ๅ’,1,ๅ’/9/9/ๅ…ต,1,ๅ…ต,1,ๅ…ต,1,ๅ…ต,1,ๅ…ต/1,็‚ฎ,5,็‚ฎ,1/9/ไฟฅ,ๅ‚Œ,็›ธ,ไป•,ๅธฅ,ไป•,็›ธ,ๅ‚Œ,ไฟฅ 0 /"
202
+ #
203
+ # @example Parse an empty 3x8x8 board position
204
+ # FEEN.parse("8/8/8/8/8/8/8/8//8/8/8/8/8/8/8/8//8/8/8/8/8/8/8/8 0 /")
205
+ # # => {
206
+ # # active_side: 0,
207
+ # # indexes: [3, 8, 8],
208
+ # # pieces_in_hand_by_players: [
209
+ # # [],
210
+ # # []
211
+ # # ],
212
+ # # squares: Array.new(3 * 8 * 8)
213
+ # # }
214
+ #
215
+ # @example Parse Four-player chess's starting position
216
+ # FEEN.parse("3,yR,yN,yB,yK,yQ,yB,yN,yR,3/3,yP,yP,yP,yP,yP,yP,yP,yP,3/14/bR,bP,10,gP,gR/bN,bP,10,gP,gN/bB,bP,10,gP,gB/bK,bP,10,gP,gQ/bQ,bP,10,gP,gK/bB,bP,10,gP,gB/bN,bP,10,gP,gN/bR,bP,10,gP,gR/14/3,rP,rP,rP,rP,rP,rP,rP,rP,3/3,rR,rN,rB,rQ,rK,rB,rN,rR,3 0 ///")
217
+ # # => {
218
+ # # active_side: 0,
219
+ # # indexes: [14, 14],
220
+ # # pieces_in_hand_by_players: [
221
+ # # [],
222
+ # # [],
223
+ # # [],
224
+ # # []
225
+ # # ],
226
+ # # squares: [
227
+ # # nil , nil , nil , "yR", "yN", "yB", "yK", "yQ", "yB", "yN", "yR", nil , nil , nil ,
228
+ # # nil , nil , nil , "yP", "yP", "yP", "yP", "yP", "yP", "yP", "yP", nil , nil , nil ,
229
+ # # nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil ,
230
+ # # "bR", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gR",
231
+ # # "bN", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gN",
232
+ # # "bB", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gB",
233
+ # # "bK", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gQ",
234
+ # # "bQ", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gK",
235
+ # # "bB", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gB",
236
+ # # "bN", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gN",
237
+ # # "bR", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gR",
238
+ # # nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil ,
239
+ # # nil , nil , nil , "rP", "rP", "rP", "rP", "rP", "rP", "rP", "rP", nil , nil , nil ,
240
+ # # nil , nil , nil , "rR", "rN", "rB", "rQ", "rK", "rB", "rN", "rR", nil , nil , nil
241
+ # # ]
242
+ # # }
243
+ #
244
+ # @example Parse Chess's starting position
245
+ # FEEN.parse("โ™œ,โ™ž,โ™,โ™›,โ™š,โ™,โ™ž,โ™œ/โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ/8/8/8/8/โ™™,โ™™,โ™™,โ™™,โ™™,โ™™,โ™™,โ™™/โ™–,โ™˜,โ™—,โ™•,โ™”,โ™—,โ™˜,โ™– 0 /")
246
+ # # => {
247
+ # # active_side: 0,
248
+ # # indexes: [8, 8],
249
+ # # pieces_in_hand_by_players: [
250
+ # # [],
251
+ # # []
252
+ # # ],
253
+ # # squares: [
254
+ # # "โ™œ", "โ™ž", "โ™", "โ™›", "โ™š", "โ™", "โ™ž", "โ™œ",
255
+ # # "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ",
256
+ # # nil, nil, nil, nil, nil, nil, nil, nil,
257
+ # # nil, nil, nil, nil, nil, nil, nil, nil,
258
+ # # nil, nil, nil, nil, nil, nil, nil, nil,
259
+ # # nil, nil, nil, nil, nil, nil, nil, nil,
260
+ # # "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™",
261
+ # # "โ™–", "โ™˜", "โ™—", "โ™•", "โ™”", "โ™—", "โ™˜", "โ™–"
262
+ # # ]
263
+ # # }
264
+ #
265
+ # @example Parse Makruk's starting position
266
+ # FEEN.parse("โ™œ,โ™ž,โ™,โ™›,โ™š,โ™,โ™ž,โ™œ/8/โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ/8/8/โ™™,โ™™,โ™™,โ™™,โ™™,โ™™,โ™™,โ™™/8/โ™–,โ™˜,โ™—,โ™”,โ™•,โ™—,โ™˜,โ™– 0 /")
267
+ # # => {
268
+ # # active_side: 0,
269
+ # # indexes: [8, 8],
270
+ # # pieces_in_hand_by_players: [
271
+ # # [],
272
+ # # []
273
+ # # ],
274
+ # # squares: [
275
+ # # "โ™œ", "โ™ž", "โ™", "โ™›", "โ™š", "โ™", "โ™ž", "โ™œ",
276
+ # # nil, nil, nil, nil, nil, nil, nil, nil,
277
+ # # "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ",
278
+ # # nil, nil, nil, nil, nil, nil, nil, nil,
279
+ # # nil, nil, nil, nil, nil, nil, nil, nil,
280
+ # # "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™",
281
+ # # nil, nil, nil, nil, nil, nil, nil, nil,
282
+ # # "โ™–", "โ™˜", "โ™—", "โ™”", "โ™•", "โ™—", "โ™˜", "โ™–"
283
+ # # ]
284
+ # # }
285
+ #
286
+ # @example Parse Shogi's starting position
287
+ # FEEN.parse("l,n,s,g,k,g,s,n,l/1,r,5,b,1/p,p,p,p,p,p,p,p,p/9/9/9/P,P,P,P,P,P,P,P,P/1,B,5,R,1/L,N,S,G,K,G,S,N,L 0 /")
288
+ # # => {
289
+ # # active_side: 0,
290
+ # # indexes: [9, 9],
291
+ # # pieces_in_hand_by_players: [
292
+ # # [],
293
+ # # []
294
+ # # ],
295
+ # # squares: [
296
+ # # "l", "n", "s", "g", "k", "g", "s", "n", "l",
297
+ # # nil, "r", nil, nil, nil, nil, nil, "b", nil,
298
+ # # "p", "p", "p", "p", "p", "p", "p", "p", "p",
299
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil,
300
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil,
301
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil,
302
+ # # "P", "P", "P", "P", "P", "P", "P", "P", "P",
303
+ # # nil, "B", nil, nil, nil, nil, nil, "R", nil,
304
+ # # "L", "N", "S", "G", "K", "G", "S", "N", "L"
305
+ # # ]
306
+ # # }
307
+ #
308
+ # @example Parse a classic Tsume Shogi problem
309
+ # FEEN.parse("3,s,k,s,3/9/4,+P,4/9/7,+B,1/9/9/9/9 0 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")
310
+ # # => {
311
+ # # active_side: 0,
312
+ # # indexes: [9, 9],
313
+ # # pieces_in_hand_by_players: [
314
+ # # %w[S],
315
+ # # %w[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]
316
+ # # ],
317
+ # # squares: [
318
+ # # nil, nil, nil, "s", "k", "s", nil, nil, nil,
319
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil,
320
+ # # nil, nil, nil, nil, "+P", nil, nil, nil, nil,
321
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil,
322
+ # # nil, nil, nil, nil, nil, nil, nil, "+B", nil,
323
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil,
324
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil,
325
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil,
326
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil
327
+ # # ]
328
+ # # }
329
+ #
330
+ # @example Parse Xiangqi's starting position
331
+ # FEEN.parse("่ปŠ,้ฆฌ,่ฑก,ๅฃซ,ๅฐ‡,ๅฃซ,่ฑก,้ฆฌ,่ปŠ/9/1,็ ฒ,5,็ ฒ,1/ๅ’,1,ๅ’,1,ๅ’,1,ๅ’,1,ๅ’/9/9/ๅ…ต,1,ๅ…ต,1,ๅ…ต,1,ๅ…ต,1,ๅ…ต/1,็‚ฎ,5,็‚ฎ,1/9/ไฟฅ,ๅ‚Œ,็›ธ,ไป•,ๅธฅ,ไป•,็›ธ,ๅ‚Œ,ไฟฅ 0 /")
332
+ # # => {
333
+ # # active_side: 0,
334
+ # # indexes: [10, 9],
335
+ # # pieces_in_hand_by_players: [
336
+ # # [],
337
+ # # []
338
+ # # ],
339
+ # # squares: [
340
+ # # "่ปŠ", "้ฆฌ", "่ฑก", "ๅฃซ", "ๅฐ‡", "ๅฃซ", "่ฑก", "้ฆฌ", "่ปŠ",
341
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil,
342
+ # # nil, "็ ฒ", nil, nil, nil, nil, nil, "็ ฒ", nil,
343
+ # # "ๅ’", nil, "ๅ’", nil, "ๅ’", nil, "ๅ’", nil, "ๅ’",
344
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil,
345
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil,
346
+ # # "ๅ…ต", nil, "ๅ…ต", nil, "ๅ…ต", nil, "ๅ…ต", nil, "ๅ…ต",
347
+ # # nil, "็‚ฎ", nil, nil, nil, nil, nil, "็‚ฎ", nil,
348
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil,
349
+ # # "ไฟฅ", "ๅ‚Œ", "็›ธ", "ไป•", "ๅธฅ", "ไป•", "็›ธ", "ๅ‚Œ", "ไฟฅ"
350
+ # # ]
351
+ # # }
352
+ module FEEN
353
+ # @example Dumps position params into a FEEN string.
354
+ #
355
+ # @param active_side [Integer] The identifier of the player who must play.
356
+ # @param indexes [Array] The shape of the board.
357
+ # @param pieces_in_hand_by_players [Array] The list of pieces in hand
358
+ # grouped by players.
359
+ # @param squares [Array] The list of squares on the board.
360
+ #
361
+ # @example Dump an empty 3x8x8 board position
362
+ # dump(
363
+ # active_side: 0,
364
+ # indexes: [3, 8, 8],
365
+ # pieces_in_hand_by_players: [
366
+ # [],
367
+ # []
368
+ # ],
369
+ # squares: Array.new(3 * 8 * 8)
370
+ # )
371
+ # # => "8/8/8/8/8/8/8/8//8/8/8/8/8/8/8/8//8/8/8/8/8/8/8/8 0 /"
372
+ #
373
+ # @example Dump Four-player chess's starting position
374
+ # dump(
375
+ # active_side: 0,
376
+ # indexes: [14, 14],
377
+ # pieces_in_hand_by_players: [
378
+ # [],
379
+ # [],
380
+ # [],
381
+ # []
382
+ # ],
383
+ # squares: [
384
+ # nil , nil , nil , "yR", "yN", "yB", "yK", "yQ", "yB", "yN", "yR", nil , nil , nil ,
385
+ # nil , nil , nil , "yP", "yP", "yP", "yP", "yP", "yP", "yP", "yP", nil , nil , nil ,
386
+ # nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil ,
387
+ # "bR", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gR",
388
+ # "bN", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gN",
389
+ # "bB", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gB",
390
+ # "bK", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gQ",
391
+ # "bQ", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gK",
392
+ # "bB", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gB",
393
+ # "bN", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gN",
394
+ # "bR", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gR",
395
+ # nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil ,
396
+ # nil , nil , nil , "rP", "rP", "rP", "rP", "rP", "rP", "rP", "rP", nil , nil , nil ,
397
+ # nil , nil , nil , "rR", "rN", "rB", "rQ", "rK", "rB", "rN", "rR", nil , nil , nil
398
+ # ]
399
+ # )
400
+ # # => "3,yR,yN,yB,yK,yQ,yB,yN,yR,3/3,yP,yP,yP,yP,yP,yP,yP,yP,3/14/bR,bP,10,gP,gR/bN,bP,10,gP,gN/bB,bP,10,gP,gB/bK,bP,10,gP,gQ/bQ,bP,10,gP,gK/bB,bP,10,gP,gB/bN,bP,10,gP,gN/bR,bP,10,gP,gR/14/3,rP,rP,rP,rP,rP,rP,rP,rP,3/3,rR,rN,rB,rQ,rK,rB,rN,rR,3 0 ///"
401
+ #
402
+ # @example Dump Chess's starting position
403
+ # dump(
404
+ # active_side: 0,
405
+ # indexes: [8, 8],
406
+ # pieces_in_hand_by_players: [
407
+ # [],
408
+ # []
409
+ # ],
410
+ # squares: [
411
+ # "โ™œ", "โ™ž", "โ™", "โ™›", "โ™š", "โ™", "โ™ž", "โ™œ",
412
+ # "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ",
413
+ # nil, nil, nil, nil, nil, nil, nil, nil,
414
+ # nil, nil, nil, nil, nil, nil, nil, nil,
415
+ # nil, nil, nil, nil, nil, nil, nil, nil,
416
+ # nil, nil, nil, nil, nil, nil, nil, nil,
417
+ # "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™",
418
+ # "โ™–", "โ™˜", "โ™—", "โ™•", "โ™”", "โ™—", "โ™˜", "โ™–"
419
+ # ]
420
+ # )
421
+ # # => "โ™œ,โ™ž,โ™,โ™›,โ™š,โ™,โ™ž,โ™œ/โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ/8/8/8/8/โ™™,โ™™,โ™™,โ™™,โ™™,โ™™,โ™™,โ™™/โ™–,โ™˜,โ™—,โ™•,โ™”,โ™—,โ™˜,โ™– 0 /"
422
+ #
423
+ # @example Dump Chess's position after the move 1. e4
424
+ # dump(
425
+ # active_side: 1,
426
+ # indexes: [8, 8],
427
+ # pieces_in_hand_by_players: [
428
+ # [],
429
+ # []
430
+ # ],
431
+ # squares: [
432
+ # "โ™œ", "โ™ž", "โ™", "โ™›", "โ™š", "โ™", "โ™ž", "โ™œ",
433
+ # "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ",
434
+ # nil, nil, nil, nil, nil, nil, nil, nil,
435
+ # nil, nil, nil, nil, nil, nil, nil, nil,
436
+ # nil, nil, nil, nil, "โ™™", nil, nil, nil,
437
+ # nil, nil, nil, nil, nil, nil, nil, nil,
438
+ # "โ™™", "โ™™", "โ™™", "โ™™", nil, "โ™™", "โ™™", "โ™™",
439
+ # "โ™–", "โ™˜", "โ™—", "โ™•", "โ™”", "โ™—", "โ™˜", "โ™–"
440
+ # ]
441
+ # )
442
+ # # => "โ™œ,โ™ž,โ™,โ™›,โ™š,โ™,โ™ž,โ™œ/โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ/8/8/4,โ™™,3/8/โ™™,โ™™,โ™™,โ™™,1,โ™™,โ™™,โ™™/โ™–,โ™˜,โ™—,โ™•,โ™”,โ™—,โ™˜,โ™– 1 /"
443
+ #
444
+ # @example Dump Chess's position after the moves 1. e4 c5
445
+ # dump(
446
+ # active_side: 0,
447
+ # indexes: [8, 8],
448
+ # pieces_in_hand_by_players: [
449
+ # [],
450
+ # []
451
+ # ],
452
+ # squares: [
453
+ # "โ™œ", "โ™ž", "โ™", "โ™›", "โ™š", "โ™", "โ™ž", "โ™œ",
454
+ # "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", nil, "โ™Ÿ", "โ™Ÿ", "โ™Ÿ",
455
+ # nil, nil, nil, nil, nil, nil, nil, nil,
456
+ # nil, nil, nil, nil, "โ™Ÿ", nil, nil, nil,
457
+ # nil, nil, nil, nil, "โ™™", nil, nil, nil,
458
+ # nil, nil, nil, nil, nil, nil, nil, nil,
459
+ # "โ™™", "โ™™", "โ™™", "โ™™", nil, "โ™™", "โ™™", "โ™™",
460
+ # "โ™–", "โ™˜", "โ™—", "โ™•", "โ™”", "โ™—", "โ™˜", "โ™–"
461
+ # ]
462
+ # )
463
+ # # => "โ™œ,โ™ž,โ™,โ™›,โ™š,โ™,โ™ž,โ™œ/โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,1,โ™Ÿ,โ™Ÿ,โ™Ÿ/8/4,โ™Ÿ,3/4,โ™™,3/8/โ™™,โ™™,โ™™,โ™™,1,โ™™,โ™™,โ™™/โ™–,โ™˜,โ™—,โ™•,โ™”,โ™—,โ™˜,โ™– 0 /"
464
+ #
465
+ # @example Dump Makruk's starting position
466
+ # dump(
467
+ # active_side: 0,
468
+ # indexes: [8, 8],
469
+ # pieces_in_hand_by_players: [
470
+ # [],
471
+ # []
472
+ # ],
473
+ # squares: [
474
+ # "โ™œ", "โ™ž", "โ™", "โ™›", "โ™š", "โ™", "โ™ž", "โ™œ",
475
+ # nil, nil, nil, nil, nil, nil, nil, nil,
476
+ # "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ",
477
+ # nil, nil, nil, nil, nil, nil, nil, nil,
478
+ # nil, nil, nil, nil, nil, nil, nil, nil,
479
+ # "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™",
480
+ # nil, nil, nil, nil, nil, nil, nil, nil,
481
+ # "โ™–", "โ™˜", "โ™—", "โ™”", "โ™•", "โ™—", "โ™˜", "โ™–"
482
+ # ]
483
+ # )
484
+ # # => "โ™œ,โ™ž,โ™,โ™›,โ™š,โ™,โ™ž,โ™œ/8/โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ/8/8/โ™™,โ™™,โ™™,โ™™,โ™™,โ™™,โ™™,โ™™/8/โ™–,โ™˜,โ™—,โ™”,โ™•,โ™—,โ™˜,โ™– 0 /"
485
+ #
486
+ # @example Dump Shogi's starting position
487
+ # dump(
488
+ # active_side: 0,
489
+ # indexes: [9, 9],
490
+ # pieces_in_hand_by_players: [
491
+ # [],
492
+ # []
493
+ # ],
494
+ # squares: [
495
+ # "l", "n", "s", "g", "k", "g", "s", "n", "l",
496
+ # nil, "r", nil, nil, nil, nil, nil, "b", nil,
497
+ # "p", "p", "p", "p", "p", "p", "p", "p", "p",
498
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
499
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
500
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
501
+ # "P", "P", "P", "P", "P", "P", "P", "P", "P",
502
+ # nil, "B", nil, nil, nil, nil, nil, "R", nil,
503
+ # "L", "N", "S", "G", "K", "G", "S", "N", "L"
504
+ # ]
505
+ # )
506
+ # # => "l,n,s,g,k,g,s,n,l/1,r,5,b,1/p,p,p,p,p,p,p,p,p/9/9/9/P,P,P,P,P,P,P,P,P/1,B,5,R,1/L,N,S,G,K,G,S,N,L 0 /"
507
+ #
508
+ # @example Dump a classic Tsume Shogi problem
509
+ # dump(
510
+ # active_side: 0,
511
+ # indexes: [9, 9],
512
+ # pieces_in_hand_by_players: [
513
+ # %w[S],
514
+ # %w[r r b g g g g s n n n n p p p p p p p p p p p p p p p p p]
515
+ # ],
516
+ # squares: [
517
+ # nil, nil, nil, "s", "k", "s", nil, nil, nil,
518
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
519
+ # nil, nil, nil, nil, "+P", nil, nil, nil, nil,
520
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
521
+ # nil, nil, nil, nil, nil, nil, nil, "+B", nil,
522
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
523
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
524
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
525
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil
526
+ # ]
527
+ # )
528
+ # # => "3,s,k,s,3/9/4,+P,4/9/7,+B,1/9/9/9/9 0 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"
529
+ #
530
+ # @example Dump Xiangqi's starting position
531
+ # dump(
532
+ # active_side: 0,
533
+ # indexes: [10, 9],
534
+ # pieces_in_hand_by_players: [
535
+ # [],
536
+ # []
537
+ # ],
538
+ # squares: [
539
+ # "่ปŠ", "้ฆฌ", "่ฑก", "ๅฃซ", "ๅฐ‡", "ๅฃซ", "่ฑก", "้ฆฌ", "่ปŠ",
540
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
541
+ # nil, "็ ฒ", nil, nil, nil, nil, nil, "็ ฒ", nil,
542
+ # "ๅ’", nil, "ๅ’", nil, "ๅ’", nil, "ๅ’", nil, "ๅ’",
543
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
544
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
545
+ # "ๅ…ต", nil, "ๅ…ต", nil, "ๅ…ต", nil, "ๅ…ต", nil, "ๅ…ต",
546
+ # nil, "็‚ฎ", nil, nil, nil, nil, nil, "็‚ฎ", nil,
547
+ # nil, nil, nil, nil, nil, nil, nil, nil, nil,
548
+ # "ไฟฅ", "ๅ‚Œ", "็›ธ", "ไป•", "ๅธฅ", "ไป•", "็›ธ", "ๅ‚Œ", "ไฟฅ"
549
+ # ]
550
+ # )
551
+ # # => "่ปŠ,้ฆฌ,่ฑก,ๅฃซ,ๅฐ‡,ๅฃซ,่ฑก,้ฆฌ,่ปŠ/9/1,็ ฒ,5,็ ฒ,1/ๅ’,1,ๅ’,1,ๅ’,1,ๅ’,1,ๅ’/9/9/ๅ…ต,1,ๅ…ต,1,ๅ…ต,1,ๅ…ต,1,ๅ…ต/1,็‚ฎ,5,็‚ฎ,1/9/ไฟฅ,ๅ‚Œ,็›ธ,ไป•,ๅธฅ,ไป•,็›ธ,ๅ‚Œ,ไฟฅ 0 /"
552
+ #
553
+ # @return [String] The FEEN string representing the position.
554
+ def self.dump(active_side:, indexes:, pieces_in_hand_by_players:, squares:)
555
+ Dumper.call(
556
+ active_side: active_side,
557
+ indexes: indexes,
558
+ pieces_in_hand_by_players: pieces_in_hand_by_players,
559
+ squares: squares
560
+ )
561
+ end
562
+
563
+ # Parses a FEEN string into position params.
564
+ #
565
+ # @param feen [String] The FEEN string representing a position.
566
+ #
567
+ # @example Parse an empty 3x8x8 board position
568
+ # parse("8/8/8/8/8/8/8/8//8/8/8/8/8/8/8/8//8/8/8/8/8/8/8/8 0 /")
569
+ # # => {
570
+ # # active_side: 0,
571
+ # # indexes: [3, 8, 8],
572
+ # # pieces_in_hand_by_players: [
573
+ # # [],
574
+ # # []
575
+ # # ],
576
+ # # squares: Array.new(3 * 8 * 8)
577
+ # # }
578
+ #
579
+ # @example Parse Four-player chess's starting position
580
+ # parse("3,yR,yN,yB,yK,yQ,yB,yN,yR,3/3,yP,yP,yP,yP,yP,yP,yP,yP,3/14/bR,bP,10,gP,gR/bN,bP,10,gP,gN/bB,bP,10,gP,gB/bK,bP,10,gP,gQ/bQ,bP,10,gP,gK/bB,bP,10,gP,gB/bN,bP,10,gP,gN/bR,bP,10,gP,gR/14/3,rP,rP,rP,rP,rP,rP,rP,rP,3/3,rR,rN,rB,rQ,rK,rB,rN,rR,3 0 ///")
581
+ # # => {
582
+ # # active_side: 0,
583
+ # # indexes: [14, 14],
584
+ # # pieces_in_hand_by_players: [
585
+ # # [],
586
+ # # [],
587
+ # # [],
588
+ # # []
589
+ # # ],
590
+ # # squares: [
591
+ # # nil , nil , nil , "yR", "yN", "yB", "yK", "yQ", "yB", "yN", "yR", nil , nil , nil ,
592
+ # # nil , nil , nil , "yP", "yP", "yP", "yP", "yP", "yP", "yP", "yP", nil , nil , nil ,
593
+ # # nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil ,
594
+ # # "bR", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gR",
595
+ # # "bN", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gN",
596
+ # # "bB", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gB",
597
+ # # "bK", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gQ",
598
+ # # "bQ", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gK",
599
+ # # "bB", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gB",
600
+ # # "bN", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gN",
601
+ # # "bR", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gR",
602
+ # # nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil ,
603
+ # # nil , nil , nil , "rP", "rP", "rP", "rP", "rP", "rP", "rP", "rP", nil , nil , nil ,
604
+ # # nil , nil , nil , "rR", "rN", "rB", "rQ", "rK", "rB", "rN", "rR", nil , nil , nil
605
+ # # ]
606
+ # # }
607
+ #
608
+ # @example Parse Chess's starting position
609
+ # parse("โ™œ,โ™ž,โ™,โ™›,โ™š,โ™,โ™ž,โ™œ/โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ/8/8/8/8/โ™™,โ™™,โ™™,โ™™,โ™™,โ™™,โ™™,โ™™/โ™–,โ™˜,โ™—,โ™•,โ™”,โ™—,โ™˜,โ™– 0 /")
610
+ # # => {
611
+ # # active_side: 0,
612
+ # # indexes: [8, 8],
613
+ # # pieces_in_hand_by_players: [
614
+ # # [],
615
+ # # []
616
+ # # ],
617
+ # # squares: [
618
+ # # "โ™œ", "โ™ž", "โ™", "โ™›", "โ™š", "โ™", "โ™ž", "โ™œ",
619
+ # # "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ",
620
+ # # nil, nil, nil, nil, nil, nil, nil, nil,
621
+ # # nil, nil, nil, nil, nil, nil, nil, nil,
622
+ # # nil, nil, nil, nil, nil, nil, nil, nil,
623
+ # # nil, nil, nil, nil, nil, nil, nil, nil,
624
+ # # "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™",
625
+ # # "โ™–", "โ™˜", "โ™—", "โ™•", "โ™”", "โ™—", "โ™˜", "โ™–"
626
+ # # ]
627
+ # # }
628
+ #
629
+ # @example Parse Makruk's starting position
630
+ # parse("โ™œ,โ™ž,โ™,โ™›,โ™š,โ™,โ™ž,โ™œ/8/โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ,โ™Ÿ/8/8/โ™™,โ™™,โ™™,โ™™,โ™™,โ™™,โ™™,โ™™/8/โ™–,โ™˜,โ™—,โ™”,โ™•,โ™—,โ™˜,โ™– 0 /")
631
+ # # => {
632
+ # # active_side: 0,
633
+ # # indexes: [8, 8],
634
+ # # pieces_in_hand_by_players: [
635
+ # # [],
636
+ # # []
637
+ # # ],
638
+ # # squares: [
639
+ # # "โ™œ", "โ™ž", "โ™", "โ™›", "โ™š", "โ™", "โ™ž", "โ™œ",
640
+ # # nil, nil, nil, nil, nil, nil, nil, nil,
641
+ # # "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ", "โ™Ÿ",
642
+ # # nil, nil, nil, nil, nil, nil, nil, nil,
643
+ # # nil, nil, nil, nil, nil, nil, nil, nil,
644
+ # # "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™", "โ™™",
645
+ # # nil, nil, nil, nil, nil, nil, nil, nil,
646
+ # # "โ™–", "โ™˜", "โ™—", "โ™”", "โ™•", "โ™—", "โ™˜", "โ™–"
647
+ # # ]
648
+ # # }
649
+ #
650
+ # @example Parse Shogi's starting position
651
+ # parse("l,n,s,g,k,g,s,n,l/1,r,5,b,1/p,p,p,p,p,p,p,p,p/9/9/9/P,P,P,P,P,P,P,P,P/1,B,5,R,1/L,N,S,G,K,G,S,N,L 0 /")
652
+ # # => {
653
+ # # active_side: 0,
654
+ # # indexes: [9, 9],
655
+ # # pieces_in_hand_by_players: [
656
+ # # [],
657
+ # # []
658
+ # # ],
659
+ # # squares: [
660
+ # # "l", "n", "s", "g", "k", "g", "s", "n", "l",
661
+ # # nil, "r", nil, nil, nil, nil, nil, "b", nil,
662
+ # # "p", "p", "p", "p", "p", "p", "p", "p", "p",
663
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil,
664
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil,
665
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil,
666
+ # # "P", "P", "P", "P", "P", "P", "P", "P", "P",
667
+ # # nil, "B", nil, nil, nil, nil, nil, "R", nil,
668
+ # # "L", "N", "S", "G", "K", "G", "S", "N", "L"
669
+ # # ]
670
+ # # }
671
+ #
672
+ # @example Parse a classic Tsume Shogi problem
673
+ # parse("3,s,k,s,3/9/4,+P,4/9/7,+B,1/9/9/9/9 0 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")
674
+ # # => {
675
+ # # active_side: 0,
676
+ # # indexes: [9, 9],
677
+ # # pieces_in_hand_by_players: [
678
+ # # %w[S],
679
+ # # %w[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]
680
+ # # ],
681
+ # # squares: [
682
+ # # nil, nil, nil, "s", "k", "s", nil, nil, nil,
683
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil,
684
+ # # nil, nil, nil, nil, "+P", nil, nil, nil, nil,
685
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil,
686
+ # # nil, nil, nil, nil, nil, nil, nil, "+B", nil,
687
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil,
688
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil,
689
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil,
690
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil
691
+ # # ]
692
+ # # }
693
+ #
694
+ # @example Parse Xiangqi's starting position
695
+ # parse("่ปŠ,้ฆฌ,่ฑก,ๅฃซ,ๅฐ‡,ๅฃซ,่ฑก,้ฆฌ,่ปŠ/9/1,็ ฒ,5,็ ฒ,1/ๅ’,1,ๅ’,1,ๅ’,1,ๅ’,1,ๅ’/9/9/ๅ…ต,1,ๅ…ต,1,ๅ…ต,1,ๅ…ต,1,ๅ…ต/1,็‚ฎ,5,็‚ฎ,1/9/ไฟฅ,ๅ‚Œ,็›ธ,ไป•,ๅธฅ,ไป•,็›ธ,ๅ‚Œ,ไฟฅ 0 /")
696
+ # # => {
697
+ # # active_side: 0,
698
+ # # indexes: [10, 9],
699
+ # # pieces_in_hand_by_players: [
700
+ # # [],
701
+ # # []
702
+ # # ],
703
+ # # squares: [
704
+ # # "่ปŠ", "้ฆฌ", "่ฑก", "ๅฃซ", "ๅฐ‡", "ๅฃซ", "่ฑก", "้ฆฌ", "่ปŠ",
705
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil,
706
+ # # nil, "็ ฒ", nil, nil, nil, nil, nil, "็ ฒ", nil,
707
+ # # "ๅ’", nil, "ๅ’", nil, "ๅ’", nil, "ๅ’", nil, "ๅ’",
708
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil,
709
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil,
710
+ # # "ๅ…ต", nil, "ๅ…ต", nil, "ๅ…ต", nil, "ๅ…ต", nil, "ๅ…ต",
711
+ # # nil, "็‚ฎ", nil, nil, nil, nil, nil, "็‚ฎ", nil,
712
+ # # nil, nil, nil, nil, nil, nil, nil, nil, nil,
713
+ # # "ไฟฅ", "ๅ‚Œ", "็›ธ", "ไป•", "ๅธฅ", "ไป•", "็›ธ", "ๅ‚Œ", "ไฟฅ"
714
+ # # ]
715
+ # # }
716
+ # @return [Hash] The position params representing the position.
717
+ def self.parse(feen)
718
+ Parser.call(feen)
719
+ end
720
+ end