sashite-epin 2.0.0 → 2.2.0

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.
data/lib/sashite/epin.rb CHANGED
@@ -1,224 +1,80 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "sashite/pin"
4
+
5
+ require_relative "epin/constants"
6
+ require_relative "epin/errors"
3
7
  require_relative "epin/identifier"
8
+ require_relative "epin/parser"
4
9
 
5
10
  module Sashite
6
- # EPIN (Extended Piece Identifier Notation) implementation for Ruby
7
- #
8
- # Extends PIN (Piece Identifier Notation) with a derivation marker to track piece style
9
- # in cross-style games. EPIN is simply: PIN + optional style derivation marker (').
10
- #
11
- # ## Core Concept
12
- #
13
- # EPIN addresses the need to distinguish between:
14
- # - **Native pieces**: Using their own side's native style (no marker)
15
- # - **Derived pieces**: Using the opponent's native style (marked with ')
16
- #
17
- # This distinction is essential for cross-style games where different players use
18
- # different game traditions (e.g., Chess vs Makruk, Chess vs Shogi).
19
- #
20
- # ## Pure Composition
21
- #
22
- # EPIN doesn't reimplement PIN - it's pure composition:
23
- #
24
- # EPIN = PIN + derived flag
25
- #
26
- # All piece attributes (name, side, state, terminal) come from the PIN component.
27
- # EPIN adds only the 5th attribute: piece style (native vs derived).
28
- #
29
- # ## Minimal API
30
- #
31
- # Module-level methods (3 total):
32
- # 1. valid?(epin_string) - validate EPIN string
33
- # 2. parse(epin_string) - parse into Identifier
34
- # 3. new(pin, derived: false) - create from PIN component
35
- #
36
- # ## Five Fundamental Attributes
37
- #
38
- # EPIN represents all five piece attributes from the Sashité Game Protocol:
39
- #
40
- # From PIN component (4 attributes):
41
- # - **Piece Name**: epin.pin.type
42
- # - **Piece Side**: epin.pin.side
43
- # - **Piece State**: epin.pin.state
44
- # - **Terminal Status**: epin.pin.terminal?
45
- #
46
- # From EPIN (5th attribute):
47
- # - **Piece Style**: epin.derived? (native vs derived)
48
- #
49
- # ## Format Structure
50
- #
51
- # Structure: `<pin>[']`
52
- #
53
- # Grammar (BNF):
54
- # <epin> ::= <pin> | <pin> "'"
55
- # <pin> ::= ["+" | "-"] <letter> ["^"]
56
- # <letter> ::= "A" | ... | "Z" | "a" | ... | "z"
57
- #
58
- # Regular Expression: `/\A[-+]?[A-Za-z]\^?'?\z/`
59
- #
60
- # ## Semantics
61
- #
62
- # ### Native vs Derived
63
- #
64
- # In cross-style games (e.g., Chess vs Makruk):
65
- # - First player's native style: Chess
66
- # - Second player's native style: Makruk
67
- #
68
- # Then:
69
- # - "K" = First player king in Chess style (native)
70
- # - "K'" = First player king in Makruk style (derived from opponent)
71
- # - "k" = Second player king in Makruk style (native)
72
- # - "k'" = Second player king in Chess style (derived from opponent)
11
+ # EPIN (Extended Piece Identifier Notation) implementation for Ruby.
73
12
  #
74
- # ### Backward Compatibility
13
+ # EPIN extends PIN with an optional derivation marker (') that flags
14
+ # whether a piece uses a native or derived style.
75
15
  #
76
- # Every valid PIN token is a valid EPIN token:
77
- # - "K" is valid PIN and valid EPIN (native)
78
- # - "+R^" is valid PIN and valid EPIN (native)
79
- # - All PIN semantics preserved
16
+ # == Format
80
17
  #
81
- # EPIN extends PIN by adding the optional derivation marker:
82
- # - "K'" is valid EPIN (derived)
83
- # - "+R^'" is valid EPIN (enhanced, terminal, derived)
18
+ # <pin>[']
84
19
  #
