sashite-epin 1.1.0 → 2.0.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
@@ -5,65 +5,220 @@ require_relative "epin/identifier"
5
5
  module Sashite
6
6
  # EPIN (Extended Piece Identifier Notation) implementation for Ruby
7
7
  #
8
- # Provides style-aware ASCII-based format for representing pieces in abstract strategy board games.
9
- # EPIN extends PIN by adding derivation markers that distinguish pieces by their style origin,
10
- # enabling cross-style game scenarios and piece origin tracking.
11
- #
12
- # Format: [<state>]<letter>[<derivation>]
13
- # - State modifier: "+" (enhanced), "-" (diminished), or none (normal)
14
- # - Letter: A-Z (first player), a-z (second player)
15
- # - Derivation marker: "'" (foreign style), or none (native style)
16
- #
17
- # Examples:
18
- # "K" - First player king (native style, normal state)
19
- # "k'" - Second player king (foreign style, normal state)
20
- # "+R'" - First player rook (foreign style, enhanced state)
21
- # "-p" - Second player pawn (native style, diminished state)
22
- #
23
- # @see https://sashite.dev/specs/epin/1.0.0/
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)
73
+ #
74
+ # ### Backward Compatibility
75
+ #
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
80
+ #
81
+ # EPIN extends PIN by adding the optional derivation marker:
82
+ # - "K'" is valid EPIN (derived)
83
+ # - "+R^'" is valid EPIN (enhanced, terminal, derived)
84
+ #
85
+ # ## Examples
86
+ #
87
+ # ### Basic Usage
88
+ #
89
+ # # Parse EPIN strings
90
+ # native = Sashite::Epin.parse("K^") # Native king
91
+ # derived = Sashite::Epin.parse("K^'") # Derived king
92
+ #
93
+ # # Access attributes via PIN component
94
+ # native.pin.type # => :K
95
+ # native.pin.terminal? # => true
96
+ # native.derived? # => false
97
+ #
98
+ # # Create from PIN component
99
+ # pin = Sashite::Pin.parse("K^")
100
+ # epin = Sashite::Epin.new(pin, derived: false)
101
+ # epin.to_s # => "K^"
102
+ #
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)
24
147
  module Epin
25
148
  # Check if a string is a valid EPIN notation
26
149
  #
27
- # @param epin_string [String] The string to validate
150
+ # Validates both the EPIN format and the underlying PIN component.
151
+ #
152
+ # @param epin_string [String] the string to validate
28
153
  # @return [Boolean] true if valid EPIN, false otherwise
29
154
  #
30
- # @example
31
- # Sashite::Epin.valid?("K") # => true
32
- # Sashite::Epin.valid?("+R'") # => true
33
- # Sashite::Epin.valid?("-p") # => true
34
- # Sashite::Epin.valid?("KK") # => false
35
- # Sashite::Epin.valid?("++K") # => false
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)
36
162
  def self.valid?(epin_string)
37
163
  Identifier.valid?(epin_string)
38
164
  end
39
165
 
40
166
  # Parse an EPIN string into an Identifier object
41
167
  #
42
- # @param epin_string [String] EPIN notation string
43
- # @return [Epin::Identifier] new identifier instance
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.
171
+ #
172
+ # @param epin_string [String] EPIN notation string (format: <pin>['])
173
+ # @return [Epin::Identifier] parsed identifier with PIN component and derivation flag
44
174
  # @raise [ArgumentError] if the EPIN string is invalid
45
- # @example
46
- # Sashite::Epin.parse("K") # => #<Epin::Identifier type=:K side=:first state=:normal native=true>
47
- # Sashite::Epin.parse("+R'") # => #<Epin::Identifier type=:R side=:first state=:enhanced native=false>
48
- # Sashite::Epin.parse("-p") # => #<Epin::Identifier type=:P side=:second state=:diminished native=true>
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)
49
190
  def self.parse(epin_string)
50
191
  Identifier.parse(epin_string)
51
192
  end
52
193
 
53
- # Create a new identifier instance
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^'"
212
+ #
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)
54
217
  #
55
- # @param type [Symbol] piece type (:A to :Z)
56
- # @param side [Symbol] player side (:first or :second)
57
- # @param state [Symbol] piece state (:normal, :enhanced, or :diminished)
58
- # @param native [Boolean] style derivation (true for native, false for foreign)
59
- # @return [Epin::Identifier] new identifier instance
60
- # @raise [ArgumentError] if parameters are invalid
61
- # @example
62
- # Sashite::Epin.identifier(:K, :first, :normal, true) # => #<Epin::Identifier type=:K side=:first state=:normal native=true>
63
- # Sashite::Epin.identifier(:R, :first, :enhanced, false) # => #<Epin::Identifier type=:R side=:first state=:enhanced native=false>
64
- # Sashite::Epin.identifier(:P, :second, :diminished, true) # => #<Epin::Identifier type=:P side=:second state=:diminished native=true>
65
- def self.identifier(type, side, state, native)
66
- Identifier.new(type, side, state, native)
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)
67
222
  end
68
223
  end
69
224
  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: 1.1.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyril Kato
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: 3.1.0
18
+ version: 3.2.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.1.0
25
+ version: 3.2.0
26
26
  description: |
27
27
  EPIN (Extended Piece Identifier Notation) extends PIN to provide style-aware piece representation
28
28
  in abstract strategy board games. This gem implements the EPIN Specification v1.0.0 with
@@ -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.6.9
68
+ rubygems_version: 3.7.2
69
69
  specification_version: 4
70
70
  summary: EPIN (Extended Piece Identifier Notation) implementation for Ruby extending
71
71
  PIN with style derivation markers.