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 +4 -4
- data/README.md +142 -411
- data/lib/sashite/pin.rb +524 -43
- metadata +4 -10
- data/lib/sashite/pin/identifier.rb +0 -442
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,81 +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
|
-
|
|
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
|
-
#
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
#
|
|
53
|
-
Sashite::Pin.valid?("K")
|
|
54
|
-
Sashite::Pin.valid?("+R")
|
|
55
|
-
Sashite::Pin.valid?("K^")
|
|
56
|
-
Sashite::Pin.valid?("
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
enhanced
|
|
61
|
-
|
|
62
|
-
diminished =
|
|
63
|
-
diminished.to_s
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
#
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
#
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
80
|
-
enhanced.enhanced?
|
|
81
|
-
diminished.diminished?
|
|
85
|
+
pin.normal? # => true
|
|
86
|
+
enhanced.enhanced? # => true
|
|
87
|
+
diminished.diminished? # => true
|
|
82
88
|
|
|
83
89
|
# Side queries
|
|
84
|
-
|
|
85
|
-
flipped.second_player?
|
|
90
|
+
pin.first_player? # => true
|
|
91
|
+
flipped.second_player? # => true
|
|
86
92
|
|
|
87
|
-
#
|
|
88
|
-
|
|
89
|
-
enhanced.prefix # => "+"
|
|
90
|
-
identifier.prefix # => ""
|
|
91
|
-
terminal_king.suffix # => "^"
|
|
93
|
+
# Terminal queries
|
|
94
|
+
terminal.terminal? # => true
|
|
92
95
|
|
|
93
|
-
#
|
|
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
|
-
#
|
|
107
|
-
|
|
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
|
-
|
|
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
|
-
|
|
124
|
-
* Lowercase: Second player pieces
|
|
125
|
-
* **State Modifier** (optional prefix):
|
|
120
|
+
### Side Convention
|
|
126
121
|
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
###
|
|
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
|
-
#
|
|
184
|
-
|
|
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
|
-
|
|
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
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
-
|
|
253
|
-
white_king.type # => :K
|
|
254
|
-
black_king.type # => :K (same type!)
|
|
152
|
+
### Conversion
|
|
255
153
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
-
|
|
261
|
-
white_king.letter # => "K"
|
|
262
|
-
black_king.letter # => "k"
|
|
161
|
+
### Transformations
|
|
263
162
|
|
|
264
|
-
|
|
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
|
-
#
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
#
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
#
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
#
|
|
286
|
-
|
|
287
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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
|
-
|
|
207
|
+
## Data Structure
|
|
208
|
+
|
|
364
209
|
```ruby
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
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 |
|
|
391
|
-
|
|
392
|
-
|
|
|
393
|
-
|
|
|
394
|
-
|
|
|
395
|
-
|
|
|
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/)
|
|
464
|
-
- [PNN](https://sashite.dev/specs/pnn/)
|
|
465
|
-
- [
|
|
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
|
|