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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: de87bfc9b071e9f1ff14b6f7378ba0a6f9f78cdb9a9bfe32ca00f2494a1be2cd
4
- data.tar.gz: fd4d19e8a3b7fb845f081b127a859ff0791c8c11c05e1efb4846152044f67c18
3
+ metadata.gz: 28b60c3d60fd3241172fee9e143c53e6dca04082a2efe7f0fa7559b85dc6eac0
4
+ data.tar.gz: a15999144de7bcd5637b6efca4ed1c4a8ae7d3757d6d583123dfaf44468d6d35
5
5
  SHA512:
6
- metadata.gz: e9fe95297b8577a3bf171a865deca0dc9c2eed91a7d039c04f97077d53d3fa912ca553f28f448f0dd987087926d3ca5225624d7f2d8d08a9275a16a11efe26e0
7
- data.tar.gz: b82b7e00f5ece6f5e5859750226be3c74db23e694c8e53def8c8c656fe2fb3ecba7f0f4166cf3d9a4f6c7b4ab7af3cf6544da87a2684e438a389d96e04b3dc75
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.beta4"
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 = "rnbqk=bnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK=BNR - CHESS/chess"
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=", "b", "n", "r"],
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=", "B", "N", "R"]
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("rnbqk=bnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK=BNR - CHESS/chess")
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=", "b", "n", "r"],
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=", "B", "N", "R"]
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
- # => "rnbqk=bnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK=BNR - CHESS/chess"
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 = "rnbqk=bnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK=BNR - CHESS/chess"
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
- - The `=` suffixes on kings indicate castling rights on both sides (though FEEN doesn't define this semantics)
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 `+`**: May indicate promotion or special state
220
+ - **Prefix `+`**: Enhanced state
216
221
  - Example in shogi: `+P` may represent a promoted pawn
217
222
 
218
- - **Suffix `=`**: May indicate dual-option status
219
- - Example in chess: `K=` may represent a king eligible for both kingside and queenside castling
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 `>`**: May indicate right-side constraint
226
- - Example in chess: `K>` may represent a king eligible for kingside castling only
227
- - Example in chess: `P>` may represent a pawn that may be captured en passant from the right
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/) - a project dedicated to promoting chess variants and sharing the beauty of Chinese, Japanese, and Western chess cultures.
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", "K=", "+P")
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=", "b", "n", "r"],
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=", "B", "N", "R"]
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
- # # => "rnbqk=bnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK=BNR - CHESS/chess"
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
- SUFFIX_EQUALS = "="
34
- SUFFIX_LEFT = "<"
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 = "rnbqk=bnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK=BNR - CHESS/chess"
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=", "b", "n", "r"],
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=", "B", "N", "R"]
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 = "rnbqk=bnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK=BNR - CHESS/chess"
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=", "b", "n", "r"],
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=", "B", "N", "R"]
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
- # # => "rnbqk=bnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK=BNR - CHESS/chess"
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 = "rnbqk=bnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK=BNR - CHESS/chess"
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=", "b", "n", "r"],
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=", "B", "N", "R"]
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("rnbqk=bnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK=BNR - CHESS/chess")
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.beta4
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