sashite-pin 3.2.0 → 3.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5f278c657907ec277b42f6c5f7b22b5df56d6cb57bd6438eeda33c3c847294f9
4
- data.tar.gz: 96e8c58b731ea334214aa5803e27a7fdd629f17df38b1eb55ddf8654397bd5f5
3
+ metadata.gz: 629bf8b689b39a81e6b52ca9b1386f53c6c807f5f57ae608fb99449ceb08854e
4
+ data.tar.gz: '0883b5de633e958ddbf72908ffd49832e999e0a1f99b615ce8aa5fa1222cd4f5'
5
5
  SHA512:
6
- metadata.gz: d451a383989a72da646522113f4ece370b88d57edc940c932cc238311dde1d3f52e477858a1dabba6ff7ee2b9d1a2ee561c9827230ecc3986c421ded53814634
7
- data.tar.gz: 74cf7b9dd82202b8999951d42ae44bac1cd84cc286819268732070cc8b0bde769984de64a374bcd72ab1e5eaa803dd84e452e85f657163d5bd8d8512c978318b
6
+ metadata.gz: a64ac32e1afb6b84c388823f81022efe6eda1a469ce82920b4d8119f0e747e4f5a8788c92d397a48165b36fcfda45111df785dad8523f3e861e31acb55e6fd68
7
+ data.tar.gz: 99a1e0e87304f2b1f525483cf952a4a0d9fdf0768297010894e6d429f4431b7c5476f287f94e3e85dd9c4c46669546ed094c2afc0fdd6296c9bccebcd8aa255b
data/README.md CHANGED
@@ -1,17 +1,17 @@
1
- # Pin.rb
1
+ # Sashite::Pin
2
2
 
