sashite-snn 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.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +267 -73
  3. data/lib/sashite/snn/name.rb +97 -19
  4. data/lib/sashite/snn.rb +48 -14
  5. metadata +16 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 517b0310bd57b5d0b931a5f14eb1c4d6257bbd6d4f215c467ff475c8b42542fc
4
- data.tar.gz: 2fa5a177d6350dd2501b87eac5a567f5cf48c681439b3af23f2bae1df99579a3
3
+ metadata.gz: 1528b909fb1159a1b9d3fb06e4b62114efa110d991226945024f442fb08501e9
4
+ data.tar.gz: 3c6c44c3d69d1eacad6141d9309a27e5972dde45067bc8272e9584ed6cfc56ee
5
5
  SHA512:
6
- metadata.gz: 200ef38765b8c581f93c51657699e4b420c26abe1d355227ff47892b493f787ec9c03e7202a91b9466c99c97a95a1b914837e59bb2784ef6568cc2ca3f94dd49
7
- data.tar.gz: 6b01e73ad4e61246e8e61a4e0bd85f7b3f0fcb964f7532c42d6c95a2b5b665c64487e032e46cb7b77806b3a410137536812aac3278428b2c31af8d91811597ec
6
+ metadata.gz: fc20688ee514ec76125132aba5b121aa4b705230eb716f2abee6c5c1a88978aadfa52bf8cfa1c870315284050808f5fe5903a5e141c760af92f7aaca9bac82d5
7
+ data.tar.gz: b44e7af48ae53d52ca2b1961831b5eca677c2b8182589062139af966d3e960f0638720fba33c7e9099b6a8fe9424124f49386e3c9b025e71f93edfb797e9e558
data/README.md CHANGED
@@ -9,16 +9,20 @@
9
9
 
10
10
  ## What is SNN?
11
11
 
12
- SNN (Style Name Notation) is a formal, rule-agnostic naming system for identifying **styles** in abstract strategy board games such as chess, shōgi, xiangqi, and their many variants. Each style is represented by a canonical, human-readable ASCII name (e.g., `"Chess"`, `"Shogi"`, `"Xiangqi"`, `"Minishogi"`).
12
+ SNN (Style Name Notation) is a **foundational**, human-readable naming system for identifying game styles in abstract strategy board games. SNN serves as a primitive building block using descriptive alphabetic names with case encoding to represent style identity and player assignment.
13
13
 