85
- # ## Examples
20
+ # - *PIN*: Any valid PIN token (abbr, side, state, terminal)
21
+ # - *Derivation marker*: <tt>'</tt> (derived) or absent (native)
86
22
  #
87
- # ### Basic Usage
23
+ # == Examples
88
24
  #
89
- # # Parse EPIN strings
90
- # native = Sashite::Epin.parse("K^") # Native king
91
- # derived = Sashite::Epin.parse("K^'") # Derived king
25
+ # epin = Sashite::Epin.parse("K^'")
26
+ # epin.pin.abbr # => :K
27
+ # epin.pin.side # => :first
28
+ # epin.pin.terminal? # => true
29
+ # epin.derived? # => true
92
30
  #
93
- # # Access attributes via PIN component
94
- # native.pin.type # => :K
95
- # native.pin.terminal? # => true
96
- # native.derived? # => false
31
+ # epin = Sashite::Epin.parse("+R")
32
+ # epin.to_s # => "+R"
97
33
  #
98
- # # Create from PIN component
99
- # pin = Sashite::Pin.parse("K^")
100
- # epin = Sashite::Epin.new(pin, derived: false)
101
- # epin.to_s # => "K^"
34
+ # Sashite::Epin.valid?("K^'") # => true
35
+ # Sashite::Epin.valid?("invalid") # => false
102
36
  #
103
- # ### Transformations
104
- #
105
- # epin = Sashite::Epin.parse("K^")
106
- #
107
- # # Mark as derived
108
- # derived = epin.mark_derived
109
- # derived.to_s # => "K^'"
110
- #
111
- # # Transform PIN component
112
- # queen = epin.with_pin(epin.pin.with_type(:Q))
113
- # queen.to_s # => "Q^"
114
- #
115
- # # Transform both
116
- # derived_queen = epin
117
- # .with_pin(epin.pin.with_type(:Q))
118
- # .mark_derived
119
- # derived_queen.to_s # => "Q^'"
120
- #
121
- # ### Cross-Style Games
122
- #
123
- # # Chess vs Makruk match
124
- # # First player = Chess, Second player = Makruk
125
- #
126
- # chess_king = Sashite::Epin.parse("K^") # Native Chess king
127
- # makruk_pawn = Sashite::Epin.parse("P'") # Derived Makruk pawn
128
- #
129
- # chess_king.native? # => true (uses Chess style)
130
- # makruk_pawn.derived? # => true (uses Makruk style)
131
- #
132
- # ## Design Properties
133
- #
134
- # - **Rule-agnostic**: Independent of game mechanics
135
- # - **Pure composition**: Extends PIN minimally (PIN + derived flag)
136
- # - **Minimal API**: Only 3 module methods, 6 instance methods
137
- # - **Component transparency**: Direct PIN access via epin.pin
138
- # - **Backward compatible**: All PIN tokens are valid EPIN tokens
139
- # - **Immutable**: All instances frozen, transformations return new objects
140
- # - **Type-safe**: Full PIN type preservation
141
- # - **Style-aware**: Tracks native vs derived pieces
142
- # - **Compact**: Single character overhead for style information
143
- #
144
- # @see https://sashite.dev/specs/epin/1.0.0/ EPIN Specification v1.0.0
145
- # @see https://sashite.dev/specs/epin/1.0.0/examples/ EPIN Examples
146
- # @see https://sashite.dev/specs/pin/1.0.0/ PIN Specification (base component)
37
+ # @see https://sashite.dev/specs/epin/1.0.0/
147
38
  module Epin
148
- # Check if a string is a valid EPIN notation
39
+ # Parses an EPIN string into an Identifier.
149
40
  #
150
- # Validates both the EPIN format and the underlying PIN component.
41
+ # @param string [String] The EPIN string to parse
42
+ # @return [Identifier] A new Identifier instance
43
+ # @raise [Errors::Argument] If the string is not a valid EPIN
151
44
  #
