sashite-pin 4.0.0 → 4.1.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.
- checksums.yaml +4 -4
- data/LICENSE +1 -1
- data/README.md +45 -38
- data/lib/sashite/pin/constants.rb +2 -2
- data/lib/sashite/pin/errors/argument/messages.rb +1 -1
- data/lib/sashite/pin/identifier.rb +48 -48
- data/lib/sashite/pin/parser.rb +6 -6
- data/lib/sashite/pin.rb +3 -3
- data/lib/sashite-pin.rb +0 -11
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e00046935bd19fe218aed6188bc310389c9a93e5e36aad8b91890d36046c4165
|
|
4
|
+
data.tar.gz: e90e9430c444175c658262c486eddfe3c09a6e5da2c88b0b1365fb91f14616a0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f716220c9d53ba9003712d64c3335f20b395c2174637b2432f43ca01c654dca7586b84f3057747126cf899003478afc071de31cd93e76ce0dc49dd7f7a9f99d5
|
|
7
|
+
data.tar.gz: 0aedbcd707733bbd81dd6fa48608a52f66016aec2e63162c5f841cbaf359959d3f902d8dddca2e08848d57dce1f3ccb31e3baeebb791812bea91c678a1f9fe0f
|
data/LICENSE
CHANGED
|
@@ -186,7 +186,7 @@
|
|
|
186
186
|
same "printed page" as the copyright notice for easier
|
|
187
187
|
identification within third-party archives.
|
|
188
188
|
|
|
189
|
-
Copyright 2025 Cyril Kato
|
|
189
|
+
Copyright 2025-2026 Cyril Kato
|
|
190
190
|
|
|
191
191
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
192
|
you may not use this file except in compliance with the License.
|
data/README.md
CHANGED
|
@@ -35,7 +35,7 @@ require "sashite/pin"
|
|
|
35
35
|
|
|
36
36
|
# Standard parsing (raises on error)
|
|
37
37
|
pin = Sashite::Pin.parse("K")
|
|
38
|
-
pin.
|
|
38
|
+
pin.abbr # => :K
|
|
39
39
|
pin.side # => :first
|
|
40
40
|
pin.state # => :normal
|
|
41
41
|
pin.terminal? # => false
|
|
@@ -84,23 +84,6 @@ Sashite::Pin.valid?("K^") # => true
|
|
|
84
84
|
Sashite::Pin.valid?("invalid") # => false
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
### Accessing Identifier Data
|
|
88
|
-
|
|
89
|
-
```ruby
|
|
90
|
-
pin = Sashite::Pin.parse("+K^")
|
|
91
|
-
|
|
92
|
-
# Get attributes
|
|
93
|
-
pin.type # => :K
|
|
94
|
-
pin.side # => :first
|
|
95
|
-
pin.state # => :enhanced
|
|
96
|
-
pin.terminal? # => true
|
|
97
|
-
|
|
98
|
-
# Get string components
|
|
99
|
-
pin.letter # => "K"
|
|
100
|
-
pin.prefix # => "+"
|
|
101
|
-
pin.suffix # => "^"
|
|
102
|
-
```
|
|
103
|
-
|
|
104
87
|
### Transformations
|
|
105
88
|
|
|
106
89
|
All transformations return new immutable instances.
|
|
@@ -117,12 +100,12 @@ pin.normalize.to_s # => "K"
|
|
|
117
100
|
pin.flip.to_s # => "k"
|
|
118
101
|
|
|
119
102
|
# Terminal transformations
|
|
120
|
-
pin.
|
|
121
|
-
pin.
|
|
103
|
+
pin.terminal.to_s # => "K^"
|
|
104
|
+
pin.non_terminal.to_s # => "K"
|
|
122
105
|
|
|
123
106
|
# Attribute changes
|
|
124
|
-
pin.
|
|
125
|
-
pin.with_side(:second).to_s
|
|
107
|
+
pin.with_abbr(:Q).to_s # => "Q"
|
|
108
|
+
pin.with_side(:second).to_s # => "k"
|
|
126
109
|
pin.with_state(:enhanced).to_s # => "+K"
|
|
127
110
|
pin.with_terminal(true).to_s # => "K^"
|
|
128
111
|
```
|
|
@@ -146,7 +129,7 @@ pin.terminal? # => true
|
|
|
146
129
|
|
|
147
130
|
# Comparison queries
|
|
148
131
|
other = Sashite::Pin.parse("k")
|
|
149
|
-
pin.
|
|
132
|
+
pin.same_abbr?(other) # => true
|
|
150
133
|
pin.same_side?(other) # => false
|
|
151
134
|
pin.same_state?(other) # => false
|
|
152
135
|
pin.same_terminal?(other) # => false
|
|
@@ -162,19 +145,19 @@ class Sashite::Pin::Identifier
|
|
|
162
145
|
# Creates an Identifier from attributes.
|
|
163
146
|
# Raises ArgumentError if attributes are invalid.
|
|
164
147
|
#
|
|
165
|
-
# @param
|
|
166
|
-
# @param side [Symbol]
|
|
148
|
+
# @param abbr [Symbol] Piece name abbreviation (:A to :Z)
|
|
149
|
+
# @param side [Symbol] Piece side (:first or :second)
|
|
167
150
|
# @param state [Symbol] Piece state (:normal, :enhanced, or :diminished)
|
|
168
151
|
# @param terminal [Boolean] Terminal status
|
|
169
152
|
# @return [Identifier]
|
|
170
|
-
def initialize(
|
|
153
|
+
def initialize(abbr, side, state = :normal, terminal: false)
|
|
171
154
|
|
|
172
|
-
# Returns the piece
|
|
155
|
+
# Returns the piece name abbreviation (always uppercase symbol).
|
|
173
156
|
#
|
|
174
157
|
# @return [Symbol]
|
|
175
|
-
def
|
|
158
|
+
def abbr
|
|
176
159
|
|
|
177
|
-
# Returns the
|
|
160
|
+
# Returns the piece side.
|
|
178
161
|
#
|
|
179
162
|
# @return [Symbol] :first or :second
|
|
180
163
|
def side
|
|
@@ -199,7 +182,7 @@ end
|
|
|
199
182
|
### Constants
|
|
200
183
|
|
|
201
184
|
```ruby
|
|
202
|
-
Sashite::Pin::Constants::
|
|
185
|
+
Sashite::Pin::Constants::VALID_ABBRS # => [:A, :B, ..., :Z]
|
|
203
186
|
Sashite::Pin::Constants::VALID_SIDES # => [:first, :second]
|
|
204
187
|
Sashite::Pin::Constants::VALID_STATES # => [:normal, :enhanced, :diminished]
|
|
205
188
|
Sashite::Pin::Constants::MAX_STRING_LENGTH # => 3
|
|
@@ -229,8 +212,10 @@ def Sashite::Pin.valid?(string)
|
|
|
229
212
|
|
|
230
213
|
### Transformations
|
|
231
214
|
|
|
215
|
+
All transformations return new `Sashite::Pin::Identifier` instances:
|
|
216
|
+
|
|
232
217
|
```ruby
|
|
233
|
-
# State transformations
|
|
218
|
+
# State transformations
|
|
234
219
|
def enhance # => Identifier with :enhanced state
|
|
235
220
|
def diminish # => Identifier with :diminished state
|
|
236
221
|
def normalize # => Identifier with :normal state
|
|
@@ -239,16 +224,38 @@ def normalize # => Identifier with :normal state
|
|
|
239
224
|
def flip # => Identifier with opposite side
|
|
240
225
|
|
|
241
226
|
# Terminal transformations
|
|
242
|
-
def
|
|
243
|
-
def
|
|
227
|
+
def terminal # => Identifier with terminal: true
|
|
228
|
+
def non_terminal # => Identifier with terminal: false
|
|
244
229
|
|
|
245
230
|
# Attribute changes
|
|
246
|
-
def
|
|
247
|
-
def with_side(new_side)
|
|
248
|
-
def with_state(new_state)
|
|
231
|
+
def with_abbr(new_abbr) # => Identifier with different abbreviation
|
|
232
|
+
def with_side(new_side) # => Identifier with different side
|
|
233
|
+
def with_state(new_state) # => Identifier with different state
|
|
249
234
|
def with_terminal(new_terminal) # => Identifier with specified terminal status
|
|
250
235
|
```
|
|
251
236
|
|
|
237
|
+
### Queries
|
|
238
|
+
|
|
239
|
+
```ruby
|
|
240
|
+
# State queries
|
|
241
|
+
def normal? # => Boolean
|
|
242
|
+
def enhanced? # => Boolean
|
|
243
|
+
def diminished? # => Boolean
|
|
244
|
+
|
|
245
|
+
# Side queries
|
|
246
|
+
def first_player? # => Boolean
|
|
247
|
+
def second_player? # => Boolean
|
|
248
|
+
|
|
249
|
+
# Terminal query
|
|
250
|
+
def terminal? # => Boolean
|
|
251
|
+
|
|
252
|
+
# Comparison queries
|
|
253
|
+
def same_abbr?(other) # => Boolean
|
|
254
|
+
def same_side?(other) # => Boolean
|
|
255
|
+
def same_state?(other) # => Boolean
|
|
256
|
+
def same_terminal?(other) # => Boolean
|
|
257
|
+
```
|
|
258
|
+
|
|
252
259
|
### Errors
|
|
253
260
|
|
|
254
261
|
All parsing and validation errors raise `ArgumentError` with descriptive messages:
|
|
@@ -263,11 +270,11 @@ All parsing and validation errors raise `ArgumentError` with descriptive message
|
|
|
263
270
|
|
|
264
271
|
## Design Principles
|
|
265
272
|
|
|
266
|
-
- **Bounded values**: Explicit validation of
|
|
273
|
+
- **Bounded values**: Explicit validation of abbreviations, sides, and states
|
|
267
274
|
- **Object-oriented**: `Identifier` class enables methods and encapsulation
|
|
268
275
|
- **Ruby idioms**: `valid?` predicate, `to_s` conversion, `ArgumentError` for invalid input
|
|
269
276
|
- **Immutable identifiers**: Frozen instances prevent mutation
|
|
270
|
-
- **Transformation methods**: Return new instances for
|
|
277
|
+
- **Transformation methods**: Return new instances for attribute changes
|
|
271
278
|
- **No dependencies**: Pure Ruby standard library only
|
|
272
279
|
|
|
273
280
|
## Related Specifications
|
|
@@ -6,8 +6,8 @@ module Sashite
|
|
|
6
6
|
#
|
|
7
7
|
# This module defines the valid values for PIN attributes.
|
|
8
8
|
module Constants
|
|
9
|
-
# Valid piece
|
|
10
|
-
|
|
9
|
+
# Valid piece name abbreviations (uppercase symbols A-Z).
|
|
10
|
+
VALID_ABBRS = %i[A B C D E F G H I J K L M N O P Q R S T U V W X Y Z].freeze
|
|
11
11
|
|
|
12
12
|
# Valid player sides.
|
|
13
13
|
VALID_SIDES = %i[first second].freeze
|
|
@@ -17,7 +17,7 @@ module Sashite
|
|
|
17
17
|
INVALID_TERMINAL_MARKER = "invalid terminal marker"
|
|
18
18
|
|
|
19
19
|
# Validation errors (constructor)
|
|
20
|
-
|
|
20
|
+
INVALID_ABBR = "abbr must be a symbol from :A to :Z"
|
|
21
21
|
INVALID_SIDE = "side must be :first or :second"
|
|
22
22
|
INVALID_STATE = "state must be :normal, :enhanced, or :diminished"
|
|
23
23
|
INVALID_TERMINAL = "terminal must be true or false"
|
|
@@ -8,8 +8,8 @@ module Sashite
|
|
|
8
8
|
# Represents a parsed PIN (Piece Identifier Notation) identifier.
|
|
9
9
|
#
|
|
10
10
|
# An Identifier encodes four attributes of a piece:
|
|
11
|
-
# -
|
|
12
|
-
# - Side: the
|
|
11
|
+
# - Abbr: the piece name abbreviation (A-Z as uppercase symbol)
|
|
12
|
+
# - Side: the piece side (:first or :second)
|
|
13
13
|
# - State: the piece state (:normal, :enhanced, or :diminished)
|
|
14
14
|
# - Terminal: whether the piece is terminal (true or false)
|
|
15
15
|
#
|
|
@@ -21,16 +21,16 @@ module Sashite
|
|
|
21
21
|
# pin = Identifier.new(:K, :first, :normal, terminal: true)
|
|
22
22
|
#
|
|
23
23
|
# @example String conversion
|
|
24
|
-
# Identifier.new(:K, :first).to_s
|
|
25
|
-
# Identifier.new(:R, :second, :enhanced).to_s
|
|
24
|
+
# Identifier.new(:K, :first).to_s # => "K"
|
|
25
|
+
# Identifier.new(:R, :second, :enhanced).to_s # => "+r"
|
|
26
26
|
# Identifier.new(:K, :first, :normal, terminal: true).to_s # => "K^"
|
|
27
27
|
#
|
|
28
28
|
# @see https://sashite.dev/specs/pin/1.0.0/
|
|
29
29
|
class Identifier
|
|
30
|
-
# @return [Symbol] Piece
|
|
31
|
-
attr_reader :
|
|
30
|
+
# @return [Symbol] Piece name abbreviation (:A to :Z, always uppercase)
|
|
31
|
+
attr_reader :abbr
|
|
32
32
|
|
|
33
|
-
# @return [Symbol]
|
|
33
|
+
# @return [Symbol] Piece side (:first or :second)
|
|
34
34
|
attr_reader :side
|
|
35
35
|
|
|
36
36
|
# @return [Symbol] Piece state (:normal, :enhanced, or :diminished)
|
|
@@ -38,8 +38,8 @@ module Sashite
|
|
|
38
38
|
|
|
39
39
|
# Creates a new Identifier instance.
|
|
40
40
|
#
|
|
41
|
-
# @param
|
|
42
|
-
# @param side [Symbol]
|
|
41
|
+
# @param abbr [Symbol] Piece name abbreviation (:A to :Z)
|
|
42
|
+
# @param side [Symbol] Piece side (:first or :second)
|
|
43
43
|
# @param state [Symbol] Piece state (:normal, :enhanced, or :diminished)
|
|
44
44
|
# @param terminal [Boolean] Terminal status
|
|
45
45
|
# @return [Identifier] A new frozen Identifier instance
|
|
@@ -49,13 +49,13 @@ module Sashite
|
|
|
49
49
|
# Identifier.new(:K, :first)
|
|
50
50
|
# Identifier.new(:R, :second, :enhanced)
|
|
51
51
|
# Identifier.new(:K, :first, :normal, terminal: true)
|
|
52
|
-
def initialize(
|
|
53
|
-
|
|
52
|
+
def initialize(abbr, side, state = :normal, terminal: false)
|
|
53
|
+
validate_abbr!(abbr)
|
|
54
54
|
validate_side!(side)
|
|
55
55
|
validate_state!(state)
|
|
56
56
|
validate_terminal!(terminal)
|
|
57
57
|
|
|
58
|
-
@
|
|
58
|
+
@abbr = abbr
|
|
59
59
|
@side = side
|
|
60
60
|
@state = state
|
|
61
61
|
@terminal = terminal
|
|
@@ -68,7 +68,7 @@ module Sashite
|
|
|
68
68
|
# @return [Boolean] true if terminal piece, false otherwise
|
|
69
69
|
#
|
|
70
70
|
# @example
|
|
71
|
-
# Identifier.new(:K, :first).terminal?
|
|
71
|
+
# Identifier.new(:K, :first).terminal? # => false
|
|
72
72
|
# Identifier.new(:K, :first, :normal, terminal: true).terminal? # => true
|
|
73
73
|
def terminal?
|
|
74
74
|
@terminal
|
|
@@ -83,8 +83,8 @@ module Sashite
|
|
|
83
83
|
# @return [String] The PIN string
|
|
84
84
|
#
|
|
85
85
|
# @example
|
|
86
|
-
# Identifier.new(:K, :first).to_s
|
|
87
|
-
# Identifier.new(:R, :second, :enhanced).to_s
|
|
86
|
+
# Identifier.new(:K, :first).to_s # => "K"
|
|
87
|
+
# Identifier.new(:R, :second, :enhanced).to_s # => "+r"
|
|
88
88
|
# Identifier.new(:K, :first, :normal, terminal: true).to_s # => "K^"
|
|
89
89
|
def to_s
|
|
90
90
|
"#{prefix}#{letter}#{suffix}"
|
|
@@ -99,8 +99,8 @@ module Sashite
|
|
|
99
99
|
# Identifier.new(:K, :second).letter # => "k"
|
|
100
100
|
def letter
|
|
101
101
|
case side
|
|
102
|
-
when :first then String(
|
|
103
|
-
when :second then String(
|
|
102
|
+
when :first then String(abbr.upcase)
|
|
103
|
+
when :second then String(abbr.downcase)
|
|
104
104
|
end
|
|
105
105
|
end
|
|
106
106
|
|
|
@@ -145,7 +145,7 @@ module Sashite
|
|
|
145
145
|
def enhance
|
|
146
146
|
return self if enhanced?
|
|
147
147
|
|
|
148
|
-
self.class.new(
|
|
148
|
+
self.class.new(abbr, side, :enhanced, terminal: terminal?)
|
|
149
149
|
end
|
|
150
150
|
|
|
151
151
|
# Returns a new Identifier with diminished state.
|
|
@@ -158,7 +158,7 @@ module Sashite
|
|
|
158
158
|
def diminish
|
|
159
159
|
return self if diminished?
|
|
160
160
|
|
|
161
|
-
self.class.new(
|
|
161
|
+
self.class.new(abbr, side, :diminished, terminal: terminal?)
|
|
162
162
|
end
|
|
163
163
|
|
|
164
164
|
# Returns a new Identifier with normal state.
|
|
@@ -171,7 +171,7 @@ module Sashite
|
|
|
171
171
|
def normalize
|
|
172
172
|
return self if normal?
|
|
173
173
|
|
|
174
|
-
self.class.new(
|
|
174
|
+
self.class.new(abbr, side, :normal, terminal: terminal?)
|
|
175
175
|
end
|
|
176
176
|
|
|
177
177
|
# ========================================================================
|
|
@@ -187,7 +187,7 @@ module Sashite
|
|
|
187
187
|
# pin.flip.to_s # => "k"
|
|
188
188
|
def flip
|
|
189
189
|
new_side = first_player? ? :second : :first
|
|
190
|
-
self.class.new(
|
|
190
|
+
self.class.new(abbr, new_side, state, terminal: terminal?)
|
|
191
191
|
end
|
|
192
192
|
|
|
193
193
|
# ========================================================================
|
|
@@ -200,11 +200,11 @@ module Sashite
|
|
|
200
200
|
#
|
|
201
201
|
# @example
|
|
202
202
|
# pin = Identifier.new(:K, :first)
|
|
203
|
-
# pin.
|
|
204
|
-
def
|
|
203
|
+
# pin.terminal.to_s # => "K^"
|
|
204
|
+
def terminal
|
|
205
205
|
return self if terminal?
|
|
206
206
|
|
|
207
|
-
self.class.new(
|
|
207
|
+
self.class.new(abbr, side, state, terminal: true)
|
|
208
208
|
end
|
|
209
209
|
|
|
210
210
|
# Returns a new Identifier unmarked as terminal.
|
|
@@ -213,30 +213,30 @@ module Sashite
|
|
|
213
213
|
#
|
|
214
214
|
# @example
|
|
215
215
|
# pin = Identifier.new(:K, :first, :normal, terminal: true)
|
|
216
|
-
# pin.
|
|
217
|
-
def
|
|
216
|
+
# pin.non_terminal.to_s # => "K"
|
|
217
|
+
def non_terminal
|
|
218
218
|
return self unless terminal?
|
|
219
219
|
|
|
220
|
-
self.class.new(
|
|
220
|
+
self.class.new(abbr, side, state, terminal: false)
|
|
221
221
|
end
|
|
222
222
|
|
|
223
223
|
# ========================================================================
|
|
224
224
|
# Attribute Transformations
|
|
225
225
|
# ========================================================================
|
|
226
226
|
|
|
227
|
-
# Returns a new Identifier with a different
|
|
227
|
+
# Returns a new Identifier with a different abbreviation.
|
|
228
228
|
#
|
|
229
|
-
# @param
|
|
230
|
-
# @return [Identifier] A new Identifier with the specified
|
|
231
|
-
# @raise [Errors::Argument] If the
|
|
229
|
+
# @param new_abbr [Symbol] The new piece name abbreviation (:A to :Z)
|
|
230
|
+
# @return [Identifier] A new Identifier with the specified abbreviation
|
|
231
|
+
# @raise [Errors::Argument] If the abbreviation is invalid
|
|
232
232
|
#
|
|
233
233
|
# @example
|
|
234
234
|
# pin = Identifier.new(:K, :first)
|
|
235
|
-
# pin.
|
|
236
|
-
def
|
|
237
|
-
return self if
|
|
235
|
+
# pin.with_abbr(:Q).to_s # => "Q"
|
|
236
|
+
def with_abbr(new_abbr)
|
|
237
|
+
return self if abbr.equal?(new_abbr)
|
|
238
238
|
|
|
239
|
-
self.class.new(
|
|
239
|
+
self.class.new(new_abbr, side, state, terminal: terminal?)
|
|
240
240
|
end
|
|
241
241
|
|
|
242
242
|
# Returns a new Identifier with a different side.
|
|
@@ -251,7 +251,7 @@ module Sashite
|
|
|
251
251
|
def with_side(new_side)
|
|
252
252
|
return self if side.equal?(new_side)
|
|
253
253
|
|
|
254
|
-
self.class.new(
|
|
254
|
+
self.class.new(abbr, new_side, state, terminal: terminal?)
|
|
255
255
|
end
|
|
256
256
|
|
|
257
257
|
# Returns a new Identifier with a different state.
|
|
@@ -266,7 +266,7 @@ module Sashite
|
|
|
266
266
|
def with_state(new_state)
|
|
267
267
|
return self if state.equal?(new_state)
|
|
268
268
|
|
|
269
|
-
self.class.new(
|
|
269
|
+
self.class.new(abbr, side, new_state, terminal: terminal?)
|
|
270
270
|
end
|
|
271
271
|
|
|
272
272
|
# Returns a new Identifier with a different terminal status.
|
|
@@ -281,7 +281,7 @@ module Sashite
|
|
|
281
281
|
def with_terminal(new_terminal)
|
|
282
282
|
return self if terminal?.equal?(new_terminal)
|
|
283
283
|
|
|
284
|
-
self.class.new(
|
|
284
|
+
self.class.new(abbr, side, state, terminal: new_terminal)
|
|
285
285
|
end
|
|
286
286
|
|
|
287
287
|
# ========================================================================
|
|
@@ -346,17 +346,17 @@ module Sashite
|
|
|
346
346
|
# Comparison Queries
|
|
347
347
|
# ========================================================================
|
|
348
348
|
|
|
349
|
-
# Checks if two Identifiers have the same
|
|
349
|
+
# Checks if two Identifiers have the same abbreviation.
|
|
350
350
|
#
|
|
351
351
|
# @param other [Identifier] The other Identifier to compare
|
|
352
|
-
# @return [Boolean] true if same
|
|
352
|
+
# @return [Boolean] true if same abbreviation
|
|
353
353
|
#
|
|
354
354
|
# @example
|
|
355
355
|
# pin1 = Identifier.new(:K, :first)
|
|
356
356
|
# pin2 = Identifier.new(:K, :second)
|
|
357
|
-
# pin1.
|
|
358
|
-
def
|
|
359
|
-
|
|
357
|
+
# pin1.same_abbr?(pin2) # => true
|
|
358
|
+
def same_abbr?(other)
|
|
359
|
+
abbr.equal?(other.abbr)
|
|
360
360
|
end
|
|
361
361
|
|
|
362
362
|
# Checks if two Identifiers have the same side.
|
|
@@ -409,7 +409,7 @@ module Sashite
|
|
|
409
409
|
def ==(other)
|
|
410
410
|
return false unless self.class === other
|
|
411
411
|
|
|
412
|
-
|
|
412
|
+
abbr.equal?(other.abbr) &&
|
|
413
413
|
side.equal?(other.side) &&
|
|
414
414
|
state.equal?(other.state) &&
|
|
415
415
|
terminal?.equal?(other.terminal?)
|
|
@@ -421,7 +421,7 @@ module Sashite
|
|
|
421
421
|
#
|
|
422
422
|
# @return [Integer] Hash code
|
|
423
423
|
def hash
|
|
424
|
-
[
|
|
424
|
+
[abbr, side, state, terminal?].hash
|
|
425
425
|
end
|
|
426
426
|
|
|
427
427
|
# Returns an inspect string for the Identifier.
|
|
@@ -437,10 +437,10 @@ module Sashite
|
|
|
437
437
|
# Private Validation
|
|
438
438
|
# ========================================================================
|
|
439
439
|
|
|
440
|
-
def
|
|
441
|
-
return if Constants::
|
|
440
|
+
def validate_abbr!(abbr)
|
|
441
|
+
return if Constants::VALID_ABBRS.include?(abbr)
|
|
442
442
|
|
|
443
|
-
raise Errors::Argument, Errors::Argument::Messages::
|
|
443
|
+
raise Errors::Argument, Errors::Argument::Messages::INVALID_ABBR
|
|
444
444
|
end
|
|
445
445
|
|
|
446
446
|
def validate_side!(side)
|
data/lib/sashite/pin/parser.rb
CHANGED
|
@@ -11,15 +11,15 @@ module Sashite
|
|
|
11
11
|
# to prevent ReDoS attacks and ensure strict ASCII compliance.
|
|
12
12
|
#
|
|
13
13
|
# @example
|
|
14
|
-
# Parser.parse("K") # => {
|
|
15
|
-
# Parser.parse("+r^") # => {
|
|
14
|
+
# Parser.parse("K") # => { abbr: :K, side: :first, state: :normal, terminal: false }
|
|
15
|
+
# Parser.parse("+r^") # => { abbr: :R, side: :second, state: :enhanced, terminal: true }
|
|
16
16
|
#
|
|
17
17
|
# @see https://sashite.dev/specs/pin/1.0.0/
|
|
18
18
|
module Parser
|
|
19
19
|
# Parses a PIN string into its components.
|
|
20
20
|
#
|
|
21
21
|
# @param input [String] The PIN string to parse
|
|
22
|
-
# @return [Hash] A hash with :
|
|
22
|
+
# @return [Hash] A hash with :abbr, :side, :state, and :terminal keys
|
|
23
23
|
# @raise [Errors::Argument] If the input is not a valid PIN string
|
|
24
24
|
def self.parse(input)
|
|
25
25
|
validate_input_type(input)
|
|
@@ -78,7 +78,7 @@ module Sashite
|
|
|
78
78
|
# Parses the PIN string into its components.
|
|
79
79
|
#
|
|
80
80
|
# @param input [String] The validated PIN string
|
|
81
|
-
# @return [Hash] A hash with :
|
|
81
|
+
# @return [Hash] A hash with :abbr, :side, :state, and :terminal keys
|
|
82
82
|
# @raise [Errors::Argument] If the structure is invalid
|
|
83
83
|
def parse_components(input)
|
|
84
84
|
pos = 0
|
|
@@ -98,7 +98,7 @@ module Sashite
|
|
|
98
98
|
byte = input.getbyte(pos)
|
|
99
99
|
raise Errors::Argument, Errors::Argument::Messages::MUST_CONTAIN_ONE_LETTER unless ascii_letter?(byte)
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
abbr = byte.chr.upcase.to_sym
|
|
102
102
|
side = uppercase_letter?(byte) ? :first : :second
|
|
103
103
|
pos += 1
|
|
104
104
|
|
|
@@ -114,7 +114,7 @@ module Sashite
|
|
|
114
114
|
# Ensure no extra characters
|
|
115
115
|
raise Errors::Argument, Errors::Argument::Messages::MUST_CONTAIN_ONE_LETTER if pos < input.bytesize
|
|
116
116
|
|
|
117
|
-
{
|
|
117
|
+
{ abbr: abbr, side: side, state: state, terminal: terminal }
|
|
118
118
|
end
|
|
119
119
|
|
|
120
120
|
# Checks if a byte is a state modifier (+ or -).
|
data/lib/sashite/pin.rb
CHANGED
|
@@ -16,7 +16,7 @@ module Sashite
|
|
|
16
16
|
#
|
|
17
17
|
# [<state-modifier>]<letter>[<terminal-marker>]
|
|
18
18
|
#
|
|
19
|
-
# - *Letter* (+A-Z+, +a-z+): Piece
|
|
19
|
+
# - *Letter* (+A-Z+, +a-z+): Piece name abbreviation and side
|
|
20
20
|
# - *State modifier*: <tt>+</tt> (enhanced), <tt>-</tt> (diminished), or none (normal)
|
|
21
21
|
# - *Terminal marker*: <tt>^</tt> (terminal piece) or none
|
|
22
22
|
#
|
|
@@ -32,7 +32,7 @@ module Sashite
|
|
|
32
32
|
# == Examples
|
|
33
33
|
#
|
|
34
34
|
# pin = Sashite::Pin.parse("K")
|
|
35
|
-
# pin.
|
|
35
|
+
# pin.abbr # => :K
|
|
36
36
|
# pin.side # => :first
|
|
37
37
|
# pin.state # => :normal
|
|
38
38
|
# pin.terminal? # => false
|
|
@@ -70,7 +70,7 @@ module Sashite
|
|
|
70
70
|
components = Parser.parse(string)
|
|
71
71
|
|
|
72
72
|
Identifier.new(
|
|
73
|
-
components[:
|
|
73
|
+
components[:abbr],
|
|
74
74
|
components[:side],
|
|
75
75
|
components[:state],
|
|
76
76
|
terminal: components[:terminal]
|
data/lib/sashite-pin.rb
CHANGED
|
@@ -1,14 +1,3 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "sashite/pin"
|
|
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
|