14
- This gem implements the [SNN Specification v1.0.0](https://sashite.dev/specs/snn/1.0.0/), supporting validation, parsing, and comparison of style names.
14
+ Each SNN name is a case-consistent alphabetic identifier where:
15
+ - **Uppercase names** (e.g., `CHESS`, `SHOGI`) represent the **first player's** style
16
+ - **Lowercase names** (e.g., `chess`, `shogi`) represent the **second player's** style
17
+
18
+ This gem implements the [SNN Specification v1.0.0](https://sashite.dev/specs/snn/1.0.0/) as a foundational primitive with no dependencies.
15
19
 
16
20
  ## Installation
17
21
 
18
22
  ```ruby
19
23
  # In your Gemfile
20
24
  gem "sashite-snn"
21
- ````
25
+ ```
22
26
 
23
27
  Or install manually:
24
28
 
@@ -26,130 +30,320 @@ Or install manually:
26
30
  gem install sashite-snn
27
31
  ```
28
32
 
29
- ## Usage
30
-
31
- ### Basic Operations
33
+ ## Quick Start
32
34
 
33
35
  ```ruby
34
36
  require "sashite/snn"
35
37
 
36
38
  # Parse SNN strings into style name objects
37
- name = Sashite::Snn.parse("Shogi") # => #<Snn::Name value="Shogi">
38
- name.to_s # => "Shogi"
39
- name.value # => "Shogi"
39
+ name = Sashite::Snn.parse("SHOGI") # => #<Snn::Name value="SHOGI">
40
+ name.to_s # => "SHOGI"
41
+ name.value # => "SHOGI"
40
42
 
41
43
  # Create from string or symbol
42
- name = Sashite::Snn.name("Chess") # => #<Snn::Name value="Chess">
43
- name = Sashite::Snn::Name.new(:Xiangqi) # => #<Snn::Name value="Xiangqi">
44
+ name = Sashite::Snn.name("CHESS") # => #<Snn::Name value="CHESS">
45
+ name = Sashite::Snn::Name.new(:xiangqi) # => #<Snn::Name value="xiangqi">
44
46
 
45
47
  # Validate SNN strings
46
- Sashite::Snn.valid?("Go9x9") # => true
47
- Sashite::Snn.valid?("chess") # => false (must start with uppercase)
48
- Sashite::Snn.valid?("3DChess") # => false (invalid character)
48
+ Sashite::Snn.valid?("MAKRUK") # => true
49
+ Sashite::Snn.valid?("shogi") # => true
50
+ Sashite::Snn.valid?("Chess") # => false (mixed case)
51
+ Sashite::Snn.valid?("Chess960") # => false (contains digits)
52
+ ```
53
+
54
+ ## SNN Format
55
+
56
+ An SNN string consists of alphabetic characters only, all in the same case:
57
+
58
+ ```
59
+ <alphabetic-name>
60
+ ```
61
+
62
+ **Examples:**
63
+ - `CHESS` — Chess style for first player
64
+ - `chess` — Chess style for second player
65
+ - `SHOGI` — Shōgi style for first player
66
+ - `shogi` — Shōgi style for second player
67
+
68
+ ## Format Specification
69
+
70
+ ### Structure
71
+
72
+ ```
73
+ <alphabetic-name>
74
+ ```
75
+
76
+ Where the name directly represents the style identity and player assignment through case.
77
+
78
+ ### Grammar (BNF)
79
+
80
+ ```bnf
81
+ <snn> ::= <uppercase-name> | <lowercase-name>
82
+
83
+ <uppercase-name> ::= <uppercase-letter>+
84
+ <lowercase-name> ::= <lowercase-letter>+
85
+
86
+ <uppercase-letter> ::= "A" | "B" | "C" | ... | "Z"
87
+ <lowercase-letter> ::= "a" | "b" | "c" | ... | "z"
49
88
  ```
50
89
 
51
- ### Normalization and Comparison
90
+ ### Regular Expression
52
91
 
53
92
  ```ruby
54
- a = Sashite::Snn.parse("Chess960")
55
- b = Sashite::Snn.parse("Chess960")
93
+ /\A([A-Z]+|[a-z]+)\z/
94
+ ```
95
+
96
+ ### Constraints
97
+
98
+ 1. **Case consistency**: All letters must be either uppercase OR lowercase
99
+ 2. **Alphabetic only**: Only ASCII letters allowed (no digits, no special characters)
100
+ 3. **Direct assignment**: Names represent styles through explicit association
101
+
102
+ ## API Reference
103
+
104
+ ### Module Methods
56
105
 
57
- a == b # => true
58
- a.same_base_name?(Sashite::Snn.parse("Chess")) # => true if both resolve to same SIN
59
- a.to_s # => "Chess960"
106
+ #### `Sashite::Snn.valid?(string)`
107
+
108
+ Returns `true` if the string is valid SNN notation.
109
+
110
+ - **Parameter**: `string` (String) - String to validate
111
+ - **Returns**: `Boolean` - `true` if valid, `false` otherwise
112
+
113
+ ```ruby
114
+ Sashite::Snn.valid?("CHESS") # => true
115
+ Sashite::Snn.valid?("shogi") # => true
116
+ Sashite::Snn.valid?("Chess") # => false (mixed case)
117
+ Sashite::Snn.valid?("CHESS960") # => false (contains digits)
118
+ Sashite::Snn.valid?("3DChess") # => false (starts with digit)
60
119
  ```
61
120
 
62
- ### Canonical Representation
121
+ #### `Sashite::Snn.parse(string)`
122
+
123
+ Parses an SNN string into a `Name` object.
124
+
125
+ - **Parameter**: `string` (String) - SNN notation string
126
+ - **Returns**: `Name` - Immutable name object
127
+ - **Raises**: `ArgumentError` if the string is invalid
63
128
 
64
129
  ```ruby
65
- # All names are normalized to a canonical format
66
- name = Sashite::Snn.parse("Minishogi")
67
- name.value # => "Minishogi"
68
- name.to_s # => "Minishogi"
130
+ name = Sashite::Snn.parse("SHOGI") # => #<Snn::Name value="SHOGI">
131
+ name = Sashite::Snn.parse("chess") # => #<Snn::Name value="chess">
69
132
  ```
70
133
 
71
- ### Collections and Filtering
134
+ #### `Sashite::Snn.name(value)`
135
+
136
+ Creates a new `Name` instance directly.
137
+
138
+ - **Parameter**: `value` (String, Symbol) - Style name to construct
139
+ - **Returns**: `Name` - New name instance
140
+ - **Raises**: `ArgumentError` if name format is invalid
72
141
 
73
142
  ```ruby
74
- names = %w[Chess Shogi Makruk Antichess Minishogi].map { |n| Sashite::Snn.parse(n) }
143
+ Sashite::Snn.name("XIANGQI") # => #<Snn::Name value="XIANGQI">
144
+ Sashite::Snn.name(:makruk) # => #<Snn::Name value="makruk">
145
+ ```
75
146
 
76
- # Filter by prefix
77
- names.select { |n| n.value.start_with?("Mini") }.map(&:to_s)
78
- # => ["Minishogi"]
147
+ ### Name Object
148
+
149
+ The `Name` object is immutable and provides read-only access to the style name:
150
+
151
+ ```ruby
152
+ name = Sashite::Snn.parse("SHOGI")
153
+
154
+ name.value # => "SHOGI"
155
+ name.to_s # => "SHOGI"
156
+ name.frozen? # => true
79
157
  ```
80
158
 
81
- ## Format Specification
159
+ **Equality and hashing:**
160
+ ```ruby
161
+ name1 = Sashite::Snn.parse("CHESS")
162
+ name2 = Sashite::Snn.parse("CHESS")
163
+ name3 = Sashite::Snn.parse("chess")
82
164
 
83
- ### Structure
165
+ name1 == name2 # => true
166
+ name1.hash == name2.hash # => true
167
+ name1 == name3 # => false (different case = different player)
168
+ ```
169
+
170
+ ## Examples
84
171
 
172
+ ### Traditional Chess Family
173
+
174
+ ```ruby
175
+ # First player styles (uppercase)
176
+ chess = Sashite::Snn.parse("CHESS") # Western Chess
177
+ shogi = Sashite::Snn.parse("SHOGI") # Japanese Chess
178
+ xiangqi = Sashite::Snn.parse("XIANGQI") # Chinese Chess
179
+ makruk = Sashite::Snn.parse("MAKRUK") # Thai Chess
180
+
181
+ # Second player styles (lowercase)
182
+ chess_p2 = Sashite::Snn.parse("chess")
183
+ shogi_p2 = Sashite::Snn.parse("shogi")
85
184
  ```
86
- <uppercase-letter>[<lowercase-letter | digit>]*
185
+
186
+ ### Historical Games
187
+
188
+ ```ruby
189
+ chaturanga = Sashite::Snn.parse("CHATURANGA") # Ancient Indian Chess
190
+ shatranj = Sashite::Snn.parse("SHATRANJ") # Medieval Islamic Chess
87
191
  ```
88
192
 
89
- ### Grammar (BNF)
193
+ ### Modern Variants
90
194
 
91
- ```bnf
92
- <snn> ::= <uppercase-letter> <tail>
195
+ ```ruby
196
+ raumschach = Sashite::Snn.parse("RAUMSCHACH") # 3D Chess
197
+ omega = Sashite::Snn.parse("OMEGA") # Omega Chess
198
+ ```
93
199
 
94
- <tail> ::= "" ; Single letter (e.g., "X")
95
- | <alphanumeric-char> <tail> ; Extended name
200
+ ### Case Consistency
96
201
 
97
- <alphanumeric-char> ::= <lowercase-letter> | <digit>
202
+ ```ruby
203
+ # Valid - all uppercase
204
+ Sashite::Snn.valid?("CHESS") # => true
205
+ Sashite::Snn.valid?("XIANGQI") # => true
206
+
207
+ # Valid - all lowercase
208
+ Sashite::Snn.valid?("shogi") # => true
209
+ Sashite::Snn.valid?("makruk") # => true
210
+
211
+ # Invalid - mixed case
212
+ Sashite::Snn.valid?("Chess") # => false
213
+ Sashite::Snn.valid?("Shogi") # => false
214
+ Sashite::Snn.valid?("XiangQi") # => false
215
+
216
+ # Invalid - contains non-alphabetic characters
217
+ Sashite::Snn.valid?("CHESS960") # => false
218
+ Sashite::Snn.valid?("GO9X9") # => false
219
+ Sashite::Snn.valid?("MINI_SHOGI") # => false
220
+ ```
98
221
 
99
- <uppercase-letter> ::= "A" | "B" | "C" | ... | "Z"
100
- <lowercase-letter> ::= "a" | "b" | "c" | ... | "z"
101
- <digit> ::= "0" | "1" | "2" | "3" | ... | "9"
222
+ ### Working with Names
223
+
224
+ ```ruby
225
+ # Create and compare
226
+ name1 = Sashite::Snn.parse("SHOGI")
227
+ name2 = Sashite::Snn.parse("SHOGI")
228
+ name1 == name2 # => true
229
+
230
+ # String and symbol inputs
231
+ name1 = Sashite::Snn.name("XIANGQI")
232
+ name2 = Sashite::Snn.name(:XIANGQI)
233
+ name1 == name2 # => true
234
+
235
+ # Immutability
236
+ name = Sashite::Snn.parse("CHESS")
237
+ name.frozen? # => true
238
+ name.value.frozen? # => true
102
239
  ```
103
240
 
104
- ### Regular Expression
241
+ ### Collections
105
242
 
106
243
  ```ruby
107
- /\A[A-Z][a-z0-9]*\z/
244
+ # Create a set of styles
245
+ styles = %w[CHESS SHOGI XIANGQI MAKRUK].map { |n| Sashite::Snn.parse(n) }
246
+
247
+ # Filter by prefix
248
+ styles.select { |s| s.value.start_with?("X") }.map(&:to_s)
249
+ # => ["XIANGQI"]
250
+
251
+ # Use in hash
252
+ style_map = {
253
+ Sashite::Snn.parse("CHESS") => "Western Chess",
254
+ Sashite::Snn.parse("SHOGI") => "Japanese Chess"
255
+ }
108
256
  ```
109
257
 
110
- ## Design Principles
258
+ ## Relationship with SIN
111
259
 
112
- * **Human-readable**: Names like `"Shogi"` or `"Chess960"` are intuitive and descriptive.
113
- * **Canonical**: One valid name per game style within a given context.
114
- * **ASCII-only**: Compatible with all systems.
115
- * **Scalable**: Supports unlimited distinct names for current and future game variants.
260
+ **SNN and SIN are independent primitives** that serve complementary roles:
116
261
 
117
- ## Integration with SIN
262
+ - **SNN**: Human-readable, descriptive names (`CHESS`, `SHOGI`)
263
+ - **SIN**: Compact, single-character identification (`C`, `S`)
118
264
 
119
- SNN names serve as the formal source for SIN character identifiers. For example:
265
+ ### Optional Correspondence
120
266
 
121
- | SNN | SIN |
122
- | --------- | ------- |
123
- | `Chess` | `C`/`c` |
124
- | `Shogi` | `S`/`s` |
125
- | `Xiangqi` | `X`/`x` |
126
- | `Makruk` | `M`/`m` |
267
+ While both specifications can be used independently, they may be related through:
127
268
 
128
- Multiple SNN names may map to the same SIN character (e.g., `"Chess"` and `"Chess960"` both → `C`), but SNN provides unambiguous naming within broader contexts.
269
+ - **Mapping tables**: External context defining SNN SIN relationships
270
+ - **Case consistency**: When mapped, case must be preserved (`CHESS` ↔ `C`, `chess` ↔ `c`)
129
271
 
130
- ## Examples
272
+ ```ruby
273
+ # Example mapping (defined externally, not part of SNN)
274
+ SNN_TO_SIN = {
275
+ "CHESS" => "C",
276
+ "chess" => "c",
277
+ "SHOGI" => "S",
278
+ "shogi" => "s"
279
+ }
280
+
281
+ # Multiple SNN names may map to the same SIN
282
+ SNN_TO_SIN["CAPABLANCA"] = "C" # Also maps to "C"
283
+ SNN_TO_SIN["COURIER"] = "C" # Also maps to "C"
284
+ ```
285
+
286
+ ### Important Notes
287
+
288
+ 1. **No dependency**: SNN does not depend on SIN, nor SIN on SNN
289
+ 2. **Bidirectional mapping requires context**: Converting between SNN and SIN requires external mapping information
290
+ 3. **Independent usage**: Systems may use SNN alone, SIN alone, or both with defined mappings
291
+ 4. **Multiple mappings**: One SNN name may correspond to multiple SIN characters in different contexts, and vice versa
292
+
293
+ ## Error Handling
131
294
 
132
295
  ```ruby
133
- Sashite::Snn.parse("Chess") # => #<Snn::Name value="Chess">
134
- Sashite::Snn.parse("Chess960") # => #<Snn::Name value="Chess960">
135
- Sashite::Snn.valid?("Minishogi") # => true
136
- Sashite::Snn.valid?("miniShogi") # => false
296
+ begin
297
+ name = Sashite::Snn.parse("Chess960")
298
+ rescue ArgumentError => e
299
+ warn "Invalid SNN: #{e.message}"
300
+ # => "Invalid SNN string: \"Chess960\""
301
+ end
137
302
  ```
138
303
 
139
- ## API Reference
304
+ ### Common Errors
305
+
306
+ ```ruby
307
+ # Mixed case
308
+ Sashite::Snn.parse("Chess")
309
+ # => ArgumentError: Invalid SNN string: "Chess"
310
+
311
+ # Contains digits
312
+ Sashite::Snn.parse("CHESS960")
313
+ # => ArgumentError: Invalid SNN string: "CHESS960"
314
+
315
+ # Contains special characters
316
+ Sashite::Snn.parse("MINI_SHOGI")
317
+ # => ArgumentError: Invalid SNN string: "MINI_SHOGI"
318
+
319
+ # Empty string
320
+ Sashite::Snn.parse("")
321
+ # => ArgumentError: Invalid SNN string: ""
322
+ ```
323
+
324
+ ## Design Principles
325
+
326
+ - **Human-readable**: Descriptive names for better usability
327
+ - **Case-consistent**: Visual distinction between players through case
328
+ - **Foundational primitive**: Serves as building block for formal style identification
329
+ - **Rule-agnostic**: Independent of specific game mechanics
330
+ - **Self-contained**: No external dependencies
331
+ - **Immutable**: All objects are frozen and thread-safe
332
+ - **Canonical**: Each style has one valid representation per context
140
333
 
141
- ### Main Module
334
+ ## Properties
142
335
 
143
- * `Sashite::Snn.valid?(str)` Returns `true` if the string is valid SNN.
144
- * `Sashite::Snn.parse(str)` Returns a `Sashite::Snn::Name` object.
145
- * `Sashite::Snn.name(sym_or_str)` Alias for constructing a name.
336
+ - **Purely functional**: Immutable data structures, no side effects
337
+ - **Specification compliant**: Strict adherence to [SNN v1.0.0](https://sashite.dev/specs/snn/1.0.0/)
338
+ - **Minimal API**: Simple validation, parsing, and comparison
339
+ - **Universal**: Supports any abstract strategy board game style
340
+ - **No dependencies**: Foundational primitive requiring no external gems
146
341
 
147
- ### `Sashite::Snn::Name`
342
+ ## Documentation
148
343
 
149
- * `#value` Returns the canonical string value.
150
- * `#to_s` Returns the string representation.
151
- * `#==`, `#eql?`, `#hash` Value-based equality.
152
- * `#same_base_name?(other)` – Optional helper for SIN mapping equivalence.
344
+ - [SNN Specification v1.0.0](https://sashite.dev/specs/snn/1.0.0/) Complete technical specification
345
+ - [SNN Examples](https://sashite.dev/specs/snn/1.0.0/examples/) Comprehensive examples
346
+ - [API Documentation](https://rubydoc.info/github/sashite/snn.rb/main) Full API reference
153
347
 
154
348
  ## Development
155
349
 
@@ -4,16 +4,36 @@ module Sashite
4
4
  module Snn
5
5
  # Represents a style name in SNN (Style Name Notation) format.
6
6
  #
7
- # SNN provides a canonical naming system for abstract strategy game styles.
8
- # Each name must start with an uppercase ASCII letter, followed by zero or more
9
- # lowercase letters or digits.
7
+ # SNN provides a foundational naming system for abstract strategy game styles.
8
+ # Each name must consist of alphabetic characters only, all in the same case
9
+ # (either all uppercase or all lowercase).
10
+ #
11
+ # Case encoding:
12
+ # - UPPERCASE names represent the first player's style
13
+ # - lowercase names represent the second player's style
14
+ #
15
+ # Constraints:
16
+ # - Alphabetic characters only (A-Z or a-z)
17
+ # - Case consistency required (all uppercase OR all lowercase)
18
+ # - No digits, no special characters, no mixed case
10
19
  #
11
20
  # All instances are immutable.
21
+ #
22
+ # @example Valid names
23
+ # Sashite::Snn::Name.new("CHESS") # => #<Snn::Name value="CHESS">
24
+ # Sashite::Snn::Name.new("shogi") # => #<Snn::Name value="shogi">
25
+ # Sashite::Snn::Name.new("XIANGQI") # => #<Snn::Name value="XIANGQI">
26
+ #
27
+ # @example Invalid names
28
+ # Sashite::Snn::Name.new("Chess") # => ArgumentError (mixed case)
29
+ # Sashite::Snn::Name.new("CHESS960") # => ArgumentError (contains digits)
30
+ # Sashite::Snn::Name.new("GO9X9") # => ArgumentError (contains digits)
12
31
  class Name
13
32
  # SNN validation pattern matching the specification
14
- SNN_PATTERN = /\A[A-Z][a-z0-9]*\z/
33
+ # Format: All uppercase OR all lowercase alphabetic characters
34
+ SNN_PATTERN = /\A([A-Z]+|[a-z]+)\z/
15
35
 
16
- # Error messages
36
+ # Error message for invalid SNN strings
17
37
  ERROR_INVALID_NAME = "Invalid SNN string: %s"
18
38
 
19
39
  # @return [String] the canonical style name
@@ -21,8 +41,20 @@ module Sashite
21
41
 
22
42
  # Create a new style name instance
23
43
  #
24
- # @param name [String, Symbol] the style name (e.g., "Shogi", :Chess960)
44
+ # The name must follow SNN format rules: all uppercase or all lowercase
45
+ # alphabetic characters only. No digits, special characters, or mixed case.
46
+ #
47
+ # @param name [String, Symbol] the style name (e.g., "SHOGI", :chess)
25
48
  # @raise [ArgumentError] if the name does not match SNN pattern
49
+ #
50
+ # @example Create valid names
51
+ # Sashite::Snn::Name.new("CHESS") # First player Chess
52
+ # Sashite::Snn::Name.new("shogi") # Second player Shōgi
53
+ # Sashite::Snn::Name.new(:XIANGQI) # First player Xiangqi
54
+ #
55
+ # @example Invalid names raise errors
56
+ # Sashite::Snn::Name.new("Chess") # Mixed case
57
+ # Sashite::Snn::Name.new("CHESS960") # Contains digits
26
58
  def initialize(name)
27
59
  string_value = name.to_s
28
60
  self.class.validate_format(string_value)
@@ -33,57 +65,103 @@ module Sashite
33
65
 
34
66
  # Parse an SNN string into a Name object
35
67
  #
68
+ # This is an alias for the constructor, provided for consistency
69
+ # with other Sashité specifications.
70
+ #
36
71
  # @param string [String] the SNN-formatted style name
37
72
  # @return [Name] a new Name instance
38
73
  # @raise [ArgumentError] if the string is invalid
39
74
  #
40
- # @example
41
- # Sashite::Snn::Name.parse("Shogi") # => #<Snn::Name value="Shogi">
75
+ # @example Parse valid names
76
+ # Sashite::Snn::Name.parse("SHOGI") # => #<Snn::Name value="SHOGI">
77
+ # Sashite::Snn::Name.parse("chess") # => #<Snn::Name value="chess">
42
78
  def self.parse(string)
43
79
  new(string)
44
80
  end
45
81
 
46
82
  # Check whether the given string is a valid SNN name
47
83
  #
84
+ # Valid SNN strings must:
85
+ # - Contain only alphabetic characters (A-Z or a-z)
86
+ # - Have consistent case (all uppercase OR all lowercase)
87
+ # - Contain at least one character
88
+ #
48
89
  # @param string [String] input string to validate
49
90
  # @return [Boolean] true if valid, false otherwise
50
91
  #
51
- # @example
52
- # Sashite::Snn::Name.valid?("Chess") # => true
53
- # Sashite::Snn::Name.valid?("chess") # => false
92
+ # @example Valid names
93
+ # Sashite::Snn::Name.valid?("CHESS") # => true
94
+ # Sashite::Snn::Name.valid?("shogi") # => true
95
+ # Sashite::Snn::Name.valid?("XIANGQI") # => true
96
+ #
97
+ # @example Invalid names
98
+ # Sashite::Snn::Name.valid?("Chess") # => false (mixed case)
99
+ # Sashite::Snn::Name.valid?("CHESS960") # => false (contains digits)
100
+ # Sashite::Snn::Name.valid?("GO9X9") # => false (contains digits)
101
+ # Sashite::Snn::Name.valid?("") # => false (empty)
54
102
  def self.valid?(string)
55
103
  string.is_a?(::String) && string.match?(SNN_PATTERN)
56
104
  end
57
105
 
58
106
  # Returns the string representation of the name
59
107
  #
60
- # @return [String]
108
+ # @return [String] the canonical style name
109
+ #
110
+ # @example
111
+ # name = Sashite::Snn::Name.new("SHOGI")
112
+ # name.to_s # => "SHOGI"
61
113
  def to_s
62
114
  value
63
115
  end
64
116
 
65
- # Equality based on normalized string value
117
+ # Equality based on string value
66
118
  #
67
- # @param other [Object]
68
- # @return [Boolean]
119
+ # Two names are equal if they have the same string value. Case matters:
120
+ # "CHESS" (first player) is not equal to "chess" (second player).
121
+ #
122
+ # @param other [Object] object to compare with
123
+ # @return [Boolean] true if equal, false otherwise
124
+ #
125
+ # @example
126
+ # name1 = Sashite::Snn::Name.new("CHESS")
127
+ # name2 = Sashite::Snn::Name.new("CHESS")
128
+ # name3 = Sashite::Snn::Name.new("chess")
129
+ #
130
+ # name1 == name2 # => true
131
+ # name1 == name3 # => false (different case = different player)
69
132
  def ==(other)
70
133
  other.is_a?(self.class) && value == other.value
71
134
  end
72
135
 
73
- # Required for correct Set/hash behavior
136
+ # Required for correct Set/Hash behavior
74
137
  alias eql? ==
75
138
 
76
139
  # Hash based on class and value
77
140
  #
78
- # @return [Integer]
141
+ # Enables Name objects to be used as hash keys and in sets.
142
+ #
143
+ # @return [Integer] hash code
144
+ #
145
+ # @example Use as hash key
146
+ # styles = {
147
+ # Sashite::Snn::Name.new("CHESS") => "Western Chess",
148
+ # Sashite::Snn::Name.new("SHOGI") => "Japanese Chess"
149
+ # }
79
150
  def hash
80
151
  [self.class, value].hash
81
152
  end
82
153
 
83
154
  # Validate that the string is in proper SNN format
84
155
  #
85
- # @param str [String]
86
- # @raise [ArgumentError] if invalid
156
+ # @param str [String] string to validate
157
+ # @raise [ArgumentError] if the string does not match SNN pattern
158
+ #
159
+ # @example Valid format
160
+ # Sashite::Snn::Name.validate_format("CHESS") # No error
161
+ # Sashite::Snn::Name.validate_format("shogi") # No error
162
+ #
163
+ # @example Invalid format
164
+ # Sashite::Snn::Name.validate_format("Chess") # Raises ArgumentError
87
165
  def self.validate_format(str)
88
166
  raise ::ArgumentError, format(ERROR_INVALID_NAME, str.inspect) unless str.match?(SNN_PATTERN)
89
167
  end
data/lib/sashite/snn.rb CHANGED
@@ -5,53 +5,87 @@ require_relative "snn/name"
5
5
  module Sashite
6
6
  # SNN (Style Name Notation) implementation for Ruby
7
7
  #
8
- # Provides a formal naming system for identifying styles in abstract strategy board games.
9
- # SNN uses canonical, human-readable ASCII names beginning with an uppercase letter.
10
- # It supports unlimited unique style identifiers with consistent, rule-agnostic semantics.
8
+ # Provides a foundational naming system for identifying styles in abstract strategy board games.
9
+ # SNN uses canonical, human-readable alphabetic names with case encoding to represent both
10
+ # style identity and player assignment.
11
11
  #
12
- # Format: <uppercase-letter>[<lowercase-letter | digit>]*
12
+ # Format: All uppercase OR all lowercase alphabetic characters
13
13
  #
14
14
  # Examples:
15
- # "Chess" - Standard Western chess
16
- # "Shogi" - Japanese chess
17
- # "Minishogi" - 5×5 compact shōgi variant
18
- # "Chess960" - Fischer random chess
15
+ # "CHESS" - Chess style for first player
16
+ # "chess" - Chess style for second player
17
+ # "SHOGI" - Shōgi style for first player
18
+ # "shogi" - Shōgi style for second player
19
+ # "XIANGQI" - Xiangqi style for first player
20
+ # "xiangqi" - Xiangqi style for second player
21
+ #
22
+ # Case Encoding:
23
+ # - UPPERCASE names represent the first player's style
24
+ # - lowercase names represent the second player's style
25
+ #
26
+ # Constraints:
27
+ # - Alphabetic characters only (A-Z, a-z)
28
+ # - Case consistency required (all uppercase OR all lowercase)
29
+ # - No digits, no special characters, no mixed case
30
+ #
31
+ # As a foundational primitive, SNN has no dependencies and serves as a building block
32
+ # for formal style identification in the Sashité ecosystem.
19
33
  #
20
34
  # See: https://sashite.dev/specs/snn/1.0.0/
21
35
  module Snn
22
36
  # Check if a string is valid SNN notation
23
37
  #
38
+ # Valid SNN strings must contain only alphabetic characters in consistent case
39
+ # (either all uppercase or all lowercase).
40
+ #
24
41
  # @param snn_string [String] the string to validate
25
42
  # @return [Boolean] true if valid SNN, false otherwise
26
43
  #
27
44
  # @example Validate SNN strings
28
- # Sashite::Snn.valid?("Chess") # => true
29
- # Sashite::Snn.valid?("minishogi") # => false
30
- # Sashite::Snn.valid?("Go9x9") # => true
45
+ # Sashite::Snn.valid?("CHESS") # => true
46
+ # Sashite::Snn.valid?("shogi") # => true
47
+ # Sashite::Snn.valid?("Chess") # => false (mixed case)
48
+ # Sashite::Snn.valid?("CHESS960") # => false (contains digits)
49
+ # Sashite::Snn.valid?("GO9X9") # => false (contains digits)
31
50
  def self.valid?(snn_string)
32
51
  Name.valid?(snn_string)
33
52
  end
34
53
 
35
54
  # Parse an SNN string into a Name object
36
55
  #
56
+ # Converts a valid SNN string into an immutable Name object. The name must follow
57
+ # SNN format rules: all uppercase or all lowercase alphabetic characters only.
58
+ #
37
59
  # @param snn_string [String] the name string
38
60
  # @return [Snn::Name] a parsed name object
39
61
  # @raise [ArgumentError] if the name is invalid
40
62
  #
41
63
  # @example Parse valid SNN names
42
- # Sashite::Snn.parse("Shogi") # => #<Snn::Name value="Shogi">
64
+ # Sashite::Snn.parse("SHOGI") # => #<Snn::Name value="SHOGI">
65
+ # Sashite::Snn.parse("chess") # => #<Snn::Name value="chess">
66
+ #
67
+ # @example Invalid names raise errors
68
+ # Sashite::Snn.parse("Chess") # => ArgumentError (mixed case)
69
+ # Sashite::Snn.parse("CHESS960") # => ArgumentError (contains digits)
43
70
  def self.parse(snn_string)
44
71
  Name.parse(snn_string)
45
72
  end
46
73
 
47
74
  # Create a new Name instance directly
48
75
  #
76
+ # Constructs a Name object from a string or symbol. The value must follow
77
+ # SNN format rules: all uppercase or all lowercase alphabetic characters only.
78
+ #
49
79
  # @param value [String, Symbol] style name to construct
50
80
  # @return [Snn::Name] new name instance
51
81
  # @raise [ArgumentError] if name format is invalid
52
82
  #
53
- # @example
54
- # Sashite::Snn.name("Xiangqi") # => #<Snn::Name value="Xiangqi">
83
+ # @example Create names
84
+ # Sashite::Snn.name("XIANGQI") # => #<Snn::Name value="XIANGQI">
85
+ # Sashite::Snn.name(:makruk) # => #<Snn::Name value="makruk">
86
+ #
87
+ # @example Invalid formats raise errors
88
+ # Sashite::Snn.name("Chess960") # => ArgumentError
55
89
  def self.name(value)
56
90
  Name.new(value)
57
91
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sashite-snn
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyril Kato
@@ -10,12 +10,18 @@ cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  description: |
13
- SNN (Style Name Notation) provides a rule-agnostic, scalable naming system for identifying
14
- abstract strategy board game styles. This gem implements the SNN Specification v1.0.0 with
15
- a modern Ruby interface featuring immutable style name objects and functional programming
16
- principles. SNN uses canonical ASCII names (e.g., "Shogi", "Go9x9") to unambiguously refer
17
- to game styles across variants and traditions. Ideal for engines, protocols, and tools that
18
- need clear and extensible style identifiers.
13
+ SNN (Style Name Notation) provides a foundational, rule-agnostic naming system for identifying
14
+ game styles in abstract strategy board games. This gem implements the SNN Specification v1.0.0
15
+ with a modern Ruby interface featuring immutable style name objects and functional programming
16
+ principles.
17
+
18
+ SNN uses case-consistent alphabetic names (e.g., "CHESS", "SHOGI", "XIANGQI" for first player;
19
+ "chess", "shogi", "xiangqi" for second player) to unambiguously represent both style identity
20
+ and player assignment. As a foundational primitive with no dependencies, SNN serves as a building
21
+ block for formal style identification across the Sashité ecosystem.
22
+
23
+ Format: All uppercase OR all lowercase alphabetic characters only (no digits, no special characters).
24
+ Ideal for game engines, protocols, and tools requiring clear and extensible style identifiers.
19
25
  email: contact@cyril.email
20
26
  executables: []
21
27
  extensions: []
@@ -50,8 +56,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
50
56
  - !ruby/object:Gem::Version
51
57
  version: '0'
52
58
  requirements: []
53
- rubygems_version: 3.6.9
59
+ rubygems_version: 3.7.1
54
60
  specification_version: 4
55
- summary: SNN (Style Name Notation) implementation for Ruby with immutable style name
56
- objects
61
+ summary: SNN (Style Name Notation) - foundational naming system for abstract strategy
62
+ game styles
57
63
  test_files: []