feen 5.0.0.beta4 → 5.0.0.beta5
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 +4 -4
- data/README.md +22 -21
- data/lib/feen/dumper/piece_placement.rb +1 -1
- data/lib/feen/dumper.rb +3 -3
- data/lib/feen/parser/piece_placement.rb +4 -6
- data/lib/feen/parser.rb +4 -4
- data/lib/feen.rb +7 -7
- metadata +1 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 28b60c3d60fd3241172fee9e143c53e6dca04082a2efe7f0fa7559b85dc6eac0
|
4
|
+
data.tar.gz: a15999144de7bcd5637b6efca4ed1c4a8ae7d3757d6d583123dfaf44468d6d35
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 861669bea6101dba5eefcf6062b9e4e0b37152618e7ffa9c1ba963eb37b2f49cad804f4c1bbee31bd030bf4ddd9602563df7e54cc244384d37e1f45ec748c5ff
|
7
|
+
data.tar.gz: 6f00dcfc09fd378fde0fbe3cd36b078718eab1fa27e208d474637ef89853a7543be4027116d623d0e037c340f0deeae90ce337404db09c953faecba852f56ece
|
data/README.md
CHANGED
@@ -12,6 +12,7 @@
|
|
12
12
|
FEEN (Forsyth–Edwards Enhanced Notation) is a compact, canonical, and rule-agnostic textual format for representing static board positions in two-player piece-placement games.
|
13
13
|
|
14
14
|
This gem implements the [FEEN Specification v1.0.0](https://sashite.dev/documents/feen/1.0.0/), providing a Ruby interface for:
|
15
|
+
|
15
16
|
- Representing positions from various games without knowledge of specific rules
|
16
17
|
- Supporting boards of arbitrary dimensions
|
17
18
|
- Encoding pieces in hand (as used in Shogi)
|
@@ -22,7 +23,7 @@ This gem implements the [FEEN Specification v1.0.0](https://sashite.dev/document
|
|
22
23
|
|
23
24
|
```ruby
|
24
25
|
# In your Gemfile
|
25
|
-
gem "feen", ">= 5.0.0.
|
26
|
+
gem "feen", ">= 5.0.0.beta5"
|
26
27
|
```
|
27
28
|
|
28
29
|
Or install manually:
|
@@ -48,20 +49,20 @@ Convert a FEEN string into a structured Ruby object:
|
|
48
49
|
```ruby
|
49
50
|
require "feen"
|
50
51
|
|
51
|
-
feen_string = "
|
52
|
+
feen_string = "r'nbqkbnr'/pppppppp/8/8/8/8/PPPPPPPP/R'NBQKBNR' - CHESS/chess"
|
52
53
|
position = Feen.parse(feen_string)
|
53
54
|
|
54
55
|
# Result is a hash:
|
55
56
|
# {
|
56
57
|
# piece_placement: [
|
57
|
-
# ["r", "n", "b", "q", "k
|
58
|
+
# ["r'", "n", "b", "q", "k", "b", "n", "r'"],
|
58
59
|
# ["p", "p", "p", "p", "p", "p", "p", "p"],
|
59
60
|
# ["", "", "", "", "", "", "", ""],
|
60
61
|
# ["", "", "", "", "", "", "", ""],
|
61
62
|
# ["", "", "", "", "", "", "", ""],
|
62
63
|
# ["", "", "", "", "", "", "", ""],
|
63
64
|
# ["P", "P", "P", "P", "P", "P", "P", "P"],
|
64
|
-
# ["R", "N", "B", "Q", "K
|
65
|
+
# ["R'", "N", "B", "Q", "K", "B", "N", "R'"]
|
65
66
|
# ],
|
66
67
|
# pieces_in_hand: [],
|
67
68
|
# games_turn: ["CHESS", "chess"]
|
@@ -76,7 +77,7 @@ Parse a FEEN string without raising exceptions:
|
|
76
77
|
require "feen"
|
77
78
|
|
78
79
|
# Valid FEEN string
|
79
|
-
result = Feen.safe_parse("
|
80
|
+
result = Feen.safe_parse("r'nbqkbnr'/pppppppp/8/8/8/8/PPPPPPPP/R'NBQKBNR' - CHESS/chess")
|
80
81
|
# => {piece_placement: [...], pieces_in_hand: [...], games_turn: [...]}
|
81
82
|
|
82
83
|
# Invalid FEEN string
|
@@ -93,14 +94,14 @@ require "feen"
|
|
93
94
|
|
94
95
|
# Representation of a chess board in initial position
|
95
96
|
piece_placement = [
|
96
|
-
["r", "n", "b", "q", "k
|
97
|
+
["r'", "n", "b", "q", "k", "b", "n", "r'"],
|
97
98
|
["p", "p", "p", "p", "p", "p", "p", "p"],
|
98
99
|
["", "", "", "", "", "", "", ""],
|
99
100
|
["", "", "", "", "", "", "", ""],
|
100
101
|
["", "", "", "", "", "", "", ""],
|
101
102
|
["", "", "", "", "", "", "", ""],
|
102
103
|
["P", "P", "P", "P", "P", "P", "P", "P"],
|
103
|
-
["R", "N", "B", "Q", "K
|
104
|
+
["R'", "N", "B", "Q", "K", "B", "N", "R'"]
|
104
105
|
]
|
105
106
|
|
106
107
|
result = Feen.dump(
|
@@ -108,7 +109,7 @@ result = Feen.dump(
|
|
108
109
|
games_turn: %w[CHESS chess],
|
109
110
|
pieces_in_hand: []
|
110
111
|
)
|
111
|
-
# => "
|
112
|
+
# => "r'nbqkbnr'/pppppppp/8/8/8/8/PPPPPPPP/R'NBQKBNR' - CHESS/chess"
|
112
113
|
```
|
113
114
|
|
114
115
|
### Validation
|
@@ -132,6 +133,7 @@ Feen.valid?("lnsgk3l/5g3/p1ppB2pp/9/8B/2P6/P2PPPPPP/3K3R1/5rSNL N5P2gn2sl SHOGI/
|
|
132
133
|
```
|
133
134
|
|
134
135
|
The `valid?` method performs two levels of validation:
|
136
|
+
|
135
137
|
1. **Syntax check**: Verifies the string can be parsed as FEEN
|
136
138
|
2. **Canonicity check**: Ensures the string is in its canonical form through round-trip conversion
|
137
139
|
|
@@ -142,11 +144,12 @@ As FEEN is rule-agnostic, it can represent positions from various board games. H
|
|
142
144
|
### International Chess
|
143
145
|
|
144
146
|
```ruby
|
145
|
-
feen_string = "
|
147
|
+
feen_string = "r'nbqkbnr'/pppppppp/8/8/8/8/PPPPPPPP/R'NBQKBNR' - CHESS/chess"
|
146
148
|
```
|
147
149
|
|
148
150
|
In this initial chess position:
|
149
|
-
|
151
|
+
|
152
|
+
- The `'` suffixes on rooks indicate an intermediate state (which might represent castling rights in chess, though FEEN doesn't define this semantics)
|
150
153
|
- The third field `CHESS/chess` indicates it's the player with uppercase pieces' turn to move
|
151
154
|
|
152
155
|
### Shogi (Japanese Chess)
|
@@ -156,6 +159,7 @@ feen_string = "lnsgk3l/5g3/p1ppB2pp/9/8B/2P6/P2PPPPPP/3K3R1/5rSNL N5P2gln2s SHOG
|
|
156
159
|
```
|
157
160
|
|
158
161
|
In this shogi position:
|
162
|
+
|
159
163
|
- The format supports promotions with the `+` prefix (e.g., `+P` for a promoted pawn)
|
160
164
|
- The notation allows for pieces in hand, indicated in the second field
|
161
165
|
- `SHOGI/shogi` indicates it's Sente's (Black's, uppercase) turn to move
|
@@ -176,6 +180,7 @@ feen_string = "rheagaehr/9/1c5c1/s1s1s1s1s/9/9/S1S1S1S1S/1C5C1/9/RHEAGAEHR - XIA
|
|
176
180
|
```
|
177
181
|
|
178
182
|
In this Xiangqi position:
|
183
|
+
|
179
184
|
- The representation uses single letters for the different pieces
|
180
185
|
- The format naturally adapts to the presence of a "river" (empty space in the middle)
|
181
186
|
|
@@ -212,19 +217,15 @@ result = Feen.dump(
|
|
212
217
|
|
213
218
|
FEEN supports prefixes and suffixes for pieces to denote various states or capabilities:
|
214
219
|
|
215
|
-
- **Prefix `+`**:
|
220
|
+
- **Prefix `+`**: Enhanced state
|
216
221
|
- Example in shogi: `+P` may represent a promoted pawn
|
217
222
|
|
218
|
-
- **
|
219
|
-
-
|
220
|
-
|
221
|
-
- **Suffix `<`**: May indicate left-side constraint
|
222
|
-
- Example in chess: `K<` may represent a king eligible for queenside castling only
|
223
|
-
- Example in chess: `P<` may represent a pawn that may be captured _en passant_ from the left
|
223
|
+
- **Prefix `-`**: Diminished state
|
224
|
+
- Could represent a piece with limited movement or other restrictions
|
224
225
|
|
225
|
-
- **Suffix
|
226
|
-
- Example in chess: `
|
227
|
-
- Example in chess: `P
|
226
|
+
- **Suffix `'`**: Intermediate state
|
227
|
+
- Example in chess: `R'` may represent a rook that has intermediate status (such as castling eligibility)
|
228
|
+
- Example in chess: `P'` may represent a pawn that may be captured _en passant_
|
228
229
|
|
229
230
|
These modifiers have no defined semantics in the FEEN specification itself but provide a flexible framework for representing piece-specific conditions while maintaining FEEN's rule-agnostic nature.
|
230
231
|
|
@@ -239,4 +240,4 @@ The [gem](https://rubygems.org/gems/feen) is available as open source under the
|
|
239
240
|
|
240
241
|
## About Sashité
|
241
242
|
|
242
|
-
This project is maintained by [Sashité](https://sashite.com/)
|
243
|
+
This project is maintained by [Sashité](https://sashite.com/) — promoting chess variants and sharing the beauty of Chinese, Japanese, and Western chess cultures.
|
@@ -7,7 +7,7 @@ module Feen
|
|
7
7
|
#
|
8
8
|
# @param piece_placement [Array] Hierarchical array representing the board where:
|
9
9
|
# - Empty squares are represented by empty strings ("")
|
10
|
-
# - Pieces are represented by strings (e.g., "r", "
|
10
|
+
# - Pieces are represented by strings (e.g., "r", "R'", "+P")
|
11
11
|
# - Dimensions are represented by nested arrays
|
12
12
|
# @return [String] FEEN piece placement string
|
13
13
|
# @raise [ArgumentError] If the piece placement structure is invalid
|
data/lib/feen/dumper.rb
CHANGED
@@ -23,19 +23,19 @@ module Feen
|
|
23
23
|
# @example Creating a FEEN string for chess initial position
|
24
24
|
# Feen::Dumper.dump(
|
25
25
|
# piece_placement: [
|
26
|
-
# ["r", "n", "b", "q", "k
|
26
|
+
# ["r'", "n", "b", "q", "k", "b", "n", "r'"],
|
27
27
|
# ["p", "p", "p", "p", "p", "p", "p", "p"],
|
28
28
|
# ["", "", "", "", "", "", "", ""],
|
29
29
|
# ["", "", "", "", "", "", "", ""],
|
30
30
|
# ["", "", "", "", "", "", "", ""],
|
31
31
|
# ["", "", "", "", "", "", "", ""],
|
32
32
|
# ["P", "P", "P", "P", "P", "P", "P", "P"],
|
33
|
-
# ["R", "N", "B", "Q", "K
|
33
|
+
# ["R'", "N", "B", "Q", "K", "B", "N", "R'"]
|
34
34
|
# ],
|
35
35
|
# pieces_in_hand: [],
|
36
36
|
# games_turn: ["CHESS", "chess"]
|
37
37
|
# )
|
38
|
-
# # => "
|
38
|
+
# # => "r'nbqkbnr'/pppppppp/8/8/8/8/PPPPPPPP/R'NBQKBNR' - CHESS/chess"
|
39
39
|
#
|
40
40
|
# @param piece_placement [Array] Board position data structure representing the spatial
|
41
41
|
# distribution of pieces across the board, where each cell
|
@@ -30,10 +30,8 @@ module Feen
|
|
30
30
|
VALID_PREFIXES = [PREFIX_PROMOTION, PREFIX_DIMINISHED].freeze
|
31
31
|
|
32
32
|
# Valid piece suffixes
|
33
|
-
|
34
|
-
|
35
|
-
SUFFIX_RIGHT = ">"
|
36
|
-
VALID_SUFFIXES = [SUFFIX_EQUALS, SUFFIX_LEFT, SUFFIX_RIGHT].freeze
|
33
|
+
SUFFIX_INTERMEDIATE = "'"
|
34
|
+
VALID_SUFFIXES = [SUFFIX_INTERMEDIATE].freeze
|
37
35
|
|
38
36
|
# Build validation pattern step by step to match BNF
|
39
37
|
# <letter> ::= <letter-lowercase> | <letter-uppercase>
|
@@ -42,8 +40,8 @@ module Feen
|
|
42
40
|
# <prefix> ::= "+" | "-"
|
43
41
|
PREFIX = "[+-]"
|
44
42
|
|
45
|
-
# <suffix> ::= "
|
46
|
-
SUFFIX = "[
|
43
|
+
# <suffix> ::= "'"
|
44
|
+
SUFFIX = "[']"
|
47
45
|
|
48
46
|
# <piece> ::= <letter> | <prefix> <letter> | <letter> <suffix> | <prefix> <letter> <suffix>
|
49
47
|
PIECE = "(?:#{PREFIX}?#{LETTER}#{SUFFIX}?)"
|
data/lib/feen/parser.rb
CHANGED
@@ -26,18 +26,18 @@ module Feen
|
|
26
26
|
# @raise [ArgumentError] If the FEEN string is invalid
|
27
27
|
#
|
28
28
|
# @example Parsing a standard chess initial position
|
29
|
-
# feen = "
|
29
|
+
# feen = "r'nbqkbnr'/pppppppp/8/8/8/8/PPPPPPPP/R'NBQKBNR' - CHESS/chess"
|
30
30
|
# result = Feen::Parser.parse(feen)
|
31
31
|
# # => {
|
32
32
|
# # piece_placement: [
|
33
|
-
# # ["r", "n", "b", "q", "k
|
33
|
+
# # ["r'", "n", "b", "q", "k", "b", "n", "r'"],
|
34
34
|
# # ["p", "p", "p", "p", "p", "p", "p", "p"],
|
35
35
|
# # ["", "", "", "", "", "", "", ""],
|
36
36
|
# # ["", "", "", "", "", "", "", ""],
|
37
37
|
# # ["", "", "", "", "", "", "", ""],
|
38
38
|
# # ["", "", "", "", "", "", "", ""],
|
39
39
|
# # ["P", "P", "P", "P", "P", "P", "P", "P"],
|
40
|
-
# # ["R", "N", "B", "Q", "K
|
40
|
+
# # ["R'", "N", "B", "Q", "K", "B", "N", "R'"]
|
41
41
|
# # ],
|
42
42
|
# # pieces_in_hand: [],
|
43
43
|
# # games_turn: ["CHESS", "chess"]
|
@@ -76,7 +76,7 @@ module Feen
|
|
76
76
|
# @return [Hash, nil] Hash containing the parsed position data or nil if parsing fails
|
77
77
|
#
|
78
78
|
# @example Parsing a valid FEEN string
|
79
|
-
# feen = "
|
79
|
+
# feen = "r'nbqkbnr'/pppppppp/8/8/8/8/PPPPPPPP/R'NBQKBNR' - CHESS/chess"
|
80
80
|
# result = Feen::Parser.safe_parse(feen)
|
81
81
|
# # => {piece_placement: [...], pieces_in_hand: [...], games_turn: [...]}
|
82
82
|
#
|
data/lib/feen.rb
CHANGED
@@ -23,21 +23,21 @@ module Feen
|
|
23
23
|
# @raise [ArgumentError] If any parameter is invalid
|
24
24
|
# @example
|
25
25
|
# piece_placement = [
|
26
|
-
# ["r", "n", "b", "q", "k
|
26
|
+
# ["r'", "n", "b", "q", "k", "b", "n", "r'"],
|
27
27
|
# ["p", "p", "p", "p", "p", "p", "p", "p"],
|
28
28
|
# ["", "", "", "", "", "", "", ""],
|
29
29
|
# ["", "", "", "", "", "", "", ""],
|
30
30
|
# ["", "", "", "", "", "", "", ""],
|
31
31
|
# ["", "", "", "", "", "", "", ""],
|
32
32
|
# ["P", "P", "P", "P", "P", "P", "P", "P"],
|
33
|
-
# ["R", "N", "B", "Q", "K
|
33
|
+
# ["R'", "N", "B", "Q", "K", "B", "N", "R'"]
|
34
34
|
# ]
|
35
35
|
# Feen.dump(
|
36
36
|
# piece_placement: piece_placement,
|
37
37
|
# pieces_in_hand: [],
|
38
38
|
# games_turn: ["CHESS", "chess"]
|
39
39
|
# )
|
40
|
-
# # => "
|
40
|
+
# # => "r'nbqkbnr'/pppppppp/8/8/8/8/PPPPPPPP/R'NBQKBNR' - CHESS/chess"
|
41
41
|
def self.dump(piece_placement:, pieces_in_hand:, games_turn:)
|
42
42
|
Dumper.dump(piece_placement:, pieces_in_hand:, games_turn:)
|
43
43
|
end
|
@@ -51,18 +51,18 @@ module Feen
|
|
51
51
|
# - :games_turn [Array<String>] - A two-element array with [active_variant, inactive_variant]
|
52
52
|
# @raise [ArgumentError] If the FEEN string is invalid
|
53
53
|
# @example
|
54
|
-
# feen_string = "
|
54
|
+
# feen_string = "r'nbqkbnr'/pppppppp/8/8/8/8/PPPPPPPP/R'NBQKBNR' - CHESS/chess"
|
55
55
|
# Feen.parse(feen_string)
|
56
56
|
# # => {
|
57
57
|
# # piece_placement: [
|
58
|
-
# # ["r", "n", "b", "q", "k
|
58
|
+
# # ["r'", "n", "b", "q", "k", "b", "n", "r'"],
|
59
59
|
# # ["p", "p", "p", "p", "p", "p", "p", "p"],
|
60
60
|
# # ["", "", "", "", "", "", "", ""],
|
61
61
|
# # ["", "", "", "", "", "", "", ""],
|
62
62
|
# # ["", "", "", "", "", "", "", ""],
|
63
63
|
# # ["", "", "", "", "", "", "", ""],
|
64
64
|
# # ["P", "P", "P", "P", "P", "P", "P", "P"],
|
65
|
-
# # ["R", "N", "B", "Q", "K
|
65
|
+
# # ["R'", "N", "B", "Q", "K", "B", "N", "R'"]
|
66
66
|
# # ],
|
67
67
|
# # pieces_in_hand: [],
|
68
68
|
# # games_turn: ["CHESS", "chess"]
|
@@ -80,7 +80,7 @@ module Feen
|
|
80
80
|
# @return [Hash, nil] Hash containing the parsed position data or nil if parsing fails
|
81
81
|
# @example
|
82
82
|
# # Valid FEEN string
|
83
|
-
# Feen.safe_parse("
|
83
|
+
# Feen.safe_parse("r'nbqkbnr'/pppppppp/8/8/8/8/PPPPPPPP/R'NBQKBNR' - CHESS/chess")
|
84
84
|
# # => {piece_placement: [...], pieces_in_hand: [...], games_turn: [...]}
|
85
85
|
#
|
86
86
|
# # Invalid FEEN string
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: feen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.0.0.
|
4
|
+
version: 5.0.0.beta5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cyril Kato
|
@@ -46,9 +46,7 @@ metadata:
|
|
46
46
|
homepage_uri: https://github.com/sashite/feen.rb
|
47
47
|
source_code_uri: https://github.com/sashite/feen.rb
|
48
48
|
specification_uri: https://sashite.dev/documents/feen/1.0.0/
|
49
|
-
funding_uri: https://github.com/sponsors/cyril
|
50
49
|
rubygems_mfa_required: 'true'
|
51
|
-
article_uri: https://blog.cyril.email/posts/2025-05-01/introducing-feen-notation.html
|
52
50
|
rdoc_options: []
|
53
51
|
require_paths:
|
54
52
|
- lib
|