sashite-pin 2.0.1 → 3.0.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 +92 -152
- data/lib/sashite/pin/{piece.rb → identifier.rb} +73 -79
- data/lib/sashite/pin.rb +17 -23
- metadata +9 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f69bdd89484b5fc844f13c7cedbbdf2efe15f75620fe1eb695d0258120cf16d6
|
|
4
|
+
data.tar.gz: 38c51ef6f22d3267c26f5baa425306645e2a17f8448575da6759112a6b71f115
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c98b8c1e383c0414341be4ea7ea9f3f5f5864330f16f3faed608a315cd46a52e039f539c4d45f45d8b84aa6965d999560f0d0d937cf4743ec6c05655e9b6470d
|
|
7
|
+
data.tar.gz: 847ca5d4e6c6bd5697d665bebc103e01f876fc2a5f8c0947a27ef3589de94bd9d482bb69322817985e9dc6d23a3588801c8021ab54467b70bda49929cd62aef4
|
data/README.md
CHANGED
|
@@ -11,14 +11,14 @@
|
|
|
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
|
|
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.
|
|
15
15
|
|
|
16
16
|
## Installation
|
|
17
17
|
|
|
18
18
|
```ruby
|
|
19
19
|
# In your Gemfile
|
|
20
20
|
gem "sashite-pin"
|
|
21
|
-
|
|
21
|
+
````
|
|
22
22
|
|
|
23
23
|
Or install manually:
|
|
24
24
|
|
|
@@ -31,16 +31,16 @@ gem install sashite-pin
|
|
|
31
31
|
```ruby
|
|
32
32
|
require "sashite/pin"
|
|
33
33
|
|
|
34
|
-
# Parse PIN strings into
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
# Parse PIN strings into identifier objects
|
|
35
|
+
identifier = Sashite::Pin.parse("K") # => #<Pin::Identifier type=:K side=:first state=:normal>
|
|
36
|
+
identifier.to_s # => "K"
|
|
37
|
+
identifier.type # => :K
|
|
38
|
+
identifier.side # => :first
|
|
39
|
+
identifier.state # => :normal
|
|
40
40
|
|
|
41
|
-
# Create
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
# Create identifiers directly
|
|
42
|
+
identifier = Sashite::Pin.identifier(:K, :first, :normal) # => #<Pin::Identifier type=:K side=:first state=:normal>
|
|
43
|
+
identifier = Sashite::Pin::Identifier.new(:R, :second, :enhanced) # => #<Pin::Identifier type=:R side=:second state=:enhanced>
|
|
44
44
|
|
|
45
45
|
# Validate PIN strings
|
|
46
46
|
Sashite::Pin.valid?("K") # => true
|
|
@@ -48,170 +48,104 @@ Sashite::Pin.valid?("+R") # => true
|
|
|
48
48
|
Sashite::Pin.valid?("invalid") # => false
|
|
49
49
|
|
|
50
50
|
# State manipulation (returns new immutable instances)
|
|
51
|
-
enhanced =
|
|
52
|
-
enhanced.to_s
|
|
53
|
-
diminished =
|
|
54
|
-
diminished.to_s
|
|
51
|
+
enhanced = identifier.enhance # => #<Pin::Identifier type=:K side=:first state=:enhanced>
|
|
52
|
+
enhanced.to_s # => "+K"
|
|
53
|
+
diminished = identifier.diminish # => #<Pin::Identifier type=:K side=:first state=:diminished>
|
|
54
|
+
diminished.to_s # => "-K"
|
|
55
55
|
|
|
56
56
|
# Side manipulation
|
|
57
|
-
flipped =
|
|
58
|
-
flipped.to_s
|
|
57
|
+
flipped = identifier.flip # => #<Pin::Identifier type=:K side=:second state=:normal>
|
|
58
|
+
flipped.to_s # => "k"
|
|
59
59
|
|
|
60
60
|
# Type manipulation
|
|
61
|
-
queen =
|
|
62
|
-
queen.to_s
|
|
61
|
+
queen = identifier.with_type(:Q) # => #<Pin::Identifier type=:Q side=:first state=:normal>
|
|
62
|
+
queen.to_s # => "Q"
|
|
63
63
|
|
|
64
64
|
# State queries
|
|
65
|
-
|
|
66
|
-
enhanced.enhanced?
|
|
67
|
-
diminished.diminished?
|
|
65
|
+
identifier.normal? # => true
|
|
66
|
+
enhanced.enhanced? # => true
|
|
67
|
+
diminished.diminished? # => true
|
|
68
68
|
|
|
69
69
|
# Side queries
|
|
70
|
-
|
|
71
|
-
flipped.second_player?
|
|
70
|
+
identifier.first_player? # => true
|
|
71
|
+
flipped.second_player? # => true
|
|
72
72
|
|
|
73
73
|
# Attribute access
|
|
74
|
-
|
|
75
|
-
enhanced.prefix
|
|
76
|
-
|
|
74
|
+
identifier.letter # => "K"
|
|
75
|
+
enhanced.prefix # => "+"
|
|
76
|
+
identifier.prefix # => ""
|
|
77
77
|
|
|
78
78
|
# Type and side comparison
|
|
79
79
|
king1 = Sashite::Pin.parse("K")
|
|
80
80
|
king2 = Sashite::Pin.parse("k")
|
|
81
81
|
queen = Sashite::Pin.parse("Q")
|
|
82
82
|
|
|
83
|
-
king1.same_type?(king2)
|
|
84
|
-
king1.same_side?(queen)
|
|
85
|
-
king1.same_type?(queen)
|
|
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
86
|
|
|
87
87
|
# Functional transformations can be chained
|
|
88
88
|
pawn = Sashite::Pin.parse("P")
|
|
89
|
-
enemy_promoted = pawn.flip.enhance
|
|
89
|
+
enemy_promoted = pawn.flip.enhance # => "+p" (second player promoted pawn)
|
|
90
90
|
```
|
|
91
91
|
|
|
92
92
|
## Format Specification
|
|
93
93
|
|
|
94
94
|
### Structure
|
|
95
|
+
|
|
95
96
|
```
|
|
96
97
|
[<state>]<letter>
|
|
97
98
|
```
|
|
98
99
|
|
|
99
100
|
### Components
|
|
100
101
|
|
|
101
|
-
|
|
102
|
-
- Uppercase: First player pieces
|
|
103
|
-
- Lowercase: Second player pieces
|
|
104
|
-
- **State** (optional prefix):
|
|
105
|
-
- `+`: Enhanced state (promoted, upgraded, empowered)
|
|
106
|
-
- `-`: Diminished state (weakened, restricted, temporary)
|
|
107
|
-
- No prefix: Normal state
|
|
108
|
-
|
|
109
|
-
### Regular Expression
|
|
110
|
-
```ruby
|
|
111
|
-
/\A[-+]?[A-Za-z]\z/
|
|
112
|
-
```
|
|
102
|
+
* **Letter** (`A-Z`, `a-z`): Represents piece type and side
|
|
113
103
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
- `+R` - First player rook (enhanced state)
|
|
118
|
-
- `-p` - Second player pawn (diminished state)
|
|
104
|
+
* Uppercase: First player pieces
|
|
105
|
+
* Lowercase: Second player pieces
|
|
106
|
+
* **State** (optional prefix):
|
|
119
107
|
|
|
120
|
-
|
|
108
|
+
* `+`: Enhanced state (promoted, upgraded, empowered)
|
|
109
|
+
* `-`: Diminished state (weakened, restricted, temporary)
|
|
110
|
+
* No prefix: Normal state
|
|
121
111
|
|
|
122
|
-
###
|
|
123
|
-
```ruby
|
|
124
|
-
# Standard pieces
|
|
125
|
-
king = Sashite::Pin.piece(:K, :first, :normal) # => white king
|
|
126
|
-
king.first_player? # => true
|
|
127
|
-
king.type # => :K
|
|
128
|
-
|
|
129
|
-
# State modifiers for special conditions
|
|
130
|
-
castling_king = king.enhance # => castling-eligible king
|
|
131
|
-
castling_king.to_s # => "+K"
|
|
132
|
-
|
|
133
|
-
vulnerable_pawn = Sashite::Pin.piece(:P, :first, :diminished) # => en passant vulnerable
|
|
134
|
-
vulnerable_pawn.to_s # => "-P"
|
|
135
|
-
|
|
136
|
-
# All piece types
|
|
137
|
-
piece_types = [:K, :Q, :R, :B, :N, :P]
|
|
138
|
-
white_pieces = piece_types.map { |type| Sashite::Pin.piece(type, :first, :normal) }
|
|
139
|
-
black_pieces = white_pieces.map(&:flip) # Convert to black pieces
|
|
140
|
-
```
|
|
112
|
+
### Regular Expression
|
|
141
113
|
|
|
142
|
-
### Japanese Chess (Shōgi)
|
|
143
114
|
```ruby
|
|
144
|
-
|
|
145
|
-
rook = Sashite::Pin.piece(:R, :first, :normal) # => white rook
|
|
146
|
-
bishop = Sashite::Pin.piece(:B, :first, :normal) # => white bishop
|
|
147
|
-
|
|
148
|
-
# Promoted pieces (enhanced state)
|
|
149
|
-
dragon_king = rook.enhance # => promoted rook (Dragon King)
|
|
150
|
-
dragon_king.to_s # => "+R"
|
|
151
|
-
|
|
152
|
-
dragon_horse = bishop.enhance # => promoted bishop (Dragon Horse)
|
|
153
|
-
dragon_horse.to_s # => "+B"
|
|
154
|
-
|
|
155
|
-
# Promoted pawn
|
|
156
|
-
pawn = Sashite::Pin.piece(:P, :first, :normal)
|
|
157
|
-
tokin = pawn.enhance # => promoted pawn (Tokin)
|
|
158
|
-
tokin.to_s # => "+P"
|
|
159
|
-
|
|
160
|
-
# All promotable pieces can use the same pattern
|
|
161
|
-
promotable_types = [:R, :B, :S, :N, :L, :P]
|
|
162
|
-
promotable = promotable_types.map { |type| Sashite::Pin.piece(type, :first, :normal) }
|
|
163
|
-
promoted = promotable.map(&:enhance)
|
|
115
|
+
/\A[-+]?[A-Za-z]\z/
|
|
164
116
|
```
|
|
165
117
|
|
|
166
|
-
###
|
|
167
|
-
```ruby
|
|
168
|
-
# Basic pieces
|
|
169
|
-
met = Sashite::Pin.piece(:M, :first, :normal) # => white Met (queen)
|
|
170
|
-
pawn = Sashite::Pin.piece(:P, :first, :normal) # => white Bia (pawn)
|
|
171
|
-
|
|
172
|
-
# Promoted pawns
|
|
173
|
-
bia_kaew = pawn.enhance # => promoted pawn (Bia Kaew)
|
|
174
|
-
bia_kaew.to_s # => "+P"
|
|
175
|
-
|
|
176
|
-
# Makruk pieces
|
|
177
|
-
makruk_types = [:K, :M, :R, :B, :N, :P]
|
|
178
|
-
makruk_pieces = makruk_types.map { |type| Sashite::Pin.piece(type, :first, :normal) }
|
|
179
|
-
```
|
|
118
|
+
### Examples
|
|
180
119
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
flying_general = general.enhance # => flying general (special state)
|
|
186
|
-
flying_general.to_s # => "+G"
|
|
187
|
-
|
|
188
|
-
# Soldiers that crossed the river
|
|
189
|
-
soldier = Sashite::Pin.piece(:P, :first, :normal)
|
|
190
|
-
crossed_soldier = soldier.enhance # => soldier with enhanced movement
|
|
191
|
-
crossed_soldier.to_s # => "+P"
|
|
192
|
-
```
|
|
120
|
+
* `K` - First player king (normal state)
|
|
121
|
+
* `k` - Second player king (normal state)
|
|
122
|
+
* `+R` - First player rook (enhanced state)
|
|
123
|
+
* `-p` - Second player pawn (diminished state)
|
|
193
124
|
|
|
194
125
|
## API Reference
|
|
195
126
|
|
|
196
127
|
### Main Module Methods
|
|
197
128
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
|
201
132
|
|
|
202
|
-
###
|
|
133
|
+
### Identifier Class
|
|
203
134
|
|
|
204
135
|
#### Creation and Parsing
|
|
205
|
-
|
|
206
|
-
|
|
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)
|
|
207
140
|
|
|
208
141
|
#### Attribute Access
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
|
215
149
|
|
|
216
150
|
#### Type and Case Handling
|
|
217
151
|
|
|
@@ -219,46 +153,52 @@ crossed_soldier.to_s # => "+P"
|
|
|
219
153
|
|
|
220
154
|
```ruby
|
|
221
155
|
# Both create the same internal type representation
|
|
222
|
-
|
|
223
|
-
|
|
156
|
+
identifier1 = Sashite::Pin.parse("K") # type: :K, side: :first
|
|
157
|
+
identifier2 = Sashite::Pin.parse("k") # type: :K, side: :second
|
|
224
158
|
|
|
225
|
-
|
|
226
|
-
|
|
159
|
+
identifier1.type # => :K (uppercase symbol)
|
|
160
|
+
identifier2.type # => :K (same uppercase symbol)
|
|
227
161
|
|
|
228
|
-
|
|
229
|
-
|
|
162
|
+
identifier1.letter # => "K" (uppercase display)
|
|
163
|
+
identifier2.letter # => "k" (lowercase display)
|
|
230
164
|
```
|
|
231
165
|
|
|
232
166
|
#### State Queries
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
167
|
+
|
|
168
|
+
* `#normal?` - Check if normal state (no modifiers)
|
|
169
|
+
* `#enhanced?` - Check if enhanced state
|
|
170
|
+
* `#diminished?` - Check if diminished state
|
|
236
171
|
|
|
237
172
|
#### Side Queries
|
|
238
|
-
|
|
239
|
-
|
|
173
|
+
|
|
174
|
+
* `#first_player?` - Check if first player identifier
|
|
175
|
+
* `#second_player?` - Check if second player identifier
|
|
240
176
|
|
|
241
177
|
#### State Transformations (immutable - return new instances)
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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)
|
|
248
185
|
|
|
249
186
|
#### Attribute Transformations (immutable - return new instances)
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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
|
|
253
191
|
|
|
254
192
|
#### Comparison Methods
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
|
259
198
|
|
|
260
199
|
### Constants
|
|
261
|
-
|
|
200
|
+
|
|
201
|
+
* `Sashite::Pin::Identifier::PIN_PATTERN` - Regular expression for PIN validation (internal use)
|
|
262
202
|
|
|
263
203
|
## Advanced Usage
|
|
264
204
|
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module Sashite
|
|
4
4
|
module Pin
|
|
5
|
-
# Represents
|
|
5
|
+
# Represents an identifier in PIN (Piece Identifier Notation) format.
|
|
6
6
|
#
|
|
7
|
-
#
|
|
7
|
+
# An identifier consists of a single ASCII letter with optional state modifiers:
|
|
8
8
|
# - Enhanced state: prefix '+'
|
|
9
9
|
# - Diminished state: prefix '-'
|
|
10
10
|
# - Normal state: no modifier
|
|
@@ -15,7 +15,7 @@ module Sashite
|
|
|
15
15
|
#
|
|
16
16
|
# All instances are immutable - state manipulation methods return new instances.
|
|
17
17
|
# This follows the Game Protocol's piece model with Type, Side, and State attributes.
|
|
18
|
-
class
|
|
18
|
+
class Identifier
|
|
19
19
|
# PIN validation pattern matching the specification
|
|
20
20
|
PIN_PATTERN = /\A(?<prefix>[-+])?(?<letter>[a-zA-Z])\z/
|
|
21
21
|
|
|
@@ -57,7 +57,7 @@ module Sashite
|
|
|
57
57
|
# @return [Symbol] the piece state (:normal, :enhanced, or :diminished)
|
|
58
58
|
attr_reader :state
|
|
59
59
|
|
|
60
|
-
# Create a new
|
|
60
|
+
# Create a new identifier instance
|
|
61
61
|
#
|
|
62
62
|
# @param type [Symbol] piece type (:A to :Z)
|
|
63
63
|
# @param side [Symbol] player side (:first or :second)
|
|
@@ -75,15 +75,15 @@ module Sashite
|
|
|
75
75
|
freeze
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
-
# Parse a PIN string into
|
|
78
|
+
# Parse a PIN string into an Identifier object
|
|
79
79
|
#
|
|
80
80
|
# @param pin_string [String] PIN notation string
|
|
81
|
-
# @return [
|
|
81
|
+
# @return [Identifier] new identifier instance
|
|
82
82
|
# @raise [ArgumentError] if the PIN string is invalid
|
|
83
83
|
# @example
|
|
84
|
-
# Pin::
|
|
85
|
-
# Pin::
|
|
86
|
-
# Pin::
|
|
84
|
+
# Pin::Identifier.parse("k") # => #<Pin::Identifier type=:K side=:second state=:normal>
|
|
85
|
+
# Pin::Identifier.parse("+R") # => #<Pin::Identifier type=:R side=:first state=:enhanced>
|
|
86
|
+
# Pin::Identifier.parse("-p") # => #<Pin::Identifier type=:P side=:second state=:diminished>
|
|
87
87
|
def self.parse(pin_string)
|
|
88
88
|
string_value = String(pin_string)
|
|
89
89
|
matches = match_pattern(string_value)
|
|
@@ -92,27 +92,41 @@ module Sashite
|
|
|
92
92
|
enhanced = matches[:prefix] == ENHANCED_PREFIX
|
|
93
93
|
diminished = matches[:prefix] == DIMINISHED_PREFIX
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
new(piece_type, piece_side, piece_state)
|
|
95
|
+
type = letter.upcase.to_sym
|
|
96
|
+
side = letter == letter.upcase ? FIRST_PLAYER : SECOND_PLAYER
|
|
97
|
+
state = if enhanced
|
|
98
|
+
ENHANCED_STATE
|
|
99
|
+
elsif diminished
|
|
100
|
+
DIMINISHED_STATE
|
|
101
|
+
else
|
|
102
|
+
NORMAL_STATE
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
new(type, side, state)
|
|
107
106
|
end
|
|
108
107
|
|
|
109
|
-
#
|
|
108
|
+
# Check if a string is a valid PIN notation
|
|
109
|
+
#
|
|
110
|
+
# @param pin_string [String] The string to validate
|
|
111
|
+
# @return [Boolean] true if valid PIN, false otherwise
|
|
112
|
+
#
|
|
113
|
+
# @example
|
|
114
|
+
# Sashite::Pin::Identifier.valid?("K") # => true
|
|
115
|
+
# Sashite::Pin::Identifier.valid?("+R") # => true
|
|
116
|
+
# Sashite::Pin::Identifier.valid?("-p") # => true
|
|
117
|
+
# Sashite::Pin::Identifier.valid?("KK") # => false
|
|
118
|
+
# Sashite::Pin::Identifier.valid?("++K") # => false
|
|
119
|
+
def self.valid?(pin_string)
|
|
120
|
+
return false unless pin_string.is_a?(::String)
|
|
121
|
+
|
|
122
|
+
pin_string.match?(PIN_PATTERN)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Convert the identifier to its PIN string representation
|
|
110
126
|
#
|
|
111
127
|
# @return [String] PIN notation string
|
|
112
128
|
# @example
|
|
113
|
-
#
|
|
114
|
-
# piece.to_s # => "-p"
|
|
115
|
-
# piece.to_s # => "K"
|
|
129
|
+
# identifier.to_s # => "+R"
|
|
116
130
|
def to_s
|
|
117
131
|
"#{prefix}#{letter}"
|
|
118
132
|
end
|
|
@@ -135,76 +149,62 @@ module Sashite
|
|
|
135
149
|
end
|
|
136
150
|
end
|
|
137
151
|
|
|
138
|
-
# Create a new
|
|
152
|
+
# Create a new identifier with enhanced state
|
|
139
153
|
#
|
|
140
|
-
# @return [
|
|
141
|
-
# @example
|
|
142
|
-
# piece.enhance # (:K, :first, :normal) => (:K, :first, :enhanced)
|
|
154
|
+
# @return [Identifier] new identifier instance with enhanced state
|
|
143
155
|
def enhance
|
|
144
156
|
return self if enhanced?
|
|
145
157
|
|
|
146
158
|
self.class.new(type, side, ENHANCED_STATE)
|
|
147
159
|
end
|
|
148
160
|
|
|
149
|
-
# Create a new
|
|
161
|
+
# Create a new identifier without enhanced state
|
|
150
162
|
#
|
|
151
|
-
# @return [
|
|
152
|
-
# @example
|
|
153
|
-
# piece.unenhance # (:K, :first, :enhanced) => (:K, :first, :normal)
|
|
163
|
+
# @return [Identifier] new identifier instance with normal state
|
|
154
164
|
def unenhance
|
|
155
165
|
return self unless enhanced?
|
|
156
166
|
|
|
157
167
|
self.class.new(type, side, NORMAL_STATE)
|
|
158
168
|
end
|
|
159
169
|
|
|
160
|
-
# Create a new
|
|
170
|
+
# Create a new identifier with diminished state
|
|
161
171
|
#
|
|
162
|
-
# @return [
|
|
163
|
-
# @example
|
|
164
|
-
# piece.diminish # (:K, :first, :normal) => (:K, :first, :diminished)
|
|
172
|
+
# @return [Identifier] new identifier instance with diminished state
|
|
165
173
|
def diminish
|
|
166
174
|
return self if diminished?
|
|
167
175
|
|
|
168
176
|
self.class.new(type, side, DIMINISHED_STATE)
|
|
169
177
|
end
|
|
170
178
|
|
|
171
|
-
# Create a new
|
|
179
|
+
# Create a new identifier without diminished state
|
|
172
180
|
#
|
|
173
|
-
# @return [
|
|
174
|
-
# @example
|
|
175
|
-
# piece.undiminish # (:K, :first, :diminished) => (:K, :first, :normal)
|
|
181
|
+
# @return [Identifier] new identifier instance with normal state
|
|
176
182
|
def undiminish
|
|
177
183
|
return self unless diminished?
|
|
178
184
|
|
|
179
185
|
self.class.new(type, side, NORMAL_STATE)
|
|
180
186
|
end
|
|
181
187
|
|
|
182
|
-
# Create a new
|
|
188
|
+
# Create a new identifier with normal state (no modifiers)
|
|
183
189
|
#
|
|
184
|
-
# @return [
|
|
185
|
-
# @example
|
|
186
|
-
# piece.normalize # (:K, :first, :enhanced) => (:K, :first, :normal)
|
|
190
|
+
# @return [Identifier] new identifier instance with normal state
|
|
187
191
|
def normalize
|
|
188
192
|
return self if normal?
|
|
189
193
|
|
|
190
194
|
self.class.new(type, side, NORMAL_STATE)
|
|
191
195
|
end
|
|
192
196
|
|
|
193
|
-
# Create a new
|
|
197
|
+
# Create a new identifier with opposite side
|
|
194
198
|
#
|
|
195
|
-
# @return [
|
|
196
|
-
# @example
|
|
197
|
-
# piece.flip # (:K, :first, :normal) => (:K, :second, :normal)
|
|
199
|
+
# @return [Identifier] new identifier instance with opposite side
|
|
198
200
|
def flip
|
|
199
201
|
self.class.new(type, opposite_side, state)
|
|
200
202
|
end
|
|
201
203
|
|
|
202
|
-
# Create a new
|
|
204
|
+
# Create a new identifier with a different type
|
|
203
205
|
#
|
|
204
206
|
# @param new_type [Symbol] new type (:A to :Z)
|
|
205
|
-
# @return [
|
|
206
|
-
# @example
|
|
207
|
-
# piece.with_type(:Q) # (:K, :first, :normal) => (:Q, :first, :normal)
|
|
207
|
+
# @return [Identifier] new identifier instance with new type
|
|
208
208
|
def with_type(new_type)
|
|
209
209
|
self.class.validate_type(new_type)
|
|
210
210
|
return self if type == new_type
|
|
@@ -212,12 +212,10 @@ module Sashite
|
|
|
212
212
|
self.class.new(new_type, side, state)
|
|
213
213
|
end
|
|
214
214
|
|
|
215
|
-
# Create a new
|
|
215
|
+
# Create a new identifier with a different side
|
|
216
216
|
#
|
|
217
|
-
# @param new_side [Symbol] :first or :second
|
|
218
|
-
# @return [
|
|
219
|
-
# @example
|
|
220
|
-
# piece.with_side(:second) # (:K, :first, :normal) => (:K, :second, :normal)
|
|
217
|
+
# @param new_side [Symbol] new side (:first or :second)
|
|
218
|
+
# @return [Identifier] new identifier instance with new side
|
|
221
219
|
def with_side(new_side)
|
|
222
220
|
self.class.validate_side(new_side)
|
|
223
221
|
return self if side == new_side
|
|
@@ -225,12 +223,10 @@ module Sashite
|
|
|
225
223
|
self.class.new(type, new_side, state)
|
|
226
224
|
end
|
|
227
225
|
|
|
228
|
-
# Create a new
|
|
226
|
+
# Create a new identifier with a different state
|
|
229
227
|
#
|
|
230
|
-
# @param new_state [Symbol] :normal, :enhanced, or :diminished
|
|
231
|
-
# @return [
|
|
232
|
-
# @example
|
|
233
|
-
# piece.with_state(:enhanced) # (:K, :first, :normal) => (:K, :first, :enhanced)
|
|
228
|
+
# @param new_state [Symbol] new state (:normal, :enhanced, or :diminished)
|
|
229
|
+
# @return [Identifier] new identifier instance with new state
|
|
234
230
|
def with_state(new_state)
|
|
235
231
|
self.class.validate_state(new_state)
|
|
236
232
|
return self if state == new_state
|
|
@@ -238,56 +234,54 @@ module Sashite
|
|
|
238
234
|
self.class.new(type, side, new_state)
|
|
239
235
|
end
|
|
240
236
|
|
|
241
|
-
# Check if the
|
|
237
|
+
# Check if the identifier has enhanced state
|
|
242
238
|
#
|
|
243
239
|
# @return [Boolean] true if enhanced
|
|
244
240
|
def enhanced?
|
|
245
241
|
state == ENHANCED_STATE
|
|
246
242
|
end
|
|
247
243
|
|
|
248
|
-
# Check if the
|
|
244
|
+
# Check if the identifier has diminished state
|
|
249
245
|
#
|
|
250
246
|
# @return [Boolean] true if diminished
|
|
251
247
|
def diminished?
|
|
252
248
|
state == DIMINISHED_STATE
|
|
253
249
|
end
|
|
254
250
|
|
|
255
|
-
# Check if the
|
|
251
|
+
# Check if the identifier has normal state
|
|
256
252
|
#
|
|
257
|
-
# @return [Boolean] true if
|
|
253
|
+
# @return [Boolean] true if normal
|
|
258
254
|
def normal?
|
|
259
255
|
state == NORMAL_STATE
|
|
260
256
|
end
|
|
261
257
|
|
|
262
|
-
# Check if the
|
|
258
|
+
# Check if the identifier belongs to the first player
|
|
263
259
|
#
|
|
264
260
|
# @return [Boolean] true if first player
|
|
265
261
|
def first_player?
|
|
266
262
|
side == FIRST_PLAYER
|
|
267
263
|
end
|
|
268
264
|
|
|
269
|
-
# Check if the
|
|
265
|
+
# Check if the identifier belongs to the second player
|
|
270
266
|
#
|
|
271
267
|
# @return [Boolean] true if second player
|
|
272
268
|
def second_player?
|
|
273
269
|
side == SECOND_PLAYER
|
|
274
270
|
end
|
|
275
271
|
|
|
276
|
-
# Check if this
|
|
272
|
+
# Check if this identifier is the same type as another
|
|
277
273
|
#
|
|
278
|
-
# @param other [
|
|
274
|
+
# @param other [Identifier] identifier to compare with
|
|
279
275
|
# @return [Boolean] true if same type
|
|
280
|
-
# @example
|
|
281
|
-
# king1.same_type?(king2) # (:K, :first, :normal) and (:K, :second, :enhanced) => true
|
|
282
276
|
def same_type?(other)
|
|
283
277
|
return false unless other.is_a?(self.class)
|
|
284
278
|
|
|
285
279
|
type == other.type
|
|
286
280
|
end
|
|
287
281
|
|
|
288
|
-
# Check if this
|
|
282
|
+
# Check if this identifier has the same side as another
|
|
289
283
|
#
|
|
290
|
-
# @param other [
|
|
284
|
+
# @param other [Identifier] identifier to compare with
|
|
291
285
|
# @return [Boolean] true if same side
|
|
292
286
|
def same_side?(other)
|
|
293
287
|
return false unless other.is_a?(self.class)
|
|
@@ -295,9 +289,9 @@ module Sashite
|
|
|
295
289
|
side == other.side
|
|
296
290
|
end
|
|
297
291
|
|
|
298
|
-
# Check if this
|
|
292
|
+
# Check if this identifier has the same state as another
|
|
299
293
|
#
|
|
300
|
-
# @param other [
|
|
294
|
+
# @param other [Identifier] identifier to compare with
|
|
301
295
|
# @return [Boolean] true if same state
|
|
302
296
|
def same_state?(other)
|
|
303
297
|
return false unless other.is_a?(self.class)
|
|
@@ -308,7 +302,7 @@ module Sashite
|
|
|
308
302
|
# Custom equality comparison
|
|
309
303
|
#
|
|
310
304
|
# @param other [Object] object to compare with
|
|
311
|
-
# @return [Boolean] true if
|
|
305
|
+
# @return [Boolean] true if identifiers are equal
|
|
312
306
|
def ==(other)
|
|
313
307
|
return false unless other.is_a?(self.class)
|
|
314
308
|
|
|
@@ -371,7 +365,7 @@ module Sashite
|
|
|
371
365
|
|
|
372
366
|
private
|
|
373
367
|
|
|
374
|
-
# Get the opposite side of the current
|
|
368
|
+
# Get the opposite side of the current identifier
|
|
375
369
|
#
|
|
376
370
|
# @return [Symbol] :first if current side is :second, :second if current side is :first
|
|
377
371
|
def opposite_side
|
data/lib/sashite/pin.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "pin/
|
|
3
|
+
require_relative "pin/identifier"
|
|
4
4
|
|
|
5
5
|
module Sashite
|
|
6
6
|
# PIN (Piece Identifier Notation) implementation for Ruby
|
|
@@ -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
|
|
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,38 +31,36 @@ 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?(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
pin.match?(PIN_REGEX)
|
|
34
|
+
def self.valid?(pin_string)
|
|
35
|
+
Identifier.valid?(pin_string)
|
|
42
36
|
end
|
|
43
37
|
|
|
44
|
-
# Parse a PIN string into
|
|
38
|
+
# Parse a PIN string into an Identifier object
|
|
45
39
|
#
|
|
46
40
|
# @param pin_string [String] PIN notation string
|
|
47
|
-
# @return [Pin::
|
|
41
|
+
# @return [Pin::Identifier] new identifier instance
|
|
48
42
|
# @raise [ArgumentError] if the PIN string is invalid
|
|
49
43
|
# @example
|
|
50
|
-
# Sashite::Pin.parse("K") # => #<Pin::
|
|
51
|
-
# Sashite::Pin.parse("+R") # => #<Pin::
|
|
52
|
-
# Sashite::Pin.parse("-p") # => #<Pin::
|
|
44
|
+
# Sashite::Pin.parse("K") # => #<Pin::Identifier type=:K side=:first state=:normal>
|
|
45
|
+
# Sashite::Pin.parse("+R") # => #<Pin::Identifier type=:R side=:first state=:enhanced>
|
|
46
|
+
# Sashite::Pin.parse("-p") # => #<Pin::Identifier type=:P side=:second state=:diminished>
|
|
53
47
|
def self.parse(pin_string)
|
|
54
|
-
|
|
48
|
+
Identifier.parse(pin_string)
|
|
55
49
|
end
|
|
56
50
|
|
|
57
|
-
# Create a new
|
|
51
|
+
# Create a new identifier instance
|
|
58
52
|
#
|
|
59
53
|
# @param type [Symbol] piece type (:A to :Z)
|
|
60
54
|
# @param side [Symbol] player side (:first or :second)
|
|
61
55
|
# @param state [Symbol] piece state (:normal, :enhanced, or :diminished)
|
|
62
|
-
# @return [Pin::
|
|
56
|
+
# @return [Pin::Identifier] new identifier instance
|
|
63
57
|
# @raise [ArgumentError] if parameters are invalid
|
|
64
58
|
# @example
|
|
65
|
-
# Sashite::Pin.
|
|
66
|
-
# Sashite::Pin.
|
|
67
|
-
# Sashite::Pin.
|
|
68
|
-
def self.
|
|
69
|
-
|
|
59
|
+
# Sashite::Pin.identifier(:K, :first, :normal) # => #<Pin::Identifier type=:K side=:first state=:normal>
|
|
60
|
+
# Sashite::Pin.identifier(:R, :first, :enhanced) # => #<Pin::Identifier type=:R side=:first state=:enhanced>
|
|
61
|
+
# Sashite::Pin.identifier(:P, :second, :diminished) # => #<Pin::Identifier type=:P side=:second state=:diminished>
|
|
62
|
+
def self.identifier(type, side, state = :normal)
|
|
63
|
+
Identifier.new(type, side, state)
|
|
70
64
|
end
|
|
71
65
|
end
|
|
72
66
|
end
|
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:
|
|
4
|
+
version: 3.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Cyril Kato
|
|
@@ -10,13 +10,13 @@ cert_chain: []
|
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies: []
|
|
12
12
|
description: |
|
|
13
|
-
PIN (Piece Identifier Notation) provides
|
|
13
|
+
PIN (Piece Identifier Notation) provides a rule-agnostic format for identifying pieces
|
|
14
14
|
in abstract strategy board games. This gem implements the PIN Specification v1.0.0 with
|
|
15
|
-
a modern Ruby interface featuring immutable
|
|
16
|
-
principles. PIN
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
and
|
|
15
|
+
a modern Ruby interface featuring immutable identifier objects and functional programming
|
|
16
|
+
principles. PIN uses single ASCII letters with optional state modifiers and case-based
|
|
17
|
+
side encoding (A-Z for first player, a-z for second player), enabling precise and portable
|
|
18
|
+
identification of pieces across multiple games. Perfect for game engines, board game notation
|
|
19
|
+
systems, and hybrid gaming platforms requiring compact, stateful piece representation.
|
|
20
20
|
email: contact@cyril.email
|
|
21
21
|
executables: []
|
|
22
22
|
extensions: []
|
|
@@ -26,7 +26,7 @@ files:
|
|
|
26
26
|
- README.md
|
|
27
27
|
- lib/sashite-pin.rb
|
|
28
28
|
- lib/sashite/pin.rb
|
|
29
|
-
- lib/sashite/pin/
|
|
29
|
+
- lib/sashite/pin/identifier.rb
|
|
30
30
|
homepage: https://github.com/sashite/pin.rb
|
|
31
31
|
licenses:
|
|
32
32
|
- MIT
|
|
@@ -53,6 +53,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
53
53
|
requirements: []
|
|
54
54
|
rubygems_version: 3.6.9
|
|
55
55
|
specification_version: 4
|
|
56
|
-
summary: PIN (Piece Identifier Notation) implementation for Ruby with immutable
|
|
56
|
+
summary: PIN (Piece Identifier Notation) implementation for Ruby with immutable identifier
|
|
57
57
|
objects
|
|
58
58
|
test_files: []
|