152
- # @param epin_string [String] the string to validate
153
- # @return [Boolean] true if valid EPIN, false otherwise
45
+ # @example
46
+ # Sashite::Epin.parse("K")
47
+ # # => #<Sashite::Epin::Identifier K>
154
48
  #
155
- # @example Validate EPIN strings
156
- # Sashite::Epin.valid?("K^") # => true (valid PIN, native)
157
- # Sashite::Epin.valid?("K^'") # => true (valid PIN with derivation)
158
- # Sashite::Epin.valid?("+R'") # => true (enhanced derived rook)
159
- # Sashite::Epin.valid?("K^''") # => false (multiple markers)
160
- # Sashite::Epin.valid?("KK'") # => false (invalid PIN part)
161
- # Sashite::Epin.valid?("invalid") # => false (invalid format)
162
- def self.valid?(epin_string)
163
- Identifier.valid?(epin_string)
164
- end
165
-
166
- # Parse an EPIN string into an Identifier object
167
- #
168
- # Creates a new EPIN identifier by parsing the string, extracting the PIN part
169
- # and derivation marker, validating the PIN component, and creating an identifier
170
- # with the appropriate derivation status.
49
+ # Sashite::Epin.parse("K^'")
50
+ # # => #<Sashite::Epin::Identifier K^'>
171
51
  #
172
- # @param epin_string [String] EPIN notation string (format: <pin>['])
173
- # @return [Epin::Identifier] parsed identifier with PIN component and derivation flag
174
- # @raise [ArgumentError] if the EPIN string is invalid
175
- #
176
- # @example Parse different EPIN formats
177
- # Sashite::Epin.parse("K^") # => Native king, terminal
178
- # Sashite::Epin.parse("K^'") # => Derived king, terminal
179
- # Sashite::Epin.parse("+R") # => Native rook, enhanced
180
- # Sashite::Epin.parse("+R'") # => Derived rook, enhanced
181
- # Sashite::Epin.parse("-p") # => Native pawn, diminished
182
- #
183
- # @example Access all five attributes
184
- # epin = Sashite::Epin.parse("+R^'")
185
- # epin.pin.type # => :R (Piece Name)
186
- # epin.pin.side # => :first (Piece Side)
187
- # epin.pin.state # => :enhanced (Piece State)
188
- # epin.pin.terminal? # => true (Terminal Status)
189
- # epin.derived? # => true (Piece Style)
190
- def self.parse(epin_string)
191
- Identifier.parse(epin_string)
52
+ # Sashite::Epin.parse("invalid")
53
+ # # => raises Errors::Argument
54
+ def self.parse(string)
55
+ components = Parser.parse(string)
56
+
57
+ pin = ::Sashite::Pin::Identifier.new(
58
+ components[:pin][:abbr],
59
+ components[:pin][:side],
60
+ components[:pin][:state],
61
+ terminal: components[:pin][:terminal]
62
+ )
63
+
64
+ Identifier.new(pin, derived: components[:derived])
192
65
  end
193
66
 
194
- # Create a new identifier from a PIN component and derivation flag
195
- #
196
- # Constructs an EPIN identifier by combining a PIN component (which provides
197
- # the four base attributes: name, side, state, terminal) with a derivation flag
198
- # (which provides the fifth attribute: style).
199
- #
200
- # @param pin [Pin::Identifier] PIN component providing base attributes
201
- # @param derived [Boolean] whether the piece uses derived style (default: false)
202
- # @return [Epin::Identifier] new immutable identifier instance
203
- # @raise [ArgumentError] if pin is not a Pin::Identifier
204
- #
205
- # @example Create identifiers from PIN components
206
- # pin = Sashite::Pin.parse("K^")
207
- # native = Sashite::Epin.new(pin, derived: false)
208
- # native.to_s # => "K^"
209
- #
210
- # derived = Sashite::Epin.new(pin, derived: true)
211
- # derived.to_s # => "K^'"
67
+ # Checks if a string is a valid EPIN notation.
212
68
  #
