sashite-pnn 3.0.0 → 3.1.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 +94 -41
- data/lib/sashite/pnn/name.rb +53 -26
- metadata +6 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 85b86d223876b87d9bdcf29bc1484a32b667038bc378b1b9188aea72f4f2f66d
|
|
4
|
+
data.tar.gz: 27793a4f9a79241ffedbcf69f3ed4c3f6dc0293928318c308331d21eafb7df0c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 13c0726586676a5ecf632126879761198c9ca7be438aaf58720c5e469b3283c3782bc51a4e8315ee91f14fc57501c705860d7fc225de175b43526014d1464239
|
|
7
|
+
data.tar.gz: d654455e116883dfed83ac75ca22906ae42480eafa656f1f39f908ee0d5a79da7974ac2b421c0e8bdcaa8615650666fa1de1c39c9e149a7da079a7c127931be8
|
data/README.md
CHANGED
|
@@ -9,19 +9,17 @@
|
|
|
9
9
|
|
|
10
10
|
## What is PNN?
|
|
11
11
|
|
|
12
|
-
PNN (Piece Name Notation) is a formal, rule-agnostic naming system for identifying **pieces** in abstract strategy board games such as chess,
|
|
12
|
+
PNN (Piece Name Notation) is a formal, rule-agnostic naming system for identifying **pieces** in abstract strategy board games such as chess, shōgi, xiangqi, and their many variants. Each piece is represented by a canonical, human-readable ASCII name with optional state modifiers and optional terminal markers (e.g., `"KING"`, `"queen"`, `"+ROOK"`, `"-pawn"`, `"KING^"`).
|
|
13
13
|
|
|
14
|
-
This gem implements the [PNN Specification v1.0.0](https://sashite.dev/specs/pnn/1.0.0/), supporting validation, parsing, and comparison of piece names with integrated state management.
|
|
14
|
+
This gem implements the [PNN Specification v1.0.0](https://sashite.dev/specs/pnn/1.0.0/), supporting validation, parsing, and comparison of piece names with integrated state management and terminal piece identification.
|
|
15
15
|
|
|
16
16
|
## Installation
|
|
17
|
-
|
|
18
17
|
```ruby
|
|
19
18
|
# In your Gemfile
|
|
20
19
|
gem "sashite-pnn"
|
|
21
20
|
```
|
|
22
21
|
|
|
23
22
|
Or install manually:
|
|
24
|
-
|
|
25
23
|
```sh
|
|
26
24
|
gem install sashite-pnn
|
|
27
25
|
```
|
|
@@ -29,7 +27,6 @@ gem install sashite-pnn
|
|
|
29
27
|
## Usage
|
|
30
28
|
|
|
31
29
|
### Basic Operations
|
|
32
|
-
|
|
33
30
|
```ruby
|
|
34
31
|
require "sashite/pnn"
|
|
35
32
|
|
|
@@ -47,10 +44,11 @@ Sashite::Pnn.valid?("BISHOP") # => true
|
|
|
47
44
|
Sashite::Pnn.valid?("King") # => false (mixed case not allowed)
|
|
48
45
|
Sashite::Pnn.valid?("+ROOK") # => true (enhanced state)
|
|
49
46
|
Sashite::Pnn.valid?("-pawn") # => true (diminished state)
|
|
47
|
+
Sashite::Pnn.valid?("KING^") # => true (terminal piece)
|
|
48
|
+
Sashite::Pnn.valid?("+KING^") # => true (enhanced terminal piece)
|
|
50
49
|
```
|
|
51
50
|
|
|
52
51
|
### State Modifiers
|
|
53
|
-
|
|
54
52
|
```ruby
|
|
55
53
|
# Enhanced pieces (+ prefix)
|
|
56
54
|
enhanced = Sashite::Pnn.parse("+QUEEN")
|
|
@@ -70,39 +68,69 @@ normal.enhanced? # => false
|
|
|
70
68
|
normal.diminished? # => false
|
|
71
69
|
```
|
|
72
70
|
|
|
73
|
-
###
|
|
71
|
+
### Terminal Markers
|
|
72
|
+
```ruby
|
|
73
|
+
# Terminal pieces (^ suffix)
|
|
74
|
+
terminal = Sashite::Pnn.parse("KING^")
|
|
75
|
+
terminal.terminal? # => true
|
|
76
|
+
terminal.base_name # => "KING"
|
|
77
|
+
|
|
78
|
+
# Non-terminal pieces (no suffix)
|
|
79
|
+
non_terminal = Sashite::Pnn.parse("PAWN")
|
|
80
|
+
non_terminal.terminal? # => false
|
|
81
|
+
|
|
82
|
+
# Combined state and terminal marker
|
|
83
|
+
enhanced_terminal = Sashite::Pnn.parse("+ROOK^")
|
|
84
|
+
enhanced_terminal.enhanced? # => true
|
|
85
|
+
enhanced_terminal.terminal? # => true
|
|
86
|
+
enhanced_terminal.base_name # => "ROOK"
|
|
87
|
+
|
|
88
|
+
diminished_terminal = Sashite::Pnn.parse("-king^")
|
|
89
|
+
diminished_terminal.diminished? # => true
|
|
90
|
+
diminished_terminal.terminal? # => true
|
|
91
|
+
diminished_terminal.base_name # => "king"
|
|
92
|
+
```
|
|
74
93
|
|
|
94
|
+
### Player Assignment
|
|
75
95
|
```ruby
|
|
76
96
|
# First player pieces (uppercase)
|
|
77
97
|
first_player = Sashite::Pnn.parse("KING")
|
|
78
98
|
first_player.first_player? # => true
|
|
79
99
|
first_player.second_player? # => false
|
|
80
100
|
|
|
101
|
+
first_player_terminal = Sashite::Pnn.parse("KING^")
|
|
102
|
+
first_player_terminal.first_player? # => true
|
|
103
|
+
first_player_terminal.terminal? # => true
|
|
104
|
+
|
|
81
105
|
# Second player pieces (lowercase)
|
|
82
106
|
second_player = Sashite::Pnn.parse("king")
|
|
83
107
|
second_player.first_player? # => false
|
|
84
108
|
second_player.second_player? # => true
|
|
109
|
+
|
|
110
|
+
second_player_terminal = Sashite::Pnn.parse("king^")
|
|
111
|
+
second_player_terminal.second_player? # => true
|
|
112
|
+
second_player_terminal.terminal? # => true
|
|
85
113
|
```
|
|
86
114
|
|
|
87
115
|
### Normalization and Comparison
|
|
88
|
-
|
|
89
116
|
```ruby
|
|
90
117
|
a = Sashite::Pnn.parse("ROOK")
|
|
91
118
|
b = Sashite::Pnn.parse("ROOK")
|
|
92
119
|
|
|
93
120
|
a == b # => true
|
|
94
121
|
a.same_base_name?(Sashite::Pnn.parse("rook")) # => true (same piece, different player)
|
|
95
|
-
a.
|
|
122
|
+
a.same_base_name?(Sashite::Pnn.parse("ROOK^")) # => true (same piece, terminal marker)
|
|
123
|
+
a.same_base_name?(Sashite::Pnn.parse("+rook")) # => true (same piece, different state)
|
|
124
|
+
a.to_s # => "ROOK"
|
|
96
125
|
```
|
|
97
126
|
|
|
98
127
|
### Collections and Filtering
|
|
99
|
-
|
|
100
128
|
```ruby
|
|
101
|
-
pieces = %w[KING queen +ROOK -pawn BISHOP knight].map { |n| Sashite::Pnn.parse(n) }
|
|
129
|
+
pieces = %w[KING^ queen +ROOK -pawn BISHOP knight^ GENERAL^].map { |n| Sashite::Pnn.parse(n) }
|
|
102
130
|
|
|
103
131
|
# Filter by player
|
|
104
132
|
first_player_pieces = pieces.select(&:first_player?).map(&:to_s)
|
|
105
|
-
# => ["KING", "+ROOK", "BISHOP"]
|
|
133
|
+
# => ["KING^", "+ROOK", "BISHOP", "GENERAL^"]
|
|
106
134
|
|
|
107
135
|
# Filter by state
|
|
108
136
|
enhanced_pieces = pieces.select(&:enhanced?).map(&:to_s)
|
|
@@ -110,24 +138,33 @@ enhanced_pieces = pieces.select(&:enhanced?).map(&:to_s)
|
|
|
110
138
|
|
|
111
139
|
diminished_pieces = pieces.select(&:diminished?).map(&:to_s)
|
|
112
140
|
# => ["-pawn"]
|
|
141
|
+
|
|
142
|
+
# Filter by terminal status
|
|
143
|
+
terminal_pieces = pieces.select(&:terminal?).map(&:to_s)
|
|
144
|
+
# => ["KING^", "knight^", "GENERAL^"]
|
|
145
|
+
|
|
146
|
+
# Combine filters
|
|
147
|
+
first_player_terminals = pieces.select { |p| p.first_player? && p.terminal? }.map(&:to_s)
|
|
148
|
+
# => ["KING^", "GENERAL^"]
|
|
113
149
|
```
|
|
114
150
|
|
|
115
151
|
## Format Specification
|
|
116
152
|
|
|
117
153
|
### Structure
|
|
118
|
-
|
|
119
154
|
```
|
|
120
|
-
<state-modifier
|
|
155
|
+
[<state-modifier>]<piece-name>[<terminal-marker>]
|
|
121
156
|
```
|
|
122
157
|
|
|
123
158
|
Where:
|
|
124
159
|
- `<state-modifier>` is optional `+` (enhanced) or `-` (diminished)
|
|
125
160
|
- `<piece-name>` is case-consistent alphabetic characters
|
|
161
|
+
- `<terminal-marker>` is optional `^` (terminal piece)
|
|
126
162
|
|
|
127
163
|
### Grammar (BNF)
|
|
128
|
-
|
|
129
164
|
```bnf
|
|
130
|
-
<pnn> ::= <state-modifier> <name-body>
|
|
165
|
+
<pnn> ::= <state-modifier> <name-body> <terminal-marker>
|
|
166
|
+
| <state-modifier> <name-body>
|
|
167
|
+
| <name-body> <terminal-marker>
|
|
131
168
|
| <name-body>
|
|
132
169
|
|
|
133
170
|
<state-modifier> ::= "+" | "-"
|
|
@@ -137,20 +174,22 @@ Where:
|
|
|
137
174
|
<uppercase-name> ::= <uppercase-letter>+
|
|
138
175
|
<lowercase-name> ::= <lowercase-letter>+
|
|
139
176
|
|
|
177
|
+
<terminal-marker> ::= "^"
|
|
178
|
+
|
|
140
179
|
<uppercase-letter> ::= "A" | "B" | "C" | ... | "Z"
|
|
141
180
|
<lowercase-letter> ::= "a" | "b" | "c" | ... | "z"
|
|
142
181
|
```
|
|
143
182
|
|
|
144
183
|
### Regular Expression
|
|
145
|
-
|
|
146
184
|
```ruby
|
|
147
|
-
/\A[+-]?([A-Z]+|[a-z]+)
|
|
185
|
+
/\A[+-]?([A-Z]+|[a-z]+)\^?\z/
|
|
148
186
|
```
|
|
149
187
|
|
|
150
188
|
## Design Principles
|
|
151
189
|
|
|
152
190
|
* **Human-readable**: Names like `"KING"` or `"queen"` are intuitive and descriptive.
|
|
153
191
|
* **State-aware**: Integrated state management through `+` and `-` modifiers.
|
|
192
|
+
* **Terminal-aware**: Explicit identification of terminal pieces through `^` marker.
|
|
154
193
|
* **Rule-agnostic**: Independent of specific game mechanics.
|
|
155
194
|
* **Case-consistent**: Visual distinction between players through case.
|
|
156
195
|
* **Canonical**: One valid name per piece state within a given context.
|
|
@@ -160,55 +199,69 @@ Where:
|
|
|
160
199
|
|
|
161
200
|
PNN names serve as the formal source for PIN character identifiers. For example:
|
|
162
201
|
|
|
163
|
-
| PNN
|
|
164
|
-
|
|
|
165
|
-
| `KING`
|
|
166
|
-
| `king`
|
|
167
|
-
|
|
|
168
|
-
|
|
|
202
|
+
| PNN | PIN | Description |
|
|
203
|
+
| ---------- | -------- | ----------- |
|
|
204
|
+
| `KING` | `K` | First player king |
|
|
205
|
+
| `king` | `k` | Second player king |
|
|
206
|
+
| `KING^` | `K^` | Terminal first player king |
|
|
207
|
+
| `king^` | `k^` | Terminal second player king |
|
|
208
|
+
| `+ROOK` | `+R` | Enhanced first player rook |
|
|
209
|
+
| `+ROOK^` | `+R^` | Enhanced terminal first player rook |
|
|
210
|
+
| `-pawn` | `-p` | Diminished second player pawn |
|
|
211
|
+
| `-pawn^` | `-p^` | Diminished terminal second player pawn |
|
|
169
212
|
|
|
170
|
-
Multiple PNN names may map to the same PIN character (e.g., `"KING"` and `"KHAN"` both
|
|
213
|
+
Multiple PNN names may map to the same PIN character (e.g., `"KING"` and `"KHAN"` both → `K`), but PNN provides unambiguous naming within broader contexts.
|
|
171
214
|
|
|
172
215
|
## Examples
|
|
173
|
-
|
|
174
216
|
```ruby
|
|
175
217
|
# Traditional pieces
|
|
176
218
|
Sashite::Pnn.parse("KING") # => #<Pnn::Name value="KING">
|
|
177
219
|
Sashite::Pnn.parse("queen") # => #<Pnn::Name value="queen">
|
|
178
220
|
|
|
221
|
+
# Terminal pieces
|
|
222
|
+
Sashite::Pnn.parse("KING^") # => #<Pnn::Name value="KING^">
|
|
223
|
+
Sashite::Pnn.parse("general^") # => #<Pnn::Name value="general^">
|
|
224
|
+
|
|
179
225
|
# State modifiers
|
|
180
226
|
Sashite::Pnn.parse("+ROOK") # => #<Pnn::Name value="+ROOK">
|
|
181
227
|
Sashite::Pnn.parse("-pawn") # => #<Pnn::Name value="-pawn">
|
|
182
228
|
|
|
229
|
+
# Combined modifiers
|
|
230
|
+
Sashite::Pnn.parse("+KING^") # => #<Pnn::Name value="+KING^">
|
|
231
|
+
Sashite::Pnn.parse("-pawn^") # => #<Pnn::Name value="-pawn^">
|
|
232
|
+
|
|
183
233
|
# Validation
|
|
184
234
|
Sashite::Pnn.valid?("BISHOP") # => true
|
|
235
|
+
Sashite::Pnn.valid?("KING^") # => true
|
|
236
|
+
Sashite::Pnn.valid?("+ROOK^") # => true
|
|
185
237
|
Sashite::Pnn.valid?("Bishop") # => false (mixed case)
|
|
186
238
|
Sashite::Pnn.valid?("KING1") # => false (no digits allowed)
|
|
239
|
+
Sashite::Pnn.valid?("^KING") # => false (terminal marker must be suffix)
|
|
187
240
|
```
|
|
188
241
|
|
|
189
242
|
## API Reference
|
|
190
243
|
|
|
191
244
|
### Main Module
|
|
192
245
|
|
|
193
|
-
* `Sashite::Pnn.valid?(str)`
|
|
194
|
-
* `Sashite::Pnn.parse(str)`
|
|
195
|
-
* `Sashite::Pnn.name(sym_or_str)`
|
|
246
|
+
* `Sashite::Pnn.valid?(str)` — Returns `true` if the string is valid PNN.
|
|
247
|
+
* `Sashite::Pnn.parse(str)` — Returns a `Sashite::Pnn::Name` object.
|
|
248
|
+
* `Sashite::Pnn.name(sym_or_str)` — Alias for constructing a name.
|
|
196
249
|
|
|
197
250
|
### `Sashite::Pnn::Name`
|
|
198
251
|
|
|
199
|
-
* `#value`
|
|
200
|
-
* `#to_s`
|
|
201
|
-
* `#base_name`
|
|
202
|
-
* `#enhanced?`
|
|
203
|
-
* `#diminished?`
|
|
204
|
-
* `#normal?`
|
|
205
|
-
* `#
|
|
206
|
-
* `#
|
|
207
|
-
* `#
|
|
208
|
-
*
|
|
252
|
+
* `#value` — Returns the canonical string value.
|
|
253
|
+
* `#to_s` — Returns the string representation.
|
|
254
|
+
* `#base_name` — Returns the name without state modifier or terminal marker.
|
|
255
|
+
* `#enhanced?` — Returns `true` if piece has enhanced state (`+` prefix).
|
|
256
|
+
* `#diminished?` — Returns `true` if piece has diminished state (`-` prefix).
|
|
257
|
+
* `#normal?` — Returns `true` if piece has normal state (no prefix).
|
|
258
|
+
* `#terminal?` — Returns `true` if piece is a terminal piece (`^` suffix).
|
|
259
|
+
* `#first_player?` — Returns `true` if piece belongs to first player (uppercase).
|
|
260
|
+
* `#second_player?` — Returns `true` if piece belongs to second player (lowercase).
|
|
261
|
+
* `#same_base_name?(other)` — Returns `true` if both pieces have same base name.
|
|
262
|
+
* `#==`, `#eql?`, `#hash` — Value-based equality.
|
|
209
263
|
|
|
210
264
|
## Development
|
|
211
|
-
|
|
212
265
|
```sh
|
|
213
266
|
# Clone the repository
|
|
214
267
|
git clone https://github.com/sashite/pnn.rb.git
|
|
@@ -240,4 +293,4 @@ Available as open source under the [MIT License](https://opensource.org/licenses
|
|
|
240
293
|
|
|
241
294
|
## About
|
|
242
295
|
|
|
243
|
-
Maintained by [Sashité](https://sashite.com/)
|
|
296
|
+
Maintained by [Sashité](https://sashite.com/) — promoting chess variants and sharing the beauty of board game cultures.
|
data/lib/sashite/pnn/name.rb
CHANGED
|
@@ -6,13 +6,14 @@ module Sashite
|
|
|
6
6
|
#
|
|
7
7
|
# PNN provides a canonical naming system for abstract strategy game pieces.
|
|
8
8
|
# Each name consists of an optional state modifier (+ or -) followed by a
|
|
9
|
-
# case-consistent alphabetic name
|
|
10
|
-
#
|
|
9
|
+
# case-consistent alphabetic name and an optional terminal marker (^),
|
|
10
|
+
# encoding piece identity, player assignment, state, and terminal status
|
|
11
|
+
# in a human-readable format.
|
|
11
12
|
#
|
|
12
13
|
# All instances are immutable.
|
|
13
14
|
class Name
|
|
14
15
|
# PNN validation pattern matching the specification
|
|
15
|
-
PNN_PATTERN = /\A([+-]?)([A-Z]+|[a-z]+)\z/
|
|
16
|
+
PNN_PATTERN = /\A([+-]?)([A-Z]+|[a-z]+)(\^?)\z/
|
|
16
17
|
|
|
17
18
|
# Error messages
|
|
18
19
|
ERROR_INVALID_NAME = "Invalid PNN string: %s"
|
|
@@ -22,7 +23,7 @@ module Sashite
|
|
|
22
23
|
|
|
23
24
|
# Create a new piece name instance
|
|
24
25
|
#
|
|
25
|
-
# @param name [String, Symbol] the piece name (e.g., "KING", :queen, "+ROOK", "-pawn")
|
|
26
|
+
# @param name [String, Symbol] the piece name (e.g., "KING", :queen, "+ROOK", "-pawn", "KING^")
|
|
26
27
|
# @raise [ArgumentError] if the name does not match PNN pattern
|
|
27
28
|
def initialize(name)
|
|
28
29
|
string_value = name.to_s
|
|
@@ -40,8 +41,10 @@ module Sashite
|
|
|
40
41
|
# @raise [ArgumentError] if the string is invalid
|
|
41
42
|
#
|
|
42
43
|
# @example
|
|
43
|
-
# Sashite::Pnn::Name.parse("KING")
|
|
44
|
-
# Sashite::Pnn::Name.parse("+queen")
|
|
44
|
+
# Sashite::Pnn::Name.parse("KING") # => #<Pnn::Name value="KING">
|
|
45
|
+
# Sashite::Pnn::Name.parse("+queen") # => #<Pnn::Name value="+queen">
|
|
46
|
+
# Sashite::Pnn::Name.parse("KING^") # => #<Pnn::Name value="KING^">
|
|
47
|
+
# Sashite::Pnn::Name.parse("+ROOK^") # => #<Pnn::Name value="+ROOK^">
|
|
45
48
|
def self.parse(string)
|
|
46
49
|
new(string)
|
|
47
50
|
end
|
|
@@ -55,6 +58,7 @@ module Sashite
|
|
|
55
58
|
# Sashite::Pnn::Name.valid?("KING") # => true
|
|
56
59
|
# Sashite::Pnn::Name.valid?("King") # => false (mixed case)
|
|
57
60
|
# Sashite::Pnn::Name.valid?("+queen") # => true
|
|
61
|
+
# Sashite::Pnn::Name.valid?("KING^") # => true
|
|
58
62
|
# Sashite::Pnn::Name.valid?("KING1") # => false (contains digit)
|
|
59
63
|
def self.valid?(string)
|
|
60
64
|
string.is_a?(::String) && string.match?(PNN_PATTERN)
|
|
@@ -75,14 +79,16 @@ module Sashite
|
|
|
75
79
|
value
|
|
76
80
|
end
|
|
77
81
|
|
|
78
|
-
# Returns the base name without state modifier
|
|
82
|
+
# Returns the base name without state modifier or terminal marker
|
|
79
83
|
#
|
|
80
|
-
# @return [String] the piece name without + or - prefix
|
|
84
|
+
# @return [String] the piece name without + or - prefix and without ^ suffix
|
|
81
85
|
#
|
|
82
86
|
# @example
|
|
83
|
-
# Sashite::Pnn::Name.parse("KING").base_name
|
|
84
|
-
# Sashite::Pnn::Name.parse("+queen").base_name
|
|
85
|
-
# Sashite::Pnn::Name.parse("-ROOK").base_name
|
|
87
|
+
# Sashite::Pnn::Name.parse("KING").base_name # => "KING"
|
|
88
|
+
# Sashite::Pnn::Name.parse("+queen").base_name # => "queen"
|
|
89
|
+
# Sashite::Pnn::Name.parse("-ROOK").base_name # => "ROOK"
|
|
90
|
+
# Sashite::Pnn::Name.parse("KING^").base_name # => "KING"
|
|
91
|
+
# Sashite::Pnn::Name.parse("+ROOK^").base_name # => "ROOK"
|
|
86
92
|
def base_name
|
|
87
93
|
@parsed[:base_name]
|
|
88
94
|
end
|
|
@@ -92,8 +98,9 @@ module Sashite
|
|
|
92
98
|
# @return [Boolean] true if enhanced, false otherwise
|
|
93
99
|
#
|
|
94
100
|
# @example
|
|
95
|
-
# Sashite::Pnn::Name.parse("+KING").enhanced?
|
|
96
|
-
# Sashite::Pnn::Name.parse("KING").enhanced?
|
|
101
|
+
# Sashite::Pnn::Name.parse("+KING").enhanced? # => true
|
|
102
|
+
# Sashite::Pnn::Name.parse("KING").enhanced? # => false
|
|
103
|
+
# Sashite::Pnn::Name.parse("+KING^").enhanced? # => true
|
|
97
104
|
def enhanced?
|
|
98
105
|
@parsed[:state_modifier] == "+"
|
|
99
106
|
end
|
|
@@ -103,8 +110,9 @@ module Sashite
|
|
|
103
110
|
# @return [Boolean] true if diminished, false otherwise
|
|
104
111
|
#
|
|
105
112
|
# @example
|
|
106
|
-
# Sashite::Pnn::Name.parse("-pawn").diminished?
|
|
107
|
-
# Sashite::Pnn::Name.parse("pawn").diminished?
|
|
113
|
+
# Sashite::Pnn::Name.parse("-pawn").diminished? # => true
|
|
114
|
+
# Sashite::Pnn::Name.parse("pawn").diminished? # => false
|
|
115
|
+
# Sashite::Pnn::Name.parse("-pawn^").diminished? # => true
|
|
108
116
|
def diminished?
|
|
109
117
|
@parsed[:state_modifier] == "-"
|
|
110
118
|
end
|
|
@@ -114,19 +122,34 @@ module Sashite
|
|
|
114
122
|
# @return [Boolean] true if normal, false otherwise
|
|
115
123
|
#
|
|
116
124
|
# @example
|
|
117
|
-
# Sashite::Pnn::Name.parse("KING").normal?
|
|
118
|
-
# Sashite::Pnn::Name.parse("+KING").normal?
|
|
125
|
+
# Sashite::Pnn::Name.parse("KING").normal? # => true
|
|
126
|
+
# Sashite::Pnn::Name.parse("+KING").normal? # => false
|
|
127
|
+
# Sashite::Pnn::Name.parse("KING^").normal? # => true
|
|
119
128
|
def normal?
|
|
120
129
|
@parsed[:state_modifier].empty?
|
|
121
130
|
end
|
|
122
131
|
|
|
132
|
+
# Check if the piece is a terminal piece (has ^ marker)
|
|
133
|
+
#
|
|
134
|
+
# @return [Boolean] true if terminal, false otherwise
|
|
135
|
+
#
|
|
136
|
+
# @example
|
|
137
|
+
# Sashite::Pnn::Name.parse("KING^").terminal? # => true
|
|
138
|
+
# Sashite::Pnn::Name.parse("KING").terminal? # => false
|
|
139
|
+
# Sashite::Pnn::Name.parse("+KING^").terminal? # => true
|
|
140
|
+
# Sashite::Pnn::Name.parse("-pawn^").terminal? # => true
|
|
141
|
+
def terminal?
|
|
142
|
+
@parsed[:terminal_marker] == "^"
|
|
143
|
+
end
|
|
144
|
+
|
|
123
145
|
# Check if the piece belongs to the first player (uppercase)
|
|
124
146
|
#
|
|
125
147
|
# @return [Boolean] true if first player, false otherwise
|
|
126
148
|
#
|
|
127
149
|
# @example
|
|
128
|
-
# Sashite::Pnn::Name.parse("KING").first_player?
|
|
129
|
-
# Sashite::Pnn::Name.parse("queen").first_player?
|
|
150
|
+
# Sashite::Pnn::Name.parse("KING").first_player? # => true
|
|
151
|
+
# Sashite::Pnn::Name.parse("queen").first_player? # => false
|
|
152
|
+
# Sashite::Pnn::Name.parse("KING^").first_player? # => true
|
|
130
153
|
def first_player?
|
|
131
154
|
@parsed[:base_name] == @parsed[:base_name].upcase
|
|
132
155
|
end
|
|
@@ -136,13 +159,14 @@ module Sashite
|
|
|
136
159
|
# @return [Boolean] true if second player, false otherwise
|
|
137
160
|
#
|
|
138
161
|
# @example
|
|
139
|
-
# Sashite::Pnn::Name.parse("king").second_player?
|
|
140
|
-
# Sashite::Pnn::Name.parse("QUEEN").second_player?
|
|
162
|
+
# Sashite::Pnn::Name.parse("king").second_player? # => true
|
|
163
|
+
# Sashite::Pnn::Name.parse("QUEEN").second_player? # => false
|
|
164
|
+
# Sashite::Pnn::Name.parse("king^").second_player? # => true
|
|
141
165
|
def second_player?
|
|
142
166
|
@parsed[:base_name] == @parsed[:base_name].downcase
|
|
143
167
|
end
|
|
144
168
|
|
|
145
|
-
# Check if another piece has the same base name (ignoring case and
|
|
169
|
+
# Check if another piece has the same base name (ignoring case, state, and terminal marker)
|
|
146
170
|
#
|
|
147
171
|
# @param other [Name] another piece name to compare
|
|
148
172
|
# @return [Boolean] true if same base name, false otherwise
|
|
@@ -151,9 +175,11 @@ module Sashite
|
|
|
151
175
|
# king = Sashite::Pnn::Name.parse("KING")
|
|
152
176
|
# queen = Sashite::Pnn::Name.parse("king")
|
|
153
177
|
# enhanced = Sashite::Pnn::Name.parse("+KING")
|
|
178
|
+
# terminal = Sashite::Pnn::Name.parse("KING^")
|
|
154
179
|
#
|
|
155
|
-
# king.same_base_name?(queen)
|
|
156
|
-
# king.same_base_name?(enhanced)
|
|
180
|
+
# king.same_base_name?(queen) # => true (same piece, different player)
|
|
181
|
+
# king.same_base_name?(enhanced) # => true (same piece, different state)
|
|
182
|
+
# king.same_base_name?(terminal) # => true (same piece, terminal marker)
|
|
157
183
|
def same_base_name?(other)
|
|
158
184
|
return false unless other.is_a?(self.class)
|
|
159
185
|
|
|
@@ -187,8 +213,9 @@ module Sashite
|
|
|
187
213
|
def parse_components(str)
|
|
188
214
|
match = str.match(PNN_PATTERN)
|
|
189
215
|
{
|
|
190
|
-
state_modifier:
|
|
191
|
-
base_name:
|
|
216
|
+
state_modifier: match[1],
|
|
217
|
+
base_name: match[2],
|
|
218
|
+
terminal_marker: match[3]
|
|
192
219
|
}
|
|
193
220
|
end
|
|
194
221
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sashite-pnn
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Cyril Kato
|
|
@@ -13,9 +13,10 @@ description: |
|
|
|
13
13
|
PNN (Piece Name Notation) provides a rule-agnostic, scalable naming system for identifying
|
|
14
14
|
abstract strategy board game pieces. This gem implements the PNN Specification v1.0.0 with
|
|
15
15
|
a modern Ruby interface featuring immutable piece name objects and functional programming
|
|
16
|
-
principles. PNN uses canonical ASCII names with optional state modifiers
|
|
17
|
-
"+ROOK", "-pawn") to unambiguously
|
|
18
|
-
Ideal for engines, protocols, and tools
|
|
16
|
+
principles. PNN uses canonical ASCII names with optional state modifiers and optional terminal
|
|
17
|
+
markers (e.g., "KING", "queen", "+ROOK", "-pawn", "KING^", "+GENERAL^") to unambiguously
|
|
18
|
+
refer to game pieces across variants and traditions. Ideal for engines, protocols, and tools
|
|
19
|
+
that need clear and extensible piece identifiers.
|
|
19
20
|
email: contact@cyril.email
|
|
20
21
|
executables: []
|
|
21
22
|
extensions: []
|
|
@@ -50,7 +51,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
50
51
|
- !ruby/object:Gem::Version
|
|
51
52
|
version: '0'
|
|
52
53
|
requirements: []
|
|
53
|
-
rubygems_version: 3.7.
|
|
54
|
+
rubygems_version: 3.7.2
|
|
54
55
|
specification_version: 4
|
|
55
56
|
summary: PNN (Piece Name Notation) implementation for Ruby with immutable piece name
|
|
56
57
|
objects
|