sashite-pin 3.1.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 +4 -4
- data/README.md +140 -346
- data/lib/sashite/pin.rb +524 -41
- metadata +5 -10
- data/lib/sashite/pin/identifier.rb +0 -376
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 629bf8b689b39a81e6b52ca9b1386f53c6c807f5f57ae608fb99449ceb08854e
|
|
4
|
+
data.tar.gz: '0883b5de633e958ddbf72908ffd49832e999e0a1f99b615ce8aa5fa1222cd4f5'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a64ac32e1afb6b84c388823f81022efe6eda1a469ce82920b4d8119f0e747e4f5a8788c92d397a48165b36fcfda45111df785dad8523f3e861e31acb55e6fd68
|
|
7
|
+
data.tar.gz: 99a1e0e87304f2b1f525483cf952a4a0d9fdf0768297010894e6d429f4431b7c5476f287f94e3e85dd9c4c46669546ed094c2afc0fdd6296c9bccebcd8aa255b
|
data/README.md
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
# Pin
|
|
1
|
+
# Sashite::Pin
|
|
2
2
|
|
|
3
3
|
[](https://github.com/sashite/pin.rb/tags)
|
|
4
4
|
[](https://rubydoc.info/github/sashite/pin.rb/main)
|
|
5
5
|

|
|
6
6
|
[](https://github.com/sashite/pin.rb/raw/main/LICENSE.md)
|
|
7
7
|
|
|
8
|
-
> **PIN** (Piece Identifier Notation) implementation for
|
|
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/)
|
|
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,62 +31,74 @@ gem install sashite-pin
|
|
|
31
31
|
```ruby
|
|
32
32
|
require "sashite/pin"
|
|
33
33
|
|
|
34
|
-
# Parse PIN strings
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
40
|
+
|
|
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
|
|
40
47
|
|
|
41
48
|
# Create identifiers directly
|
|
42
|
-
|
|
43
|
-
|
|
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"
|
|
44
65
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
Sashite::Pin.valid?("+R") # => true
|
|
48
|
-
Sashite::Pin.valid?("invalid") # => false
|
|
66
|
+
normalized = enhanced.normalize
|
|
67
|
+
normalized.to_s # => "K"
|
|
49
68
|
|
|
50
|
-
#
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
diminished = identifier.diminish # => #<Pin::Identifier type=:K side=:first state=:diminished>
|
|
54
|
-
diminished.to_s # => "-K"
|
|
69
|
+
# Side transformation
|
|
70
|
+
flipped = pin.flip
|
|
71
|
+
flipped.to_s # => "k"
|
|
55
72
|
|
|
56
|
-
#
|
|
57
|
-
|
|
58
|
-
|
|
73
|
+
# Terminal transformations
|
|
74
|
+
terminal = pin.mark_terminal
|
|
75
|
+
terminal.to_s # => "K^"
|
|
59
76
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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"
|
|
63
83
|
|
|
64
84
|
# State queries
|
|
65
|
-
|
|
66
|
-
enhanced.enhanced?
|
|
67
|
-
diminished.diminished?
|
|
85
|
+
pin.normal? # => true
|
|
86
|
+
enhanced.enhanced? # => true
|
|
87
|
+
diminished.diminished? # => true
|
|
68
88
|
|
|
69
89
|
# Side queries
|
|
70
|
-
|
|
71
|
-
flipped.second_player?
|
|
90
|
+
pin.first_player? # => true
|
|
91
|
+
flipped.second_player? # => true
|
|
72
92
|
|
|
73
|
-
#
|
|
74
|
-
|
|
75
|
-
enhanced.prefix # => "+"
|
|
76
|
-
identifier.prefix # => ""
|
|
93
|
+
# Terminal queries
|
|
94
|
+
terminal.terminal? # => true
|
|
77
95
|
|
|
78
|
-
#
|
|
96
|
+
# Comparison
|
|
79
97
|
king1 = Sashite::Pin.parse("K")
|
|
80
98
|
king2 = Sashite::Pin.parse("k")
|
|
81
|
-
queen = Sashite::Pin.parse("Q")
|
|
82
|
-
|
|
83
|
-
king1.same_type?(king2) # => true (both kings)
|
|
84
|
-
king1.same_side?(queen) # => true (both first player)
|
|
85
|
-
king1.same_type?(queen) # => false (different types)
|
|
86
99
|
|
|
87
|
-
#
|
|
88
|
-
|
|
89
|
-
enemy_promoted = pawn.flip.enhance # => "+p" (second player promoted pawn)
|
|
100
|
+
king1.same_type?(king2) # => true
|
|
101
|
+
king1.same_side?(king2) # => false
|
|
90
102
|
```
|
|
91
103
|
|
|
92
104
|
## Format Specification
|
|
@@ -94,348 +106,130 @@ enemy_promoted = pawn.flip.enhance # => "+p" (second player promoted
|
|
|
94
106
|
### Structure
|
|
95
107
|
|
|
96
108
|
```
|
|
97
|
-
[<state>]<letter>
|
|
109
|
+
[<state-modifier>]<letter>[<terminal-marker>]
|
|
98
110
|
```
|
|
99
111
|
|
|
100
112
|
### Components
|
|
101
113
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
* `+`: Enhanced state (promoted, upgraded, empowered)
|
|
109
|
-
* `-`: Diminished state (weakened, restricted, temporary)
|
|
110
|
-
* No prefix: Normal state
|
|
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 |
|
|
111
119
|
|
|
112
|
-
###
|
|
120
|
+
### Side Convention
|
|
113
121
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
```
|
|
122
|
+
- **Uppercase** (`A-Z`): First player
|
|
123
|
+
- **Lowercase** (`a-z`): Second player
|
|
117
124
|
|
|
118
125
|
### Examples
|
|
119
126
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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 |
|
|
124
134
|
|
|
125
135
|
## API Reference
|
|
126
136
|
|
|
127
|
-
###
|
|
128
|
-
|
|
129
|
-
* `Sashite::Pin.valid?(pin_string)` - Check if string is valid PIN notation
|
|
130
|
-
* `Sashite::Pin.parse(pin_string)` - Parse PIN string into Identifier object
|
|
131
|
-
* `Sashite::Pin.identifier(type, side, state = :normal)` - Create identifier instance directly
|
|
132
|
-
|
|
133
|
-
### Identifier Class
|
|
134
|
-
|
|
135
|
-
#### Creation and Parsing
|
|
136
|
-
|
|
137
|
-
* `Sashite::Pin::Identifier.new(type, side, state = :normal)` - Create identifier instance
|
|
138
|
-
* `Sashite::Pin::Identifier.parse(pin_string)` - Parse PIN string (same as module method)
|
|
139
|
-
* `Sashite::Pin::Identifier.valid?(pin_string)` - Validate PIN string (class method)
|
|
140
|
-
|
|
141
|
-
#### Attribute Access
|
|
142
|
-
|
|
143
|
-
* `#type` - Get piece type (symbol \:A to \:Z, always uppercase)
|
|
144
|
-
* `#side` - Get player side (\:first or \:second)
|
|
145
|
-
* `#state` - Get state (\:normal, \:enhanced, or \:diminished)
|
|
146
|
-
* `#letter` - Get letter representation (string, case determined by side)
|
|
147
|
-
* `#prefix` - Get state prefix (string: "+", "-", or "")
|
|
148
|
-
* `#to_s` - Convert to PIN string representation
|
|
149
|
-
|
|
150
|
-
#### Type and Case Handling
|
|
151
|
-
|
|
152
|
-
**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
|
|
153
138
|
|
|
154
139
|
```ruby
|
|
155
|
-
#
|
|
156
|
-
|
|
157
|
-
identifier2 = Sashite::Pin.parse("k") # type: :K, side: :second
|
|
158
|
-
|
|
159
|
-
identifier1.type # => :K (uppercase symbol)
|
|
160
|
-
identifier2.type # => :K (same uppercase symbol)
|
|
161
|
-
|
|
162
|
-
identifier1.letter # => "K" (uppercase display)
|
|
163
|
-
identifier2.letter # => "k" (lowercase display)
|
|
140
|
+
Sashite::Pin.parse(pin_string) # => Sashite::Pin | raises ArgumentError
|
|
141
|
+
Sashite::Pin.valid?(pin_string) # => boolean
|
|
164
142
|
```
|
|
165
143
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
* `#normal?` - Check if normal state (no modifiers)
|
|
169
|
-
* `#enhanced?` - Check if enhanced state
|
|
170
|
-
* `#diminished?` - Check if diminished state
|
|
171
|
-
|
|
172
|
-
#### Side Queries
|
|
173
|
-
|
|
174
|
-
* `#first_player?` - Check if first player identifier
|
|
175
|
-
* `#second_player?` - Check if second player identifier
|
|
176
|
-
|
|
177
|
-
#### State Transformations (immutable - return new instances)
|
|
178
|
-
|
|
179
|
-
* `#enhance` - Create enhanced version
|
|
180
|
-
* `#unenhance` - Remove enhanced state
|
|
181
|
-
* `#diminish` - Create diminished version
|
|
182
|
-
* `#undiminish` - Remove diminished state
|
|
183
|
-
* `#normalize` - Remove all state modifiers
|
|
184
|
-
* `#flip` - Switch player (change side)
|
|
185
|
-
|
|
186
|
-
#### Attribute Transformations (immutable - return new instances)
|
|
187
|
-
|
|
188
|
-
* `#with_type(new_type)` - Create identifier with different type
|
|
189
|
-
* `#with_side(new_side)` - Create identifier with different side
|
|
190
|
-
* `#with_state(new_state)` - Create identifier with different state
|
|
191
|
-
|
|
192
|
-
#### Comparison Methods
|
|
193
|
-
|
|
194
|
-
* `#same_type?(other)` - Check if same piece type
|
|
195
|
-
* `#same_side?(other)` - Check if same side
|
|
196
|
-
* `#same_state?(other)` - Check if same state
|
|
197
|
-
* `#==(other)` - Full equality comparison
|
|
198
|
-
|
|
199
|
-
### Constants
|
|
200
|
-
|
|
201
|
-
* `Sashite::Pin::Identifier::PIN_PATTERN` - Regular expression for PIN validation (internal use)
|
|
202
|
-
|
|
203
|
-
## Advanced Usage
|
|
204
|
-
|
|
205
|
-
### Type Normalization Examples
|
|
144
|
+
### Creation
|
|
206
145
|
|
|
207
146
|
```ruby
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
147
|
+
Sashite::Pin.new(type, side)
|
|
148
|
+
Sashite::Pin.new(type, side, state)
|
|
149
|
+
Sashite::Pin.new(type, side, state, terminal: boolean)
|
|
150
|
+
```
|
|
211
151
|
|
|
212
|
-
|
|
213
|
-
white_king.type # => :K
|
|
214
|
-
black_king.type # => :K (same type!)
|
|
152
|
+
### Conversion
|
|
215
153
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
154
|
+
```ruby
|
|
155
|
+
pin.to_s # => String
|
|
156
|
+
pin.letter # => String (case determined by side)
|
|
157
|
+
pin.prefix # => String ("+" | "-" | "")
|
|
158
|
+
pin.suffix # => String ("^" | "")
|
|
159
|
+
```
|
|
219
160
|
|
|
220
|
-
|
|
221
|
-
white_king.letter # => "K"
|
|
222
|
-
black_king.letter # => "k"
|
|
161
|
+
### Transformations
|
|
223
162
|
|
|
224
|
-
|
|
225
|
-
white_king.same_type?(black_king) # => true
|
|
226
|
-
white_king.same_side?(black_king) # => false
|
|
227
|
-
```
|
|
163
|
+
All transformations return new `Sashite::Pin` instances:
|
|
228
164
|
|
|
229
|
-
### Immutable Transformations
|
|
230
165
|
```ruby
|
|
231
|
-
#
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
#
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
#
|
|
242
|
-
|
|
243
|
-
|
|
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
|
|
244
183
|
```
|
|
245
184
|
|
|
246
|
-
###
|
|
247
|
-
```ruby
|
|
248
|
-
class GameBoard
|
|
249
|
-
def initialize
|
|
250
|
-
@pieces = {}
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
def place(square, piece)
|
|
254
|
-
@pieces[square] = piece
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
def promote(square, new_type = :Q)
|
|
258
|
-
piece = @pieces[square]
|
|
259
|
-
return nil unless piece&.normal? # Can only promote normal pieces
|
|
260
|
-
|
|
261
|
-
@pieces[square] = piece.with_type(new_type).enhance
|
|
262
|
-
end
|
|
263
|
-
|
|
264
|
-
def capture(from_square, to_square)
|
|
265
|
-
captured = @pieces[to_square]
|
|
266
|
-
@pieces[to_square] = @pieces.delete(from_square)
|
|
267
|
-
captured
|
|
268
|
-
end
|
|
269
|
-
|
|
270
|
-
def pieces_by_side(side)
|
|
271
|
-
@pieces.select { |_, piece| piece.side == side }
|
|
272
|
-
end
|
|
273
|
-
|
|
274
|
-
def promoted_pieces
|
|
275
|
-
@pieces.select { |_, piece| piece.enhanced? }
|
|
276
|
-
end
|
|
277
|
-
end
|
|
278
|
-
|
|
279
|
-
# Usage
|
|
280
|
-
board = GameBoard.new
|
|
281
|
-
board.place("e1", Sashite::Pin.piece(:K, :first, :normal))
|
|
282
|
-
board.place("e8", Sashite::Pin.piece(:K, :second, :normal))
|
|
283
|
-
board.place("a7", Sashite::Pin.piece(:P, :first, :normal))
|
|
284
|
-
|
|
285
|
-
# Promote pawn
|
|
286
|
-
board.promote("a7", :Q)
|
|
287
|
-
promoted = board.promoted_pieces
|
|
288
|
-
puts promoted.values.first.to_s # => "+Q"
|
|
289
|
-
```
|
|
185
|
+
### Queries
|
|
290
186
|
|
|
291
|
-
### Piece Analysis
|
|
292
187
|
```ruby
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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)
|
|
310
205
|
```
|
|
311
206
|
|
|
312
|
-
|
|
207
|
+
## Data Structure
|
|
208
|
+
|
|
313
209
|
```ruby
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
(piece.first_player? && target_rank == 8) ||
|
|
320
|
-
(piece.second_player? && target_rank == 1)
|
|
321
|
-
when :R, :B, :S, :N, :L # Shōgi pieces that can promote
|
|
322
|
-
true
|
|
323
|
-
else
|
|
324
|
-
false
|
|
325
|
-
end
|
|
326
|
-
end
|
|
327
|
-
|
|
328
|
-
pawn = Sashite::Pin.piece(:P, :first, :normal)
|
|
329
|
-
puts can_promote?(pawn, 8) # => true
|
|
330
|
-
|
|
331
|
-
promoted_pawn = pawn.enhance
|
|
332
|
-
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
|
|
333
215
|
```
|
|
334
216
|
|
|
335
217
|
## Protocol Mapping
|
|
336
218
|
|
|
337
219
|
Following the [Game Protocol](https://sashite.dev/game-protocol/):
|
|
338
220
|
|
|
339
|
-
| Protocol Attribute | PIN Encoding |
|
|
340
|
-
|
|
341
|
-
|
|
|
342
|
-
|
|
|
343
|
-
|
|
|
344
|
-
|
|
345
|
-
**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.
|
|
346
|
-
|
|
347
|
-
**Canonical principle**: Identical pieces must have identical PIN representations.
|
|
348
|
-
|
|
349
|
-
**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/).
|
|
350
|
-
|
|
351
|
-
## Properties
|
|
352
|
-
|
|
353
|
-
* **ASCII Compatible**: Maximum portability across systems
|
|
354
|
-
* **Rule-Agnostic**: Independent of specific game mechanics
|
|
355
|
-
* **Compact Format**: 1-2 characters per piece
|
|
356
|
-
* **Visual Distinction**: Clear player differentiation through case
|
|
357
|
-
* **Type Normalization**: Consistent uppercase type representation internally
|
|
358
|
-
* **Protocol Compliant**: Direct implementation of Sashité piece attributes
|
|
359
|
-
* **Immutable**: All piece instances are frozen and transformations return new objects
|
|
360
|
-
* **Functional**: Pure functions with no side effects
|
|
361
|
-
|
|
362
|
-
## Implementation Notes
|
|
363
|
-
|
|
364
|
-
### Type Normalization Convention
|
|
365
|
-
|
|
366
|
-
PIN follows a strict type normalization convention:
|
|
367
|
-
|
|
368
|
-
1. **Internal Storage**: All piece types are stored as uppercase symbols (`:A` to `:Z`)
|
|
369
|
-
2. **Input Flexibility**: Both `"K"` and `"k"` are valid input during parsing
|
|
370
|
-
3. **Case Semantics**: Input case determines the `side` attribute, not the `type`
|
|
371
|
-
4. **Display Logic**: Output case is computed from `side` during rendering
|
|
372
|
-
|
|
373
|
-
This design ensures:
|
|
374
|
-
- Consistent internal representation regardless of input format
|
|
375
|
-
- Clear separation between piece identity (type) and ownership (side)
|
|
376
|
-
- Predictable behavior when comparing pieces of the same type
|
|
377
|
-
|
|
378
|
-
### Example Flow
|
|
379
|
-
|
|
380
|
-
```ruby
|
|
381
|
-
# Input: "k" (lowercase)
|
|
382
|
-
# ↓ Parsing
|
|
383
|
-
# type: :K (normalized to uppercase)
|
|
384
|
-
# side: :second (inferred from lowercase input)
|
|
385
|
-
# ↓ Display
|
|
386
|
-
# letter: "k" (computed from type + side)
|
|
387
|
-
# PIN: "k" (final representation)
|
|
388
|
-
```
|
|
389
|
-
|
|
390
|
-
This ensures that `parse(pin).to_s == pin` for all valid PIN strings while maintaining internal consistency.
|
|
391
|
-
|
|
392
|
-
## System Constraints
|
|
393
|
-
|
|
394
|
-
- **Maximum 26 piece types** per game system (one per ASCII letter)
|
|
395
|
-
- **Exactly 2 players** (uppercase/lowercase distinction)
|
|
396
|
-
- **3 state levels** (enhanced, normal, diminished)
|
|
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 (`^`) |
|
|
397
227
|
|
|
398
228
|
## Related Specifications
|
|
399
229
|
|
|
400
|
-
- [Game Protocol](https://sashite.dev/game-protocol/)
|
|
401
|
-
- [PNN](https://sashite.dev/specs/pnn/)
|
|
402
|
-
- [
|
|
403
|
-
- [HAND](https://sashite.dev/specs/hand/) - Reserve location notation
|
|
404
|
-
- [PMN](https://sashite.dev/specs/pmn/) - Portable Move Notation
|
|
405
|
-
|
|
406
|
-
## Documentation
|
|
407
|
-
|
|
408
|
-
- [Official PIN Specification v1.0.0](https://sashite.dev/specs/pin/1.0.0/)
|
|
409
|
-
- [PIN Examples Documentation](https://sashite.dev/specs/pin/1.0.0/examples/)
|
|
410
|
-
- [Game Protocol Foundation](https://sashite.dev/game-protocol/)
|
|
411
|
-
- [API Documentation](https://rubydoc.info/github/sashite/pin.rb/main)
|
|
412
|
-
|
|
413
|
-
## Development
|
|
414
|
-
|
|
415
|
-
```sh
|
|
416
|
-
# Clone the repository
|
|
417
|
-
git clone https://github.com/sashite/pin.rb.git
|
|
418
|
-
cd pin.rb
|
|
419
|
-
|
|
420
|
-
# Install dependencies
|
|
421
|
-
bundle install
|
|
422
|
-
|
|
423
|
-
# Run tests
|
|
424
|
-
ruby test.rb
|
|
425
|
-
|
|
426
|
-
# Generate documentation
|
|
427
|
-
yard doc
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
## Contributing
|
|
431
|
-
|
|
432
|
-
1. Fork the repository
|
|
433
|
-
2. Create a feature branch (`git checkout -b feature/new-feature`)
|
|
434
|
-
3. Add tests for your changes
|
|
435
|
-
4. Ensure all tests pass (`ruby test.rb`)
|
|
436
|
-
5. Commit your changes (`git commit -am 'Add new feature'`)
|
|
437
|
-
6. Push to the branch (`git push origin feature/new-feature`)
|
|
438
|
-
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
|
|
439
233
|
|
|
440
234
|
## License
|
|
441
235
|
|