213
- # @example Cross-style game setup
214
- # # First player uses Chess style, second uses Makruk style
215
- # chess_king = Sashite::Epin.new(Sashite::Pin.parse("K^"), derived: false)
216
- # makruk_pawn = Sashite::Epin.new(Sashite::Pin.parse("P"), derived: true)
69
+ # @param string [String] The string to validate
70
+ # @return [Boolean] true if valid, false otherwise
217
71
  #
218
- # chess_king.native? # => true (uses own Chess style)
219
- # makruk_pawn.derived? # => true (uses opponent's Makruk style)
220
- def self.new(pin, derived: false)
221
- Identifier.new(pin, derived: derived)
72
+ # @example
73
+ # Sashite::Epin.valid?("K") # => true
74
+ # Sashite::Epin.valid?("K^'") # => true
75
+ # Sashite::Epin.valid?("invalid") # => false
76
+ def self.valid?(string)
77
+ Parser.valid?(string)
222
78
  end
223
79
  end
224
80
  end
data/lib/sashite-epin.rb CHANGED
@@ -1,14 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "sashite/epin"
4
-
5
- # Sashité namespace for board game notation libraries
6
- #
7
- # Sashité provides a collection of libraries for representing and manipulating
8
- # board game concepts according to the Game Protocol specifications.
9
- #
10
- # @see https://sashite.dev/game-protocol/ Game Protocol Foundation
11
- # @see https://sashite.dev/specs/ Sashité Specifications
12
- # @author Sashité
13
- module Sashite
14
- end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sashite-epin
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyril Kato
@@ -15,32 +15,32 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: 3.2.0
18
+ version: 4.1.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: 3.2.0
25
+ version: 4.1.0
26
26
  description: |
27
- EPIN (Extended Piece Identifier Notation) extends PIN to provide style-aware piece representation
28
- in abstract strategy board games. This gem implements the EPIN Specification v1.0.0 with
29
- a modern Ruby interface featuring immutable identifier objects and functional programming
30
- principles. EPIN adds derivation markers to PIN that distinguish pieces by their style
31
- origin, enabling cross-style game scenarios and piece origin tracking. Represents all
32
- four Game Protocol piece attributes with full PIN backward compatibility. Perfect for
33
- game engines, cross-tradition tournaments, and hybrid board game environments.
27
+ EPIN (Extended Piece Identifier Notation) implementation for Ruby.
28
+ Extends PIN by adding a derivation marker to track piece style in cross-style
29
+ abstract strategy board games with a minimal compositional API.
34
30
  email: contact@cyril.email
35
31
  executables: []
36
32
  extensions: []
37
33
  extra_rdoc_files: []
38
34
  files:
39
- - LICENSE.md
40
35
  - README.md
41
36
  - lib/sashite-epin.rb
42
37
  - lib/sashite/epin.rb
38
+ - lib/sashite/epin/constants.rb
39
+ - lib/sashite/epin/errors.rb
40
+ - lib/sashite/epin/errors/argument.rb
41
+ - lib/sashite/epin/errors/argument/messages.rb
43
42
  - lib/sashite/epin/identifier.rb
43
+ - lib/sashite/epin/parser.rb
44
44
  homepage: https://github.com/sashite/epin.rb
45
45
  licenses:
46
46
  - MIT
@@ -65,7 +65,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
65
65
  - !ruby/object:Gem::Version
66
66
  version: '0'
67
67
  requirements: []
68
- rubygems_version: 3.7.2
68
+ rubygems_version: 4.0.3
69
69
  specification_version: 4
70
70
  summary: EPIN (Extended Piece Identifier Notation) implementation for Ruby extending
71
71
  PIN with style derivation markers.
data/LICENSE.md DELETED
@@ -1,21 +0,0 @@
1
- # The MIT License
2
-
3
- Copyright (c) 2025 Sashité
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.