sashite-pin 2.0.0 → 2.0.2

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: a140d90c9c73aeb4773d2e94c0825cdddfe6ce5dff722325eb58bbea896ba62e
4
- data.tar.gz: 273fbe7adfa54613f4d582feb053690724d686f2f1d966d0f6871d33e77450b3
3
+ metadata.gz: 70bbc211cb1dd38efa9f0c39b2d0e1b0fd09c2dcf2d7ac3773719ce11786a5ac
4
+ data.tar.gz: cd6831f22ee3e9fd8ce559f5535bbcbb1d9ca13ba9083a9d9c1fe66d41e2eef9
5
5
  SHA512:
6
- metadata.gz: dcdd60202c06776b8243d1330c54f93d6a38d919f9d5201660287967c987c69a119c9f1884a0c8bf3f3099d14b715b3c2d656c8725b39092df352f6ea9d5a2d8
7
- data.tar.gz: 4e87b25c1180e9cebcf47756cce651022d5c5b3227200010f2de408744910f20f596ee80bd34d6bf0ee597982189c92c22aed88907de30fea4c6c2455ac92072
6
+ metadata.gz: d0808aac8a46423256ec3546a4eb71d5882d3535b4a74929680cd31f84294ffd406716565f53ef072ff35c5c1131bb2612ab93398001d3d80772bb34a3dc3d07
7
+ data.tar.gz: ba7ecb1765c03c801f014bdbb9792fcae5dacd8d119890b0c73f0e550a345a8fc307cb39a82b803c986ef7fa50f76ad5fff39c6a7bffc5fe39e1b06bebe6b809
data/README.md CHANGED
@@ -204,15 +204,32 @@ crossed_soldier.to_s # => "+P"
204
204
  #### Creation and Parsing
205
205
  - `Sashite::Pin::Piece.new(type, side, state = :normal)` - Create piece instance
206
206
  - `Sashite::Pin::Piece.parse(pin_string)` - Parse PIN string (same as module method)
207
+ - `Sashite::Pin::Piece.valid?(pin_string)` - Validate PIN string (class method)
207
208
 
208
209
  #### Attribute Access
209
- - `#type` - Get piece type (symbol :A to :Z)
210
+ - `#type` - Get piece type (symbol :A to :Z, always uppercase)
210
211
  - `#side` - Get player side (:first or :second)
211
212
  - `#state` - Get state (:normal, :enhanced, or :diminished)
212
- - `#letter` - Get letter representation (string)
213
+ - `#letter` - Get letter representation (string, case determined by side)
213
214
  - `#prefix` - Get state prefix (string: "+", "-", or "")
214
215
  - `#to_s` - Convert to PIN string representation
215
216
 
217
+ #### Type and Case Handling
218
+
219
+ **Important**: The `type` attribute is always stored as an uppercase symbol (`:A` to `:Z`), regardless of the input case when parsing. The display case in `#letter` and `#to_s` is determined by the `side` attribute:
220
+
221
+ ```ruby
222
+ # Both create the same internal type representation
223
+ piece1 = Sashite::Pin.parse("K") # type: :K, side: :first
224
+ piece2 = Sashite::Pin.parse("k") # type: :K, side: :second
225
+
226
+ piece1.type # => :K (uppercase symbol)
227
+ piece2.type # => :K (same uppercase symbol)
228
+
229
+ piece1.letter # => "K" (uppercase display)
230
+ piece2.letter # => "k" (lowercase display)
231
+ ```
232
+
216
233
  #### State Queries
217
234
  - `#normal?` - Check if normal state (no modifiers)
218
235
  - `#enhanced?` - Check if enhanced state
@@ -242,10 +259,34 @@ crossed_soldier.to_s # => "+P"
242
259
  - `#==(other)` - Full equality comparison
243
260
 
244
261
  ### Constants
245
- - `Sashite::Pin::PIN_REGEX` - Regular expression for PIN validation
262
+ - `Sashite::Pin::Piece::PIN_PATTERN` - Regular expression for PIN validation (internal use)
246
263
 
247
264
  ## Advanced Usage
248
265
 