3
3
  [![Version](https://img.shields.io/github/v/tag/sashite/pin.rb?label=Version&logo=github)](https://github.com/sashite/pin.rb/tags)
4
4
  [![Yard documentation](https://img.shields.io/badge/Yard-documentation-blue.svg?logo=github)](https://rubydoc.info/github/sashite/pin.rb/main)
5
5
  ![Ruby](https://github.com/sashite/pin.rb/actions/workflows/main.yml/badge.svg?branch=main)
6
6
  [![License](https://img.shields.io/github/license/sashite/pin.rb?label=License&logo=github)](https://github.com/sashite/pin.rb/raw/main/LICENSE.md)
7
7
 
8
- > **PIN** (Piece Identifier Notation) implementation for the Ruby language.
8
+ > **PIN** (Piece Identifier Notation) implementation for Ruby.
9
9
 
10
10
  ## What is PIN?
11
11
 
12
12
  PIN (Piece Identifier Notation) provides an ASCII-based format for representing pieces in abstract strategy board games. PIN translates piece attributes from the [Game Protocol](https://sashite.dev/game-protocol/) into a compact, portable notation system.
13
13
 
14
- This gem implements the [PIN Specification v1.0.0](https://sashite.dev/specs/pin/1.0.0/), providing a modern Ruby interface with immutable identifier objects and functional programming principles.
14
+ This gem implements the [PIN Specification v1.0.0](https://sashite.dev/specs/pin/1.0.0/).
15
15
 
16
16
  ## Installation
17
17
 
@@ -31,81 +31,74 @@ gem install sashite-pin
31
31
  ```ruby
32
32
  require "sashite/pin"
33
33
 
34
- # Parse PIN strings into identifier objects
35
- identifier = Sashite::Pin.parse("K") # => #<Pin::Identifier type=:K side=:first state=:normal terminal=false>
36
- identifier.to_s # => "K"
37
- identifier.type # => :K
38
- identifier.side # => :first
39
- identifier.state # => :normal
40
- identifier.terminal? # => false
34
+ # Parse PIN strings
35
+ pin = Sashite::Pin.parse("K")
36
+ pin.type # => :K
37
+ pin.side # => :first
38
+ pin.state # => :normal
39
+ pin.terminal # => false
41
40
 
42
- # Parse terminal pieces (e.g., kings in chess)
43
- terminal_king = Sashite::Pin.parse("K^") # => #<Pin::Identifier type=:K side=:first state=:normal terminal=true>
44
- terminal_king.to_s # => "K^"
45
- terminal_king.terminal? # => true
41
+ pin.to_s # => "K"
42
+
43
+ # Parse with different attributes
44
+ king = Sashite::Pin.parse("K^") # Terminal king
45
+ rook = Sashite::Pin.parse("+R") # Enhanced rook
46
+ pawn = Sashite::Pin.parse("-p") # Diminished second player pawn
46
47
 
47
48
  # Create identifiers directly
48
- identifier = Sashite::Pin.identifier(:K, :first, :normal) # => #<Pin::Identifier type=:K side=:first state=:normal>
49
- terminal_king = Sashite::Pin.identifier(:K, :first, :normal, terminal: true) # => #<Pin::Identifier type=:K side=:first state=:normal terminal=true>
50
- identifier = Sashite::Pin::Identifier.new(:R, :second, :enhanced) # => #<Pin::Identifier type=:R side=:second state=:enhanced>
51
-
52
- # Validate PIN strings
53
- Sashite::Pin.valid?("K") # => true
54
- Sashite::Pin.valid?("+R") # => true
55
- Sashite::Pin.valid?("K^") # => true
56
- Sashite::Pin.valid?("+K^") # => true
57
- Sashite::Pin.valid?("invalid") # => false
58
-
59
- # State manipulation (returns new immutable instances)
60
- enhanced = identifier.enhance # => #<Pin::Identifier type=:K side=:first state=:enhanced>
61
- enhanced.to_s # => "+K"
62
- diminished = identifier.diminish # => #<Pin::Identifier type=:K side=:first state=:diminished>
63
- diminished.to_s # => "-K"
64
-
65
- # Terminal marker manipulation
66
- normal_king = Sashite::Pin.parse("K")
67
- terminal_king = normal_king.mark_terminal # => "K^"
68
- back_to_normal = terminal_king.unmark_terminal # => "K"
69
-
70
- # Side manipulation
71
- flipped = identifier.flip # => #<Pin::Identifier type=:K side=:second state=:normal>
72
- flipped.to_s # => "k"
73
-
74
- # Type manipulation
75
- queen = identifier.with_type(:Q) # => #<Pin::Identifier type=:Q side=:first state=:normal>
76
- queen.to_s # => "Q"
49
+ pin = Sashite::Pin.new(:K, :first)
50
+ pin = Sashite::Pin.new(:R, :second, :enhanced)
51
+ pin = Sashite::Pin.new(:K, :first, :normal, terminal: true)
52
+
53
+ # Validation
54
+ Sashite::Pin.valid?("K") # => true
55
+ Sashite::Pin.valid?("+R") # => true
56
+ Sashite::Pin.valid?("K^") # => true
57
+ Sashite::Pin.valid?("invalid") # => false
58
+
59
+ # State transformations (return new instances)
60
+ enhanced = pin.enhance
61
+ enhanced.to_s # => "+K"
62
+
63
+ diminished = pin.diminish
64
+ diminished.to_s # => "-K"
65
+
66
+ normalized = enhanced.normalize
67
+ normalized.to_s # => "K"
68
+
69
+ # Side transformation
70
+ flipped = pin.flip
71
+ flipped.to_s # => "k"
72
+
73
+ # Terminal transformations
74
+ terminal = pin.mark_terminal
75
+ terminal.to_s # => "K^"
76
+
77
+ non_terminal = terminal.unmark_terminal
78
+ non_terminal.to_s # => "K"
79
+
80
+ # Type transformation
81
+ queen = pin.with_type(:Q)
82
+ queen.to_s # => "Q"
77
83
 
78
84
  # State queries
79
- identifier.normal? # => true
80
- enhanced.enhanced? # => true
81
- diminished.diminished? # => true
85
+ pin.normal? # => true
86
+ enhanced.enhanced? # => true
87
+ diminished.diminished? # => true
82
88
 
83
89
  # Side queries
84
- identifier.first_player? # => true
85
- flipped.second_player? # => true
90
+ pin.first_player? # => true
91
+ flipped.second_player? # => true
86
92
 
87
- # Attribute access
88
- identifier.letter # => "K"
89
- enhanced.prefix # => "+"
90
- identifier.prefix # => ""
91
- terminal_king.suffix # => "^"
93
+ # Terminal queries
94
+ terminal.terminal? # => true
92
95
 
93
- # Type and side comparison
96
+ # Comparison
94
97
  king1 = Sashite::Pin.parse("K")
95
98
  king2 = Sashite::Pin.parse("k")
96
- queen = Sashite::Pin.parse("Q")
97
-
98
- king1.same_type?(king2) # => true (both kings)
99
- king1.same_side?(queen) # => true (both first player)
100
- king1.same_type?(queen) # => false (different types)
101
-
102
- # Functional transformations can be chained
103
- pawn = Sashite::Pin.parse("P")
104
- enemy_promoted = pawn.flip.enhance # => "+p" (second player promoted pawn)
105
99
 
106
- # Transformations preserve terminal status
107
- terminal_piece = Sashite::Pin.parse("K^")
108
- enhanced_terminal = terminal_piece.enhance # => "+K^"
100
+ king1.same_type?(king2) # => true
101
+ king1.same_side?(king2) # => false
109
102
  ```
110
103
 
111
104
  ## Format Specification
@@ -118,387 +111,125 @@ enhanced_terminal = terminal_piece.enhance # => "+K^"
118
111
 
119
112
  ### Components
120
113
 
121
- * **Letter** (`A-Z`, `a-z`): Represents piece type and side
114
+ | Component | Values | Description |
115
+ |-----------|--------|-------------|
116
+ | Letter | `A-Z`, `a-z` | Piece type and side |
117
+ | State Modifier | `+`, `-`, (none) | Enhanced, diminished, or normal |
118
+ | Terminal Marker | `^`, (none) | Terminal piece or not |
122
119
 
123
- * Uppercase: First player pieces
124
- * Lowercase: Second player pieces
125
- * **State Modifier** (optional prefix):
120
+ ### Side Convention
126
121
 
127
- * `+`: Enhanced state (promoted, upgraded, empowered)
128
- * `-`: Diminished state (weakened, restricted, temporary)
129
- * No prefix: Normal state
130
- * **Terminal Marker** (optional suffix):
131
-
132
- * `^`: Terminal piece (critical to match continuation)
133
- * No suffix: Non-terminal piece
134
-
135
- ### Regular Expression
136
-
137
- ```ruby
138
- /\A[-+]?[A-Za-z]\^?\z/
139
- ```
122
+ - **Uppercase** (`A-Z`): First player
123
+ - **Lowercase** (`a-z`): Second player
140
124
 
141
125
  ### Examples
142
126
 
143
- * `K` - First player king (normal state)
144
- * `K^` - First player king (normal state, terminal)
145
- * `k` - Second player king (normal state)
146
- * `k^` - Second player king (normal state, terminal)
147
- * `+R` - First player rook (enhanced state)
148
- * `+R^` - First player rook (enhanced state, terminal)
149
- * `-p` - Second player pawn (diminished state)
127
+ | PIN | Side | State | Terminal | Description |
128
+ |-----|------|-------|----------|-------------|
129
+ | `K` | First | Normal | No | Standard king |
130
+ | `K^` | First | Normal | Yes | Terminal king |
131
+ | `+R` | First | Enhanced | No | Promoted rook |
132
+ | `-p` | Second | Diminished | No | Weakened pawn |
133
+ | `+K^` | First | Enhanced | Yes | Enhanced terminal king |
150
134
 
151
135
  ## API Reference
152
136
 
153
- ### Main Module Methods
154
-
155
- * `Sashite::Pin.valid?(pin_string)` - Check if string is valid PIN notation
156
- * `Sashite::Pin.parse(pin_string)` - Parse PIN string into Identifier object
157
- * `Sashite::Pin.identifier(type, side, state = :normal, terminal: false)` - Create identifier instance directly
158
-
159
- ### Identifier Class
160
-
161
- #### Creation and Parsing
162
-
163
- * `Sashite::Pin::Identifier.new(type, side, state = :normal, terminal: false)` - Create identifier instance
164
- * `Sashite::Pin::Identifier.parse(pin_string)` - Parse PIN string (same as module method)
165
- * `Sashite::Pin::Identifier.valid?(pin_string)` - Validate PIN string (class method)
166
-
167
- #### Attribute Access
168
-
169
- * `#type` - Get piece type (symbol \:A to \:Z, always uppercase)
170
- * `#side` - Get player side (\:first or \:second)
171
- * `#state` - Get state (\:normal, \:enhanced, or \:diminished)
172
- * `#terminal` - Get terminal status (Boolean)
173
- * `#letter` - Get letter representation (string, case determined by side)
174
- * `#prefix` - Get state prefix (string: "+", "-", or "")
175
- * `#suffix` - Get terminal marker (string: "^" or "")
176
- * `#to_s` - Convert to PIN string representation
177
-
178
- #### Type and Case Handling
179
-
180
- **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:
137
+ ### Parsing and Validation
181
138
 
182
139
  ```ruby
183
- # Both create the same internal type representation
184
- identifier1 = Sashite::Pin.parse("K") # type: :K, side: :first
185
- identifier2 = Sashite::Pin.parse("k") # type: :K, side: :second
186
-
187
- identifier1.type # => :K (uppercase symbol)
188
- identifier2.type # => :K (same uppercase symbol)
189
-
190
- identifier1.letter # => "K" (uppercase display)
191
- identifier2.letter # => "k" (lowercase display)
140
+ Sashite::Pin.parse(pin_string) # => Sashite::Pin | raises ArgumentError
141
+ Sashite::Pin.valid?(pin_string) # => boolean
192
142
  ```
193
143
 
194
- #### State Queries
195
-
196
- * `#normal?` - Check if normal state (no modifiers)
197
- * `#enhanced?` - Check if enhanced state
198
- * `#diminished?` - Check if diminished state
199
-
200
- #### Side Queries
201
-
202
- * `#first_player?` - Check if first player identifier
203
- * `#second_player?` - Check if second player identifier
204
-
205
- #### Terminal Queries
206
-
207
- * `#terminal?` - Check if terminal piece
208
-
209
- #### State Transformations (immutable - return new instances)
210
-
211
- * `#enhance` - Create enhanced version
212
- * `#unenhance` - Remove enhanced state
213
- * `#diminish` - Create diminished version
214
- * `#undiminish` - Remove diminished state
215
- * `#normalize` - Remove all state modifiers
216
- * `#flip` - Switch player (change side)
217
-
218
- #### Attribute Transformations (immutable - return new instances)
219
-
220
- * `#with_type(new_type)` - Create identifier with different type
221
- * `#with_side(new_side)` - Create identifier with different side
222
- * `#with_state(new_state)` - Create identifier with different state
223
-
224
- #### Terminal Transformations (immutable - return new instances)
225
-
226
- * `#mark_terminal` - Create terminal version
227
- * `#unmark_terminal` - Create non-terminal version
228
- * `#with_terminal(boolean)` - Create identifier with specified terminal status
229
-
230
- #### Comparison Methods
231
-
232
- * `#same_type?(other)` - Check if same piece type
233
- * `#same_side?(other)` - Check if same side
234
- * `#same_state?(other)` - Check if same state
235
- * `#same_terminal?(other)` - Check if same terminal status
236
- * `#==(other)` - Full equality comparison
237
-
238
- ### Constants
239
-
240
- * `Sashite::Pin::Identifier::PIN_PATTERN` - Regular expression for PIN validation (internal use)
241
- * `Sashite::Pin::Identifier::TERMINAL_MARKER` - Terminal marker character (`"^"`)
242
-
243
- ## Advanced Usage
244
-
245
- ### Type Normalization Examples
144
+ ### Creation
246
145
 
247
146
  ```ruby
248
- # Parsing different cases results in same type
249
- white_king = Sashite::Pin.parse("K")
250
- black_king = Sashite::Pin.parse("k")
147
+ Sashite::Pin.new(type, side)
148
+ Sashite::Pin.new(type, side, state)
149
+ Sashite::Pin.new(type, side, state, terminal: boolean)
150
+ ```
251
151
 
252
- # Types are normalized to uppercase
253
- white_king.type # => :K
254
- black_king.type # => :K (same type!)
152
+ ### Conversion
255
153
 
256
- # Sides are different
257
- white_king.side # => :first
258
- black_king.side # => :second
154
+ ```ruby
155
+ pin.to_s # => String
156
+ pin.letter # => String (case determined by side)
157
+ pin.prefix # => String ("+" | "-" | "")
158
+ pin.suffix # => String ("^" | "")
159
+ ```
259
160
 
260
- # Display follows side convention
261
- white_king.letter # => "K"
262
- black_king.letter # => "k"
161
+ ### Transformations
263
162
 
264
- # Same type, different sides
265
- white_king.same_type?(black_king) # => true
266
- white_king.same_side?(black_king) # => false
267
- ```
163
+ All transformations return new `Sashite::Pin` instances:
268
164
 
269
- ### Immutable Transformations
270
165
  ```ruby
271
- # All transformations return new instances
272
- original = Sashite::Pin.identifier(:K, :first, :normal)
273
- enhanced = original.enhance
274
- diminished = original.diminish
275
-
276
- # Original piece is never modified
277
- puts original.to_s # => "K"
278
- puts enhanced.to_s # => "+K"
279
- puts diminished.to_s # => "-K"
280
-
281
- # Transformations can be chained
282
- result = original.flip.enhance.with_type(:Q)
283
- puts result.to_s # => "+q"
284
-
285
- # Terminal status is preserved through transformations
286
- terminal_king = Sashite::Pin.parse("K^")
287
- enhanced_terminal = terminal_king.enhance
288
- puts enhanced_terminal.to_s # => "+K^"
166
+ # State
167
+ pin.enhance # => Sashite::Pin with :enhanced state
168
+ pin.diminish # => Sashite::Pin with :diminished state
169
+ pin.normalize # => Sashite::Pin with :normal state
170
+
171
+ # Side
172
+ pin.flip # => Sashite::Pin with opposite side
173
+
174
+ # Terminal
175
+ pin.mark_terminal # => Sashite::Pin with terminal: true
176
+ pin.unmark_terminal # => Sashite::Pin with terminal: false
177
+
178
+ # Attribute changes
179
+ pin.with_type(new_type) # => Sashite::Pin with different type
180
+ pin.with_side(new_side) # => Sashite::Pin with different side
181
+ pin.with_state(new_state) # => Sashite::Pin with different state
182
+ pin.with_terminal(boolean) # => Sashite::Pin with specified terminal status
289
183
  ```
290
184
 
291
- ### Game State Management
292
- ```ruby
293
- class GameBoard
294
- def initialize
295
- @pieces = {}
296
- end
297
-
298
- def place(square, piece)
299
- @pieces[square] = piece
300
- end
301
-
302
- def promote(square, new_type = :Q)
303
- piece = @pieces[square]
304
- return nil unless piece&.normal? # Can only promote normal pieces
305
-
306
- @pieces[square] = piece.with_type(new_type).enhance
307
- end
308
-
309
- def capture(from_square, to_square)
310
- captured = @pieces[to_square]
311
- @pieces[to_square] = @pieces.delete(from_square)
312
- captured
313
- end
314
-
315
- def pieces_by_side(side)
316
- @pieces.select { |_, piece| piece.side == side }
317
- end
318
-
319
- def promoted_pieces
320
- @pieces.select { |_, piece| piece.enhanced? }
321
- end
322
-
323
- def terminal_pieces
324
- @pieces.select { |_, piece| piece.terminal? }
325
- end
326
- end
327
-
328
- # Usage
329
- board = GameBoard.new
330
- board.place("e1", Sashite::Pin.identifier(:K, :first, :normal, terminal: true))
331
- board.place("e8", Sashite::Pin.identifier(:K, :second, :normal, terminal: true))
332
- board.place("a7", Sashite::Pin.identifier(:P, :first, :normal))
333
-
334
- # Promote pawn
335
- board.promote("a7", :Q)
336
- promoted = board.promoted_pieces
337
- puts promoted.values.first.to_s # => "+Q"
338
- ```
185
+ ### Queries
339
186
 
340
- ### Piece Analysis
341
187
  ```ruby
342
- def analyze_pieces(pins)
343
- pieces = pins.map { |pin| Sashite::Pin.parse(pin) }
344
-
345
- {
346
- total: pieces.size,
347
- by_side: pieces.group_by(&:side),
348
- by_type: pieces.group_by(&:type),
349
- by_state: pieces.group_by(&:state),
350
- promoted: pieces.count(&:enhanced?),
351
- weakened: pieces.count(&:diminished?),
352
- terminal: pieces.count(&:terminal?)
353
- }
354
- end
355
-
356
- pins = %w[K^ Q +R B N P k^ q r +b n -p]
357
- analysis = analyze_pieces(pins)
358
- puts analysis[:by_side][:first].size # => 6
359
- puts analysis[:promoted] # => 2
360
- puts analysis[:terminal] # => 2
188
+ # State
189
+ pin.normal?
190
+ pin.enhanced?
191
+ pin.diminished?
192
+
193
+ # Side
194
+ pin.first_player?
195
+ pin.second_player?
196
+
197
+ # Terminal
198
+ pin.terminal?
199
+
200
+ # Comparison
201
+ pin.same_type?(other)
202
+ pin.same_side?(other)
203
+ pin.same_state?(other)
204
+ pin.same_terminal?(other)
361
205
  ```
362
206
 
363
- ### Move Validation Example
207
+ ## Data Structure
208
+
364
209
  ```ruby
365
- def can_promote?(piece, target_rank)
366
- return false unless piece.normal? # Already promoted pieces can't promote again
367
-
368
- case piece.type
369
- when :P # Pawn
370
- (piece.first_player? && target_rank == 8) ||
371
- (piece.second_player? && target_rank == 1)
372
- when :R, :B, :S, :N, :L # Shōgi pieces that can promote
373
- true
374
- else
375
- false
376
- end
377
- end
378
-
379
- pawn = Sashite::Pin.identifier(:P, :first, :normal)
380
- puts can_promote?(pawn, 8) # => true
381
-
382
- promoted_pawn = pawn.enhance
383
- puts can_promote?(promoted_pawn, 8) # => false (already promoted)
210
+ Sashite::Pin
211
+ #type => :A..:Z # Piece type (always uppercase symbol)
212
+ #side => :first | :second # Player side
213
+ #state => :normal | :enhanced | :diminished
214
+ #terminal => true | false
384
215
  ```
385
216
 
386
217
  ## Protocol Mapping
387
218
 
388
219
  Following the [Game Protocol](https://sashite.dev/game-protocol/):
389
220
 
390
- | Protocol Attribute | PIN Encoding | Examples | Notes |
391
- |-------------------|--------------|----------|-------|
392
- | **Piece Name** | ASCII letter choice | `K`/`k` = King, `P`/`p` = Pawn | Type is always stored as uppercase symbol (`:K`, `:P`) |
393
- | **Piece Side** | Letter case in display | `K` = First player, `k` = Second player | Case is determined by side during rendering |
394
- | **Piece State** | Optional prefix | `+K` = Enhanced, `-K` = Diminished, `K` = Normal | |
395
- | **Terminal Status** | Optional suffix | `K^` = Terminal, `K` = Non-terminal | Identifies pieces critical to match continuation |
396
-
397
- **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.
398
-
399
- **Canonical principle**: Identical pieces must have identical PIN representations.
400
-
401
- **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/).
402
-
403
- ## Properties
404
-
405
- * **ASCII Compatible**: Maximum portability across systems
406
- * **Rule-Agnostic**: Independent of specific game mechanics
407
- * **Compact Format**: 1-3 characters per piece
408
- * **Visual Distinction**: Clear player differentiation through case
409
- * **Type Normalization**: Consistent uppercase type representation internally
410
- * **Terminal Marker**: Explicit identification of pieces critical to match continuation
411
- * **Protocol Compliant**: Direct implementation of Sashité piece attributes
412
- * **Immutable**: All piece instances are frozen and transformations return new objects
413
- * **Functional**: Pure functions with no side effects
414
-
415
- ## Implementation Notes
416
-
417
- ### Type Normalization Convention
418
-
419
- PIN follows a strict type normalization convention:
420
-
421
- 1. **Internal Storage**: All piece types are stored as uppercase symbols (`:A` to `:Z`)
422
- 2. **Input Flexibility**: Both `"K"` and `"k"` are valid input during parsing
423
- 3. **Case Semantics**: Input case determines the `side` attribute, not the `type`
424
- 4. **Display Logic**: Output case is computed from `side` during rendering
425
-
426
- This design ensures:
427
- - Consistent internal representation regardless of input format
428
- - Clear separation between piece identity (type) and ownership (side)
429
- - Predictable behavior when comparing pieces of the same type
430
-
431
- ### Terminal Marker Convention
432
-
433
- The terminal marker (`^`) identifies pieces whose presence, condition, or capacity for action determines whether the match can continue:
434
-
435
- 1. **Suffix Position**: Always appears as the last character (`K^`, `+K^`, `-k^`)
436
- 2. **Preservation**: Terminal status is preserved through all transformations
437
- 3. **Equality**: Two pieces are equal only if they have the same terminal status
438
- 4. **Independence**: Terminal status is independent of state (normal/enhanced/diminished)
439
-
440
- ### Example Flow
441
-
442
- ```ruby
443
- # Input: "k" (lowercase)
444
- # ↓ Parsing
445
- # type: :K (normalized to uppercase)
446
- # side: :second (inferred from lowercase input)
447
- # ↓ Display
448
- # letter: "k" (computed from type + side)
449
- # PIN: "k" (final representation)
450
- ```
451
-
452
- This ensures that `parse(pin).to_s == pin` for all valid PIN strings while maintaining internal consistency.
453
-
454
- ## System Constraints
455
-
456
- - **Maximum 26 piece types** per game system (one per ASCII letter)
457
- - **Exactly 2 players** (uppercase/lowercase distinction)
458
- - **3 state levels** (enhanced, normal, diminished)
459
- - **2 terminal levels** (terminal, non-terminal)
221
+ | Protocol Attribute | PIN Encoding |
222
+ |-------------------|--------------|
223
+ | Piece Name | ASCII letter choice |
224
+ | Piece Side | Letter case |
225
+ | Piece State | Optional prefix (`+`/`-`) |
226
+ | Terminal Status | Optional suffix (`^`) |
460
227
 
461
228
  ## Related Specifications
462
229
 
463
- - [Game Protocol](https://sashite.dev/game-protocol/) - Conceptual foundation for abstract strategy board games
464
- - [PNN](https://sashite.dev/specs/pnn/) - Piece Name Notation (style-aware piece representation)
465
- - [CELL](https://sashite.dev/specs/cell/) - Board position coordinates
466
- - [HAND](https://sashite.dev/specs/hand/) - Reserve location notation
467
- - [PMN](https://sashite.dev/specs/pmn/) - Portable Move Notation
468
-
469
- ## Documentation
470
-
471
- - [Official PIN Specification v1.0.0](https://sashite.dev/specs/pin/1.0.0/)
472
- - [PIN Examples Documentation](https://sashite.dev/specs/pin/1.0.0/examples/)
473
- - [Game Protocol Foundation](https://sashite.dev/game-protocol/)
474
- - [API Documentation](https://rubydoc.info/github/sashite/pin.rb/main)
475
-
476
- ## Development
477
-
478
- ```sh
479
- # Clone the repository
480
- git clone https://github.com/sashite/pin.rb.git
481
- cd pin.rb
482
-
483
- # Install dependencies
484
- bundle install
485
-
486
- # Run tests
487
- ruby test.rb
488
-
489
- # Generate documentation
490
- yard doc
491
- ```
492
-
493
- ## Contributing
494
-
495
- 1. Fork the repository
496
- 2. Create a feature branch (`git checkout -b feature/new-feature`)
497
- 3. Add tests for your changes
498
- 4. Ensure all tests pass (`ruby test.rb`)
499
- 5. Commit your changes (`git commit -am 'Add new feature'`)
500
- 6. Push to the branch (`git push origin feature/new-feature`)
501
- 7. Create a Pull Request
230
+ - [Game Protocol](https://sashite.dev/game-protocol/) Conceptual foundation
231
+ - [PNN](https://sashite.dev/specs/pnn/) Piece Name Notation
232
+ - [PIN Specification](https://sashite.dev/specs/pin/1.0.0/) Official specification
502
233
 
503
234
  ## License
504
235