266
+ ### Type Normalization Examples
267
+
268
+ ```ruby
269
+ # Parsing different cases results in same type
270
+ white_king = Sashite::Pin.parse("K")
271
+ black_king = Sashite::Pin.parse("k")
272
+
273
+ # Types are normalized to uppercase
274
+ white_king.type # => :K
275
+ black_king.type # => :K (same type!)
276
+
277
+ # Sides are different
278
+ white_king.side # => :first
279
+ black_king.side # => :second
280
+
281
+ # Display follows side convention
282
+ white_king.letter # => "K"
283
+ black_king.letter # => "k"
284
+
285
+ # Same type, different sides
286
+ white_king.same_type?(black_king) # => true
287
+ white_king.same_side?(black_king) # => false
288
+ ```
289
+
249
290
  ### Immutable Transformations
250
291
  ```ruby
251
292
  # All transformations return new instances
@@ -356,11 +397,15 @@ puts can_promote?(promoted_pawn, 8) # => false (already promoted)
356
397
 
357
398
  Following the [Game Protocol](https://sashite.dev/game-protocol/):
358
399
 
359
- | Protocol Attribute | PIN Encoding | Examples |
360
- |-------------------|--------------|----------|
361
- | **Type** | Symbol choice | `:K`/`:k` = King, `:P`/`:p` = Pawn |
362
- | **Side** | Symbol value | `:first` = First player, `:second` = Second player |
363
- | **State** | Symbol value | `:enhanced` = Enhanced, `:diminished` = Diminished, `:normal` = Normal |
400
+ | Protocol Attribute | PIN Encoding | Examples | Notes |
401
+ |-------------------|--------------|----------|-------|
402
+ | **Type** | ASCII letter choice | `K`/`k` = King, `P`/`p` = Pawn | Type is always stored as uppercase symbol (`:K`, `:P`) |
403
+ | **Side** | Letter case in display | `K` = First player, `k` = Second player | Case is determined by side during rendering |
404
+ | **State** | Optional prefix | `+K` = Enhanced, `-K` = Diminished, `K` = Normal | |
405
+
406
+ **Type Convention**: All piece types are internally represented as uppercase symbols (`:A` to `:Z`). The display case is determined by the `side` attribute: first player pieces display as uppercase, second player pieces as lowercase.
407
+
408
+ **Canonical principle**: Identical pieces must have identical PIN representations.
364
409
 
365
410
  **Note**: PIN does not represent the **Style** attribute from the Game Protocol. For style-aware piece notation, see [Piece Name Notation (PNN)](https://sashite.dev/specs/pnn/).
366
411
 
@@ -370,10 +415,41 @@ Following the [Game Protocol](https://sashite.dev/game-protocol/):
370
415
  * **Rule-Agnostic**: Independent of specific game mechanics
371
416
  * **Compact Format**: 1-2 characters per piece
372
417
  * **Visual Distinction**: Clear player differentiation through case
418
+ * **Type Normalization**: Consistent uppercase type representation internally
373
419
  * **Protocol Compliant**: Direct implementation of Sashité piece attributes
374
420
  * **Immutable**: All piece instances are frozen and transformations return new objects
375
421
  * **Functional**: Pure functions with no side effects
376
422
 
423
+ ## Implementation Notes
424
+
425
+ ### Type Normalization Convention
426
+
427
+ PIN follows a strict type normalization convention:
428
+
429
+ 1. **Internal Storage**: All piece types are stored as uppercase symbols (`:A` to `:Z`)
430
+ 2. **Input Flexibility**: Both `"K"` and `"k"` are valid input during parsing
431
+ 3. **Case Semantics**: Input case determines the `side` attribute, not the `type`
432
+ 4. **Display Logic**: Output case is computed from `side` during rendering
433
+
434
+ This design ensures:
435
+ - Consistent internal representation regardless of input format
436
+ - Clear separation between piece identity (type) and ownership (side)
437
+ - Predictable behavior when comparing pieces of the same type
438
+
439
+ ### Example Flow
440
+
441
+ ```ruby
442
+ # Input: "k" (lowercase)
443
+ # ↓ Parsing
444
+ # type: :K (normalized to uppercase)
445
+ # side: :second (inferred from lowercase input)
446
+ # ↓ Display
447
+ # letter: "k" (computed from type + side)
448
+ # PIN: "k" (final representation)
449
+ ```
450
+
451
+ This ensures that `parse(pin).to_s == pin` for all valid PIN strings while maintaining internal consistency.
452
+
377
453
  ## System Constraints
378
454
 
379
455
  - **Maximum 26 piece types** per game system (one per ASCII letter)
@@ -106,6 +106,23 @@ module Sashite
106
106
  new(piece_type, piece_side, piece_state)
107
107
  end
108
108
 
109
+ # Check if a string is a valid PIN notation
110
+ #
111
+ # @param pin_string [String] The string to validate
112
+ # @return [Boolean] true if valid PIN, false otherwise
113
+ #
114
+ # @example
115
+ # Sashite::Pin::Piece.valid?("K") # => true
116
+ # Sashite::Pin::Piece.valid?("+R") # => true
117
+ # Sashite::Pin::Piece.valid?("-p") # => true
118
+ # Sashite::Pin::Piece.valid?("KK") # => false
119
+ # Sashite::Pin::Piece.valid?("++K") # => false
120
+ def self.valid?(pin_string)
121
+ return false unless pin_string.is_a?(::String)
122
+
123
+ pin_string.match?(PIN_PATTERN)
124
+ end
125
+
109
126
  # Convert the piece to its PIN string representation
110
127
  #
111
128
  # @return [String] PIN notation string
@@ -190,14 +207,13 @@ module Sashite
190
207
  self.class.new(type, side, NORMAL_STATE)
191
208
  end
192
209
 
193
- # Create a new piece with opposite ownership (case)
210
+ # Create a new piece with opposite side
194
211
  #
195
- # @return [Piece] new piece instance with flipped case
212
+ # @return [Piece] new piece instance with opposite side
196
213
  # @example
197
214
  # piece.flip # (:K, :first, :normal) => (:K, :second, :normal)
198
215
  def flip
199
- new_side = first_player? ? SECOND_PLAYER : FIRST_PLAYER
200
- self.class.new(type, new_side, state)
216
+ self.class.new(type, opposite_side, state)
201
217
  end
202
218
 
203
219
  # Create a new piece with a different type (keeping same side and state)
@@ -369,6 +385,15 @@ module Sashite
369
385
  end
370
386
 
371
387
  private_class_method :match_pattern
388
+
389
+ private
390
+
391
+ # Get the opposite side of the current piece
392
+ #
393
+ # @return [Symbol] :first if current side is :second, :second if current side is :first
394
+ def opposite_side
395
+ first_player? ? SECOND_PLAYER : FIRST_PLAYER
396
+ end
372
397
  end
373
398
  end
374
399
  end
data/lib/sashite/pin.rb CHANGED
@@ -20,13 +20,9 @@ module Sashite
20
20
  #
21
21
  # See: https://sashite.dev/specs/pin/1.0.0/
22
22
  module Pin
23
- # Regular expression for PIN validation
24
- # Matches: optional state modifier followed by a single letter
25
- PIN_REGEX = /\A[-+]?[A-Za-z]\z/
26
-
27
23
  # Check if a string is a valid PIN notation
28
24
  #
29
- # @param pin [String] The string to validate
25
+ # @param pin_string [String] The string to validate
30
26
  # @return [Boolean] true if valid PIN, false otherwise
31
27
  #
32
28
  # @example
@@ -35,10 +31,8 @@ module Sashite
35
31
  # Sashite::Pin.valid?("-p") # => true
36
32
  # Sashite::Pin.valid?("KK") # => false
37
33
  # Sashite::Pin.valid?("++K") # => false
38
- def self.valid?(pin)
39
- return false unless pin.is_a?(::String)
40
-
41
- pin.match?(PIN_REGEX)
34
+ def self.valid?(pin_string)
35
+ Piece.valid?(pin_string)
42
36
  end
43
37
 
44
38
  # Parse a PIN string into a Piece object
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sashite-pin
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyril Kato
@@ -15,7 +15,8 @@ description: |
15
15
  a modern Ruby interface featuring immutable piece objects and functional programming
16
16
  principles. PIN translates piece attributes from the Game Protocol into a compact,
17
17
  portable notation system using ASCII letters with optional state modifiers and
18
- case-based player group classification.
18
+ case-based side encoding. Perfect for game engines, board game notation systems,
19
+ and multi-game environments.
19
20
  email: contact@cyril.email
20
21
  executables: []
21
22
  extensions: []