sashite-snn 1.1.1 → 2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c3327ae1106ccede78736585e812180c12d7a56852a82ae70b7745e233ef5496
4
- data.tar.gz: ba5892640791f51341e52c883f52f9f21bac64e21001052e1fcbc17965a156a7
3
+ metadata.gz: 6fb3b57b1f252e95f7e7d3e8484d6729b90ca6f13dfd19705c9ac61d6095cf25
4
+ data.tar.gz: 296ec73c256baf0ea7999b74b7933b0e95f92b2b7488e3d5efb69b43b7132951
5
5
  SHA512:
6
- metadata.gz: 1798b7aec7b88cc2baddcd9fa7f3518c2d97bf16343caac92ec84f9bfad3dc0ac20830d95144984ea27795d867e80c008a090f9de7fc8d284ac853f9c326b144
7
- data.tar.gz: a6a548c819cf763b914cc1b70ce82ba86d86b7973f0231df5075931788d19fb2732d4138896b347cf6a1842c554116578e59cdfb619408acb370c2c43d469753
6
+ metadata.gz: 1e7b8270ca459703a6bd41f47424af55452f8c287567abc8bdf0e85acc69914f20a1830361ce10e3f0e157c137e3f31f60149481274b24e6dedb597d109927d6
7
+ data.tar.gz: 9a684bb46685f7a77fbade6caaf2d642f31a6b31b142e1fb5a47512e8a4133fa50adf0f24c8b551d87c67232c23db6c5436e95d819a38ac72e069f37da63aff5
data/README.md CHANGED
@@ -9,9 +9,9 @@
9
9
 
10
10
  ## What is SNN?
11
11
 
12
- SNN (Style Name Notation) provides a rule-agnostic format for identifying styles in abstract strategy board games. SNN uses standardized naming conventions with case-based side encoding, enabling clear distinction between different traditions in multi-style gaming environments.
12
+ SNN (Style Name Notation) provides a compact, ASCII-based format for identifying **styles** in abstract strategy board games. SNN uses single-character identifiers with case encoding to represent both style identity and player assignment simultaneously.
13
13
 
14
- This gem implements the [SNN Specification v1.0.0](https://sashite.dev/specs/snn/1.0.0/), providing a modern Ruby interface with immutable style objects and functional programming principles.
14
+ This gem implements the [SNN Specification v1.0.0](https://sashite.dev/specs/snn/1.0.0/), providing a rule-agnostic notation system for style identification in board games.
15
15
 
16
16
  ## Installation
17
17
 
@@ -28,144 +28,166 @@ gem install sashite-snn
28
28
 
29
29
  ## Usage
30
30
 
31
+ ### Basic Operations
32
+
31
33
  ```ruby
32
34
  require "sashite/snn"
33
35
 
34
36
  # Parse SNN strings into style objects
35
- style = Sashite::Snn.parse("CHESS") # => #<Snn::Style name=:Chess side=:first>
36
- style.to_s # => "CHESS"
37
- style.name # => :Chess
38
- style.side # => :first
37
+ style = Sashite::Snn.parse("C") # => #<Snn::Style letter=:C side=:first>
38
+ style.to_s # => "C"
39
+ style.letter # => :C
40
+ style.side # => :first
39
41
 
40
42
  # Create styles directly
41
- style = Sashite::Snn.style(:Chess, :first) # => #<Snn::Style name=:Chess side=:first>
42
- style = Sashite::Snn::Style.new(:Shogi, :second) # => #<Snn::Style name=:Shogi side=:second>
43
+ style = Sashite::Snn.style(:C, :first) # => #<Snn::Style letter=:C side=:first>
44
+ style = Sashite::Snn::Style.new(:c, :second) # => #<Snn::Style letter=:c side=:second>
43
45
 
44
46
  # Validate SNN strings
45
- Sashite::Snn.valid?("CHESS") # => true
46
- Sashite::Snn.valid?("chess") # => true
47
- Sashite::Snn.valid?("Chess") # => false (mixed case)
48
- Sashite::Snn.valid?("123") # => false (must start with letter)
49
-
50
- # Side manipulation (returns new immutable instances)
51
- black_chess = style.flip # => #<Snn::Style name=:Chess side=:second>
52
- black_chess.to_s # => "chess"
53
-
54
- # Name manipulation
55
- shogi_style = style.with_name(:Shogi) # => #<Snn::Style name=:Shogi side=:first>
56
- shogi_style.to_s # => "SHOGI"
57
-
58
- # Side queries
59
- style.first_player? # => true
60
- black_chess.second_player? # => true
61
-
62
- # Name and side comparison
63
- chess1 = Sashite::Snn.parse("CHESS")
64
- chess2 = Sashite::Snn.parse("chess")
65
- shogi = Sashite::Snn.parse("SHOGI")
66
-
67
- chess1.same_name?(chess2) # => true (both chess)
68
- chess1.same_side?(shogi) # => true (both first player)
69
- chess1.same_name?(shogi) # => false (different styles)
70
-
71
- # Functional transformations can be chained
72
- black_shogi = Sashite::Snn.parse("CHESS").with_name(:Shogi).flip
73
- black_shogi.to_s # => "shogi"
47
+ Sashite::Snn.valid?("C") # => true
48
+ Sashite::Snn.valid?("c") # => true
49
+ Sashite::Snn.valid?("1") # => false (not a letter)
50
+ Sashite::Snn.valid?("CC") # => false (not single character)
51
+ ```
52
+
53
+ ### Style Transformations
54
+
55
+ ```ruby
56
+ # All transformations return new immutable instances
57
+ style = Sashite::Snn.parse("C")
58
+
59
+ # Flip player assignment
60
+ flipped = style.flip # => #<Snn::Style letter=:c side=:second>
61
+ flipped.to_s # => "c"
62
+
63
+ # Change letter
64
+ changed = style.with_letter(:S) # => #<Snn::Style letter=:S side=:first>
65
+ changed.to_s # => "S"
66
+
67
+ # Change side
68
+ other_side = style.with_side(:second) # => #<Snn::Style letter=:c side=:second>
69
+ other_side.to_s # => "c"
70
+
71
+ # Chain transformations
72
+ result = style.flip.with_letter(:M) # => #<Snn::Style letter=:m side=:second>
73
+ result.to_s # => "m"
74
+ ```
75
+
76
+ ### Player and Style Queries
77
+
78
+ ```ruby
79
+ style = Sashite::Snn.parse("C")
80
+ opposite = Sashite::Snn.parse("s")
81
+
82
+ # Player identification
83
+ style.first_player? # => true
84
+ style.second_player? # => false
85
+ opposite.first_player? # => false
86
+ opposite.second_player? # => true
87
+
88
+ # Letter comparison
89
+ chess1 = Sashite::Snn.parse("C")
90
+ chess2 = Sashite::Snn.parse("c")
91
+ shogi = Sashite::Snn.parse("S")
92
+
93
+ chess1.same_letter?(chess2) # => true (both use letter C)
94
+ chess1.same_side?(shogi) # => true (both first player)
95
+ chess1.same_letter?(shogi) # => false (different letters)
96
+ ```
97
+
98
+ ### Style Collections
99
+
100
+ ```ruby
101
+ # Working with multiple styles
102
+ styles = %w[C c S s M m].map { |snn| Sashite::Snn.parse(snn) }
103
+
104
+ # Filter by player
105
+ first_player_styles = styles.select(&:first_player?)
106
+ first_player_styles.map(&:to_s) # => ["C", "S", "M"]
107
+
108
+ # Group by letter family
109
+ by_letter = styles.group_by { |s| s.letter.to_s.upcase }
110
+ by_letter["C"].size # => 2 (both C and c)
111
+
112
+ # Find specific combinations
113
+ chess_styles = styles.select { |s| s.letter.to_s.upcase == "C" }
114
+ chess_styles.map(&:to_s) # => ["C", "c"]
74
115
  ```
75
116
 
76
117
  ## Format Specification
77
118
 
78
119
  ### Structure
79
120
  ```
80
- <style-identifier>
121
+ <style-letter>
81
122
  ```
82
123
 
83
- ### Components
124
+ ### Grammar (BNF)
125
+ ```bnf
126
+ <snn> ::= <uppercase-letter> | <lowercase-letter>
84
127
 
85
- - **Identifier**: Alphanumeric string starting with a letter
86
- - Uppercase: First player styles (`CHESS`, `SHOGI`, `XIANGQI`)
87
- - Lowercase: Second player styles (`chess`, `shogi`, `xiangqi`)
88
- - **Case Consistency**: Entire identifier must be uppercase or lowercase (no mixed case)
128
+ <uppercase-letter> ::= "A" | "B" | "C" | ... | "Z"
129
+ <lowercase-letter> ::= "a" | "b" | "c" | ... | "z"
130
+ ```
89
131
 
90
132
  ### Regular Expression
91
133
  ```ruby
92
- # Pattern accessible via Sashite::Snn::Style::SNN_PATTERN
93
- /\A([A-Z][A-Z0-9]*|[a-z][a-z0-9]*)\z/
134
+ /\A[A-Za-z]\z/
94
135
  ```
95
136
 
96
- ### Examples
97
- - `CHESS` - First player chess style
98
- - `chess` - Second player chess style
99
- - `CHESS960` - First player Fischer Random Chess style
100
- - `SHOGI` - First player shōgi style
101
- - `shogi` - Second player shōgi style
102
- - `XIANGQI` - First player xiangqi style
137
+ ### Style Attribute Mapping
103
138
 
104
- ## Game Examples
139
+ | Style Attribute | SNN Encoding | Examples |
140
+ |-----------------|--------------|----------|
141
+ | **Style Family** | Letter choice | `C`/`c` = Chess family |
142
+ | **Player Assignment** | Letter case | `C` = First player, `c` = Second player |
105
143
 
106
- ### Classic Styles
107
- ```ruby
108
- # Traditional game styles
109
- chess = Sashite::Snn.style(:Chess, :first) # => traditional chess
110
- shogi = Sashite::Snn.style(:Shogi, :first) # => traditional shōgi
111
- xiangqi = Sashite::Snn.style(:Xiangqi, :first) # => traditional xiangqi
112
- makruk = Sashite::Snn.style(:Makruk, :first) # => traditional makruk
113
-
114
- # Player variations
115
- white_chess = chess # => first player
116
- black_chess = chess.flip # => second player
117
- black_chess.to_s # => "chess"
118
- ```
119
-
120
- ### Chess Variants
121
- ```ruby
122
- # Fischer Random Chess
123
- chess960_white = Sashite::Snn.style(:Chess960, :first)
124
- chess960_black = chess960_white.flip
125
- chess960_black.to_s # => "chess960"
144
+ ## Game Examples
126
145
 
127
- # King of the Hill Chess
128
- koth = Sashite::Snn.style(:Koth, :first)
129
- koth.to_s # => "KOTH"
146
+ The SNN specification is rule-agnostic and does not define specific letter assignments. However, here are common usage patterns:
130
147
 
131
- # Three-Check Chess
132
- threecheck = Sashite::Snn.style(:Threecheck, :first)
133
- ```
148
+ ### Traditional Game Families
134
149
 
135
- ### Shōgi Variants
136
150
  ```ruby
137
- # Traditional Shōgi
138
- standard_shogi = Sashite::Snn.style(:Shogi, :first)
151
+ # Chess family styles
152
+ chess_white = Sashite::Snn.parse("C") # First player, Chess family
153
+ chess_black = Sashite::Snn.parse("c") # Second player, Chess family
139
154
 
140
- # Mini Shōgi
141
- mini_shogi = Sashite::Snn.style(:Minishogi, :first)
155
+ # Shōgi family styles
156
+ shogi_sente = Sashite::Snn.parse("S") # First player, Shōgi family
157
+ shogi_gote = Sashite::Snn.parse("s") # Second player, Shōgi family
142
158
 
143
- # Chu Shōgi
144
- chu_shogi = Sashite::Snn.style(:Chushogi, :first)
159
+ # Xiangqi family styles
160
+ xiangqi_red = Sashite::Snn.parse("X") # First player, Xiangqi family
161
+ xiangqi_black = Sashite::Snn.parse("x") # Second player, Xiangqi family
145
162
  ```
146
163
 
147
- ### Multi-Style Gaming
164
+ ### Cross-Style Scenarios
165
+
148
166
  ```ruby
149
- # Cross-tradition match
167
+ # Different families in one match
150
168
  def create_hybrid_match
151
- styles = [
152
- Sashite::Snn.style(:Chess, :first), # White uses chess pieces
153
- Sashite::Snn.style(:Shogi, :second) # Black uses shōgi pieces
169
+ [
170
+ Sashite::Snn.parse("C"), # First player uses Chess family
171
+ Sashite::Snn.parse("s") # Second player uses Shōgi family
154
172
  ]
155
-
156
- # Each player uses their preferred piece style
157
- styles
158
173
  end
159
174
 
160
- # Style compatibility check
161
- def compatible_styles?(style1, style2)
162
- # Styles are compatible if they have different sides
163
- !style1.same_side?(style2)
164
- end
175
+ styles = create_hybrid_match
176
+ styles[0].same_side?(styles[1]) # => false (different players)
177
+ styles[0].same_letter?(styles[1]) # => false (different families)
178
+ ```
179
+
180
+ ### Variant Families
165
181
 
166
- chess_white = Sashite::Snn.parse("CHESS")
167
- shogi_black = Sashite::Snn.parse("shogi")
168
- puts compatible_styles?(chess_white, shogi_black) # => true
182
+ ```ruby
183
+ # Different letters can represent variants within traditions
184
+ makruk = Sashite::Snn.parse("M") # Makruk (Thai Chess) family
185
+ janggi = Sashite::Snn.parse("J") # Janggi (Korean Chess) family
186
+ ogi = Sashite::Snn.parse("O") # Ōgi (王棋) family
187
+
188
+ # Each family can have both players
189
+ makruk_black = makruk.flip # Second player Makruk
190
+ makruk_black.to_s # => "m"
169
191
  ```
170
192
 
171
193
  ## API Reference
@@ -174,47 +196,31 @@ puts compatible_styles?(chess_white, shogi_black) # => true
174
196
 
175
197
  - `Sashite::Snn.valid?(snn_string)` - Check if string is valid SNN notation
176
198
  - `Sashite::Snn.parse(snn_string)` - Parse SNN string into Style object
177
- - `Sashite::Snn.style(name, side)` - Create style instance directly
199
+ - `Sashite::Snn.style(letter, side)` - Create style instance directly
178
200
 
179
201
  ### Style Class
180
202
 
181
203
  #### Creation and Parsing
182
- - `Sashite::Snn::Style.new(name, side)` - Create style instance
183
- - `Sashite::Snn::Style.parse(snn_string)` - Parse SNN string (same as module method)
204
+ - `Sashite::Snn::Style.new(letter, side)` - Create style instance
205
+ - `Sashite::Snn::Style.parse(snn_string)` - Parse SNN string
184
206
 
185
207
  #### Attribute Access
186
- - `#name` - Get style name (symbol with proper capitalization)
208
+ - `#letter` - Get style letter (symbol :A through :z)
187
209
  - `#side` - Get player side (:first or :second)
188
210
  - `#to_s` - Convert to SNN string representation
189
211
 
190
- #### Name and Case Handling
191
-
192
- **Important**: The `name` attribute is always stored with proper capitalization (first letter uppercase, rest lowercase), regardless of the input case when parsing. The display case in `#to_s` is determined by the `side` attribute:
193
-
194
- ```ruby
195
- # Both create the same internal name representation
196
- style1 = Sashite::Snn.parse("CHESS") # name: :Chess, side: :first
197
- style2 = Sashite::Snn.parse("chess") # name: :Chess, side: :second
198
-
199
- style1.name # => :Chess (proper capitalization)
200
- style2.name # => :Chess (same capitalization)
201
-
202
- style1.to_s # => "CHESS" (uppercase display)
203
- style2.to_s # => "chess" (lowercase display)
204
- ```
205
-
206
- #### Side Queries
212
+ #### Player Queries
207
213
  - `#first_player?` - Check if first player style
208
214
  - `#second_player?` - Check if second player style
209
215
 
210
216
  #### Transformations (immutable - return new instances)
211
- - `#flip` - Switch player (change side)
212
- - `#with_name(new_name)` - Create style with different name
217
+ - `#flip` - Switch player assignment
218
+ - `#with_letter(new_letter)` - Create style with different letter
213
219
  - `#with_side(new_side)` - Create style with different side
214
220
 
215
221
  #### Comparison Methods
216
- - `#same_name?(other)` - Check if same style name
217
- - `#same_side?(other)` - Check if same side
222
+ - `#same_letter?(other)` - Check if same style letter (case-insensitive)
223
+ - `#same_side?(other)` - Check if same player side
218
224
  - `#==(other)` - Full equality comparison
219
225
 
220
226
  ### Style Class Constants
@@ -226,91 +232,87 @@ style2.to_s # => "chess" (lowercase display)
226
232
 
227
233
  ## Advanced Usage
228
234
 
229
- ### Name Normalization Examples
235
+ ### Letter Case and Side Mapping
230
236
 
231
237
  ```ruby
232
- # Parsing different cases results in same name
233
- white_chess = Sashite::Snn.parse("CHESS")
234
- black_chess = Sashite::Snn.parse("chess")
238
+ # SNN encodes player assignment through case
239
+ upper_case_letters = ("A".."Z").map { |letter| Sashite::Snn.parse(letter) }
240
+ lower_case_letters = ("a".."z").map { |letter| Sashite::Snn.parse(letter) }
235
241
 
236
- # Names are normalized with proper capitalization
237
- white_chess.name # => :Chess
238
- black_chess.name # => :Chess (same name!)
242
+ # All uppercase letters are first player
243
+ upper_case_letters.all?(&:first_player?) # => true
239
244
 
240
- # Sides are different
241
- white_chess.side # => :first
242
- black_chess.side # => :second
245
+ # All lowercase letters are second player
246
+ lower_case_letters.all?(&:second_player?) # => true
243
247
 
244
- # Display follows side convention
245
- white_chess.to_s # => "CHESS"
246
- black_chess.to_s # => "chess"
248
+ # Letter families are related by case
249
+ letter_a_first = Sashite::Snn.parse("A")
250
+ letter_a_second = Sashite::Snn.parse("a")
247
251
 
248
- # Same name, different sides
249
- white_chess.same_name?(black_chess) # => true
250
- white_chess.same_side?(black_chess) # => false
252
+ letter_a_first.same_letter?(letter_a_second) # => true
253
+ letter_a_first.same_side?(letter_a_second) # => false
251
254
  ```
252
255
 
253
256
  ### Immutable Transformations
257
+
254
258
  ```ruby
255
259
  # All transformations return new instances
256
- original = Sashite::Snn.style(:Chess, :first)
260
+ original = Sashite::Snn.style(:C, :first)
257
261
  flipped = original.flip
258
- renamed = original.with_name(:Shogi)
262
+ changed_letter = original.with_letter(:S)
259
263
 
260
264
  # Original style is never modified
261
- puts original.to_s # => "CHESS"
262
- puts flipped.to_s # => "chess"
263
- puts renamed.to_s # => "SHOGI"
265
+ original.to_s # => "C" (unchanged)
266
+ flipped.to_s # => "c"
267
+ changed_letter.to_s # => "S"
264
268
 
265
269
  # Transformations can be chained
266
- result = original.flip.with_name(:Xiangqi)
267
- puts result.to_s # => "xiangqi"
270
+ result = original.flip.with_letter(:M).flip
271
+ result.to_s # => "M"
268
272
  ```
269
273
 
270
274
  ### Game Configuration Management
275
+
271
276
  ```ruby
272
277
  class GameConfiguration
273
278
  def initialize
274
279
  @player_styles = {}
275
280
  end
276
281
 
277
- def set_player_style(player, style_name)
282
+ def set_player_style(player, letter)
278
283
  side = player == :white ? :first : :second
279
- @player_styles[player] = Sashite::Snn.style(style_name, side)
284
+ @player_styles[player] = Sashite::Snn.style(letter, side)
280
285
  end
281
286
 
282
287
  def get_player_style(player)
283
288
  @player_styles[player]
284
289
  end
285
290
 
286
- def style_mismatch?
291
+ def cross_family_match?
287
292
  return false if @player_styles.size < 2
288
293
 
289
294
  styles = @player_styles.values
290
- !styles.all? { |style| style.same_name?(styles.first) }
295
+ !styles.all? { |style| style.same_letter?(styles.first) }
291
296
  end
292
297
 
293
- def cross_tradition_match?
294
- return false if @player_styles.size < 2
295
-
296
- style_names = @player_styles.values.map(&:name).uniq
297
- style_names.size > 1
298
+ def same_family_match?
299
+ !cross_family_match?
298
300
  end
299
301
  end
300
302
 
301
303
  # Usage
302
304
  config = GameConfiguration.new
303
- config.set_player_style(:white, :Chess)
304
- config.set_player_style(:black, :Shogi)
305
+ config.set_player_style(:white, :C) # Chess family, first player
306
+ config.set_player_style(:black, :S) # Shōgi family, second player
305
307
 
306
- puts config.cross_tradition_match? # => true
307
- puts config.style_mismatch? # => true
308
+ config.cross_family_match? # => true
308
309
 
309
310
  white_style = config.get_player_style(:white)
310
- puts white_style.to_s # => "CHESS"
311
+ white_style.to_s # => "C"
311
312
  ```
312
313
 
313
314
  ### Style Analysis
315
+
314
316
  ```ruby
315
317
  def analyze_styles(snns)
316
318
  styles = snns.map { |snn| Sashite::Snn.parse(snn) }
@@ -318,29 +320,30 @@ def analyze_styles(snns)
318
320
  {
319
321
  total: styles.size,
320
322
  by_side: styles.group_by(&:side),
321
- by_name: styles.group_by(&:name),
322
- unique_names: styles.map(&:name).uniq.size,
323
- cross_tradition: styles.map(&:name).uniq.size > 1
323
+ by_letter: styles.group_by { |s| s.letter.to_s.upcase },
324
+ unique_letters: styles.map { |s| s.letter.to_s.upcase }.uniq.size,
325
+ cross_family: styles.map { |s| s.letter.to_s.upcase }.uniq.size > 1
324
326
  }
325
327
  end
326
328
 
327
- snns = %w[CHESS chess SHOGI shogi XIANGQI xiangqi]
329
+ snns = %w[C c S s X x]
328
330
  analysis = analyze_styles(snns)
329
- puts analysis[:by_side][:first].size # => 3
330
- puts analysis[:unique_names] # => 3
331
- puts analysis[:cross_tradition] # => true
331
+ analysis[:by_side][:first].size # => 3
332
+ analysis[:unique_letters] # => 3
333
+ analysis[:cross_family] # => true
332
334
  ```
333
335
 
334
- ### Tournament Style Management
336
+ ### Tournament Style Registry
337
+
335
338
  ```ruby
336
339
  class TournamentStyleRegistry
337
340
  def initialize
338
341
  @registered_styles = Set.new
339
342
  end
340
343
 
341
- def register_style(style_name)
342
- # Register both sides of a style
343
- first_player_style = Sashite::Snn.style(style_name, :first)
344
+ def register_letter(letter)
345
+ # Register both sides of a letter family
346
+ first_player_style = Sashite::Snn.style(letter.to_s.upcase.to_sym, :first)
344
347
  second_player_style = first_player_style.flip
345
348
 
346
349
  @registered_styles.add(first_player_style)
@@ -359,94 +362,62 @@ class TournamentStyleRegistry
359
362
  @registered_styles.select { |style| style.side == side }
360
363
  end
361
364
 
362
- def supported_traditions
363
- @registered_styles.map(&:name).uniq
365
+ def supported_families
366
+ @registered_styles.map { |s| s.letter.to_s.upcase }.uniq.sort
364
367
  end
365
368
  end
366
369
 
367
370
  # Usage
368
371
  registry = TournamentStyleRegistry.new
369
- registry.register_style(:Chess)
370
- registry.register_style(:Shogi)
372
+ registry.register_letter(:C)
373
+ registry.register_letter(:S)
371
374
 
372
- chess_white = Sashite::Snn.parse("CHESS")
373
- shogi_black = Sashite::Snn.parse("shogi")
375
+ chess_white = Sashite::Snn.parse("C")
376
+ shogi_black = Sashite::Snn.parse("s")
374
377
 
375
- puts registry.valid_pairing?(chess_white, shogi_black) # => true
376
- puts registry.supported_traditions # => [:Chess, :Shogi]
378
+ registry.valid_pairing?(chess_white, shogi_black) # => true
379
+ registry.supported_families # => ["C", "S"]
377
380
  ```
378
381
 
379
382
  ## Protocol Mapping
380
383
 
381
- Following the [Game Protocol](https://sashite.dev/game-protocol/):
384
+ Following the [Sashité Protocol](https://sashite.dev/protocol/):
382
385
 
383
386
  | Protocol Attribute | SNN Encoding | Examples | Notes |
384
387
  |-------------------|--------------|----------|-------|
385
- | **Style** | Alphanumeric identifier | `CHESS`, `SHOGI`, `XIANGQI` | Name is always stored with proper capitalization |
386
- | **Side** | Case encoding | `CHESS` = First player, `chess` = Second player | Case is determined by side during rendering |
387
-
388
- **Name Convention**: All style names are internally represented with proper capitalization (first letter uppercase, rest lowercase). The display case is determined by the `side` attribute: first player styles display as uppercase, second player styles as lowercase.
389
-
390
- **Canonical principle**: Identical styles must have identical SNN representations.
391
-
392
- ## Properties
393
-
394
- * **Rule-Agnostic**: Independent of specific game mechanics
395
- * **Cross-Style Support**: Enables multi-tradition gaming environments
396
- * **Canonical Representation**: Consistent naming for equivalent styles
397
- * **Name Normalization**: Consistent proper capitalization representation internally
398
- * **Immutable**: All style instances are frozen and transformations return new objects
399
- * **Functional**: Pure functions with no side effects
400
-
401
- ## Implementation Notes
402
-
403
- ### Name Normalization Convention
404
-
405
- SNN follows a strict name normalization convention:
388
+ | **Style Family** | Letter choice | `C`, `S`, `X` | Rule-agnostic letter assignment |
389
+ | **Player Assignment** | Case encoding | `C` = First player, `c` = Second player | Case determines side |
406
390
 
407
- 1. **Internal Storage**: All style names are stored with proper capitalization (first letter uppercase, rest lowercase)
408
- 2. **Input Flexibility**: Both `"CHESS"` and `"chess"` are valid input during parsing
409
- 3. **Case Semantics**: Input case determines the `side` attribute, not the `name`
410
- 4. **Display Logic**: Output case is computed from `side` during rendering
411
-
412
- This design ensures:
413
- - Consistent internal representation regardless of input format
414
- - Clear separation between style identity (name) and ownership (side)
415
- - Predictable behavior when comparing styles of the same name
416
-
417
- ### Example Flow
418
-
419
- ```ruby
420
- # Input: "chess" (lowercase)
421
- # ↓ Parsing
422
- # name: :Chess (normalized with proper capitalization)
423
- # side: :second (inferred from lowercase input)
424
- # ↓ Display
425
- # SNN: "chess" (final representation)
426
- ```
391
+ ## System Constraints
427
392
 
428
- This ensures that `parse(snn).to_s == snn` for all valid SNN strings while maintaining internal consistency.
393
+ - **26 possible identifiers** per player using ASCII letters (A-Z, a-z)
394
+ - **Exactly 2 players** through case distinction
395
+ - **Single character** per style-player combination
396
+ - **Rule-agnostic** - no predefined letter meanings
429
397
 
430
- ## System Constraints
398
+ ## Design Properties
431
399
 
432
- - **Alphanumeric identifiers** starting with a letter
433
- - **Exactly 2 players** (uppercase/lowercase distinction)
434
- - **Case consistency** within each identifier (no mixed case)
400
+ - **ASCII compatibility**: Maximum portability across systems
401
+ - **Rule-agnostic**: Independent of specific game mechanics
402
+ - **Minimal overhead**: Single character per style-player combination
403
+ - **Canonical representation**: Each style-player combination has exactly one SNN identifier
404
+ - **Immutable**: All style instances are frozen and transformations return new objects
405
+ - **Functional**: Pure functions with no side effects
435
406
 
436
407
  ## Related Specifications
437
408
 
438
- - [Game Protocol](https://sashite.dev/game-protocol/) - Conceptual foundation for abstract strategy board games
439
- - [PIN](https://sashite.dev/specs/pin/) - Piece Identifier Notation (ASCII piece representation)
409
+ - [SNN Specification v1.0.0](https://sashite.dev/specs/snn/1.0.0/) - Complete technical specification
410
+ - [SNN Examples](https://sashite.dev/specs/snn/1.0.0/examples/) - Practical implementation examples
411
+ - [Sashité Protocol](https://sashite.dev/protocol/) - Conceptual foundation for abstract strategy board games
412
+ - [PIN](https://sashite.dev/specs/pin/) - Piece Identifier Notation
440
413
  - [PNN](https://sashite.dev/specs/pnn/) - Piece Name Notation (style-aware piece representation)
441
- - [CELL](https://sashite.dev/specs/cell/) - Board position coordinates
442
- - [HAND](https://sashite.dev/specs/hand/) - Reserve location notation
443
- - [PMN](https://sashite.dev/specs/pmn/) - Portable Move Notation
414
+ - [QPI](https://sashite.dev/specs/qpi/) - Qualified Piece Identifier
444
415
 
445
416
  ## Documentation
446
417
 
447
418
  - [Official SNN Specification v1.0.0](https://sashite.dev/specs/snn/1.0.0/)
448
419
  - [SNN Examples Documentation](https://sashite.dev/specs/snn/1.0.0/examples/)
449
- - [Game Protocol Foundation](https://sashite.dev/game-protocol/)
420
+ - [Sashité Protocol Foundation](https://sashite.dev/protocol/)
450
421
  - [API Documentation](https://rubydoc.info/github/sashite/snn.rb/main)
451
422
 
452
423
  ## Development
@@ -4,18 +4,15 @@ module Sashite
4
4
  module Snn
5
5
  # Represents a style in SNN (Style Name Notation) format.
6
6
  #
7
- # A style consists of an alphanumeric identifier with case-based side encoding:
8
- # - Uppercase identifier: first player (CHESS, SHOGI, XIANGQI)
9
- # - Lowercase identifier: second player (chess, shogi, xiangqi)
7
+ # A style consists of a single ASCII letter with case-based side encoding:
8
+ # - Uppercase letter: first player (A, B, C, ..., Z)
9
+ # - Lowercase letter: second player (a, b, c, ..., z)
10
10
  #
11
11
  # All instances are immutable - transformation methods return new instances.
12
- # This follows the Game Protocol's style model with Name and Side attributes.
12
+ # This follows the SNN Specification v1.0.0 with Letter and Side attributes.
13
13
  class Style
14
14
  # SNN validation pattern matching the specification
15
- SNN_PATTERN = /\A(?<identifier>[A-Z][A-Z0-9]*|[a-z][a-z0-9]*)\z/
16
-
17
- # Pattern for proper name capitalization (first letter uppercase, rest lowercase/digits)
18
- PROPER_NAME_PATTERN = /\A[A-Z][a-z0-9]*\z/
15
+ SNN_PATTERN = /\A[A-Za-z]\z/
19
16
 
20
17
  # Player side constants
21
18
  FIRST_PLAYER = :first
@@ -26,25 +23,25 @@ module Sashite
26
23
 
27
24
  # Error messages
28
25
  ERROR_INVALID_SNN = "Invalid SNN string: %s"
29
- ERROR_INVALID_NAME = "Name must be a symbol with proper capitalization (first letter uppercase, rest lowercase), got: %s"
26
+ ERROR_INVALID_LETTER = "Letter must be a single ASCII letter symbol (A-Z, a-z), got: %s"
30
27
  ERROR_INVALID_SIDE = "Side must be :first or :second, got: %s"
31
28
 
32
- # @return [Symbol] the style name (with proper capitalization)
33
- attr_reader :name
29
+ # @return [Symbol] the style letter (single ASCII letter as symbol)
30
+ attr_reader :letter
34
31
 
35
32
  # @return [Symbol] the player side (:first or :second)
36
33
  attr_reader :side
37
34
 
38
35
  # Create a new style instance
39
36
  #
40
- # @param name [Symbol] style name (with proper capitalization)
37
+ # @param letter [Symbol] style letter (single ASCII letter as symbol)
41
38
  # @param side [Symbol] player side (:first or :second)
42
39
  # @raise [ArgumentError] if parameters are invalid
43
- def initialize(name, side)
44
- self.class.validate_name(name)
40
+ def initialize(letter, side)
41
+ self.class.validate_letter(letter)
45
42
  self.class.validate_side(side)
46
43
 
47
- @name = name
44
+ @letter = letter
48
45
  @side = side
49
46
 
50
47
  freeze
@@ -52,26 +49,24 @@ module Sashite
52
49
 
53
50
  # Parse an SNN string into a Style object
54
51
  #
55
- # @param snn_string [String] SNN notation string
56
- # @return [Style] parsed style object with normalized name and inferred side
52
+ # @param snn_string [String] SNN notation string (single ASCII letter)
53
+ # @return [Style] parsed style object with letter and inferred side
57
54
  # @raise [ArgumentError] if the SNN string is invalid
58
- # @example Parse SNN strings with case normalization
59
- # Sashite::Snn::Style.parse("CHESS") # => #<Snn::Style name=:Chess side=:first>
60
- # Sashite::Snn::Style.parse("chess") # => #<Snn::Style name=:Chess side=:second>
61
- # Sashite::Snn::Style.parse("SHOGI") # => #<Snn::Style name=:Shogi side=:first>
55
+ # @example Parse SNN strings with case-based side inference
56
+ # Sashite::Snn::Style.parse("C") # => #<Snn::Style letter=:C side=:first>
57
+ # Sashite::Snn::Style.parse("c") # => #<Snn::Style letter=:c side=:second>
58
+ # Sashite::Snn::Style.parse("S") # => #<Snn::Style letter=:S side=:first>
62
59
  def self.parse(snn_string)
63
60
  string_value = String(snn_string)
64
- matches = match_pattern(string_value)
65
-
66
- identifier = matches[:identifier]
61
+ validate_snn_string(string_value)
67
62
 
68
63
  # Determine side from case
69
- style_side = identifier == identifier.upcase ? FIRST_PLAYER : SECOND_PLAYER
64
+ style_side = string_value == string_value.upcase ? FIRST_PLAYER : SECOND_PLAYER
70
65
 
71
- # Normalize name to proper capitalization
72
- style_name = normalize_name(identifier)
66
+ # Use the letter directly as symbol
67
+ style_letter = string_value.to_sym
73
68
 
74
- new(style_name, style_side)
69
+ new(style_letter, style_side)
75
70
  end
76
71
 
77
72
  # Check if a string is a valid SNN notation
@@ -80,9 +75,9 @@ module Sashite
80
75
  # @return [Boolean] true if valid SNN, false otherwise
81
76
  #
82
77
  # @example Validate SNN strings
83
- # Sashite::Snn::Style.valid?("CHESS") # => true
84
- # Sashite::Snn::Style.valid?("chess") # => true
85
- # Sashite::Snn::Style.valid?("Chess") # => false
78
+ # Sashite::Snn::Style.valid?("C") # => true
79
+ # Sashite::Snn::Style.valid?("c") # => true
80
+ # Sashite::Snn::Style.valid?("CHESS") # => false (multi-character)
86
81
  def self.valid?(snn_string)
87
82
  return false unless snn_string.is_a?(::String)
88
83
 
@@ -91,48 +86,53 @@ module Sashite
91
86
 
92
87
  # Convert the style to its SNN string representation
93
88
  #
94
- # @return [String] SNN notation string with case based on side
95
- # @example Display different sides
96
- # style.to_s # => "CHESS" (first player)
97
- # style.to_s # => "chess" (second player)
98
- # style.to_s # => "SHOGI" (first player)
89
+ # @return [String] SNN notation string (single ASCII letter)
90
+ # @example Display styles
91
+ # style.to_s # => "C" (first player, C family)
92
+ # style.to_s # => "c" (second player, C family)
93
+ # style.to_s # => "S" (first player, S family)
99
94
  def to_s
100
- first_player? ? name.to_s.upcase : name.to_s.downcase
95
+ letter.to_s
101
96
  end
102
97
 
103
98
  # Create a new style with opposite ownership (side)
104
99
  #
105
100
  # @return [Style] new immutable style instance with flipped side
106
101
  # @example Flip player sides
107
- # style.flip # (:Chess, :first) => (:Chess, :second)
102
+ # style.flip # (:C, :first) => (:c, :second)
108
103
  def flip
109
- self.class.new(name, opposite_side)
104
+ new_letter = first_player? ? letter.to_s.downcase.to_sym : letter.to_s.upcase.to_sym
105
+ self.class.new(new_letter, opposite_side)
110
106
  end
111
107
 
112
- # Create a new style with a different name (keeping same side)
108
+ # Create a new style with a different letter (keeping same side)
113
109
  #
114
- # @param new_name [Symbol] new name (with proper capitalization)
115
- # @return [Style] new immutable style instance with different name
116
- # @example Change style name
117
- # style.with_name(:Shogi) # (:Chess, :first) => (:Shogi, :first)
118
- def with_name(new_name)
119
- self.class.validate_name(new_name)
120
- return self if name == new_name
121
-
122
- self.class.new(new_name, side)
110
+ # @param new_letter [Symbol] new letter (single ASCII letter as symbol)
111
+ # @return [Style] new immutable style instance with different letter
112
+ # @example Change style letter
113
+ # style.with_letter(:S) # (:C, :first) => (:S, :first)
114
+ def with_letter(new_letter)
115
+ self.class.validate_letter(new_letter)
116
+ return self if letter == new_letter
117
+
118
+ # Ensure the new letter has the correct case for the current side
119
+ adjusted_letter = first_player? ? new_letter.to_s.upcase.to_sym : new_letter.to_s.downcase.to_sym
120
+ self.class.new(adjusted_letter, side)
123
121
  end
124
122
 
125
- # Create a new style with a different side (keeping same name)
123
+ # Create a new style with a different side (keeping same letter family)
126
124
  #
127
125
  # @param new_side [Symbol] :first or :second
128
126
  # @return [Style] new immutable style instance with different side
129
127
  # @example Change player side
130
- # style.with_side(:second) # (:Chess, :first) => (:Chess, :second)
128
+ # style.with_side(:second) # (:C, :first) => (:c, :second)
131
129
  def with_side(new_side)
132
130
  self.class.validate_side(new_side)
133
131
  return self if side == new_side
134
132
 
135
- self.class.new(name, new_side)
133
+ # Adjust letter case for the new side
134
+ new_letter = new_side == FIRST_PLAYER ? letter.to_s.upcase.to_sym : letter.to_s.downcase.to_sym
135
+ self.class.new(new_letter, new_side)
136
136
  end
137
137
 
138
138
  # Check if the style belongs to the first player
@@ -149,16 +149,16 @@ module Sashite
149
149
  side == SECOND_PLAYER
150
150
  end
151
151
 
152
- # Check if this style has the same name as another
152
+ # Check if this style has the same letter family as another
153
153
  #
154
154
  # @param other [Style] style to compare with
155
- # @return [Boolean] true if both styles have the same name
156
- # @example Compare style names
157
- # chess1.same_name?(chess2) # (:Chess, :first) and (:Chess, :second) => true
158
- def same_name?(other)
155
+ # @return [Boolean] true if both styles use the same letter family (case-insensitive)
156
+ # @example Compare style letter families
157
+ # c_style.same_letter?(C_style) # (:c, :second) and (:C, :first) => true
158
+ def same_letter?(other)
159
159
  return false unless other.is_a?(self.class)
160
160
 
161
- name == other.name
161
+ letter.to_s.upcase == other.letter.to_s.upcase
162
162
  end
163
163
 
164
164
  # Check if this style belongs to the same side as another
@@ -174,11 +174,11 @@ module Sashite
174
174
  # Custom equality comparison
175
175
  #
176
176
  # @param other [Object] object to compare with
177
- # @return [Boolean] true if both objects are styles with identical name and side
177
+ # @return [Boolean] true if both objects are styles with identical letter and side
178
178
  def ==(other)
179
179
  return false unless other.is_a?(self.class)
180
180
 
181
- name == other.name && side == other.side
181
+ letter == other.letter && side == other.side
182
182
  end
183
183
 
184
184
  # Alias for == to ensure Set functionality works correctly
@@ -186,19 +186,19 @@ module Sashite
186
186
 
187
187
  # Custom hash implementation for use in collections
188
188
  #
189
- # @return [Integer] hash value based on class, name, and side
189
+ # @return [Integer] hash value based on class, letter, and side
190
190
  def hash
191
- [self.class, name, side].hash
191
+ [self.class, letter, side].hash
192
192
  end
193
193
 
194
- # Validate that the name is a valid symbol with proper capitalization
194
+ # Validate that the letter is a valid single ASCII letter symbol
195
195
  #
196
- # @param name [Symbol] the name to validate
196
+ # @param letter [Symbol] the letter to validate
197
197
  # @raise [ArgumentError] if invalid
198
- def self.validate_name(name)
199
- return if valid_name?(name)
198
+ def self.validate_letter(letter)
199
+ return if valid_letter?(letter)
200
200
 
201
- raise ::ArgumentError, format(ERROR_INVALID_NAME, name.inspect)
201
+ raise ::ArgumentError, format(ERROR_INVALID_LETTER, letter.inspect)
202
202
  end
203
203
 
204
204
  # Validate that the side is a valid symbol
@@ -211,44 +211,31 @@ module Sashite
211
211
  raise ::ArgumentError, format(ERROR_INVALID_SIDE, side.inspect)
212
212
  end
213
213
 
214
- # Check if a name is valid (symbol with proper capitalization)
214
+ # Check if a letter is valid (single ASCII letter symbol)
215
215
  #
216
- # @param name [Object] the name to check
216
+ # @param letter [Object] the letter to check
217
217
  # @return [Boolean] true if valid
218
- def self.valid_name?(name)
219
- return false unless name.is_a?(::Symbol)
218
+ def self.valid_letter?(letter)
219
+ return false unless letter.is_a?(::Symbol)
220
220
 
221
- name_string = name.to_s
222
- return false if name_string.empty?
221
+ letter_string = letter.to_s
222
+ return false if letter_string.empty?
223
223
 
224
- # Must match proper capitalization pattern
225
- name_string.match?(PROPER_NAME_PATTERN)
226
- end
227
-
228
- # Normalize identifier to proper capitalization symbol
229
- #
230
- # @param identifier [String] the identifier to normalize
231
- # @return [Symbol] normalized name symbol
232
- def self.normalize_name(identifier)
233
- # Convert to proper capitalization: first letter uppercase, rest lowercase
234
- normalized = identifier.downcase
235
- normalized[0] = normalized[0].upcase if normalized.length > 0
236
- normalized.to_sym
224
+ # Must be exactly one ASCII letter
225
+ letter_string.match?(SNN_PATTERN)
237
226
  end
238
227
 
239
- # Match SNN pattern against string
228
+ # Validate SNN string format
240
229
  #
241
- # @param string [String] string to match
242
- # @return [MatchData] match data
243
- # @raise [ArgumentError] if string doesn't match
244
- def self.match_pattern(string)
245
- matches = SNN_PATTERN.match(string)
246
- return matches if matches
230
+ # @param string [String] string to validate
231
+ # @raise [ArgumentError] if string doesn't match SNN pattern
232
+ def self.validate_snn_string(string)
233
+ return if string.match?(SNN_PATTERN)
247
234
 
248
235
  raise ::ArgumentError, format(ERROR_INVALID_SNN, string)
249
236
  end
250
237
 
251
- private_class_method :valid_name?, :normalize_name, :match_pattern
238
+ private_class_method :valid_letter?, :validate_snn_string
252
239
 
253
240
  private
254
241
 
data/lib/sashite/snn.rb CHANGED
@@ -6,19 +6,19 @@ module Sashite
6
6
  # SNN (Style Name Notation) implementation for Ruby
7
7
  #
8
8
  # Provides a rule-agnostic format for identifying styles in abstract strategy board games.
9
- # SNN uses standardized naming conventions with case-based side encoding, enabling clear
10
- # distinction between different traditions in multi-style gaming environments.
9
+ # SNN uses single ASCII letters with case-based side encoding, enabling clear
10
+ # distinction between different style families in multi-style gaming environments.
11
11
  #
12
- # Format: <style-identifier>
13
- # - Uppercase identifier: First player styles (CHESS, SHOGI, XIANGQI)
14
- # - Lowercase identifier: Second player styles (chess, shogi, xiangqi)
15
- # - Case consistency: Entire identifier must be uppercase or lowercase
12
+ # Format: <style-letter>
13
+ # - Uppercase letter: First player styles (A, B, C, ..., Z)
14
+ # - Lowercase letter: Second player styles (a, b, c, ..., z)
15
+ # - Single character only: Each SNN identifier is exactly one ASCII letter
16
16
  #
17
17
  # Examples:
18
- # "CHESS" - First player chess style
19
- # "chess" - Second player chess style
20
- # "SHOGI" - First player shōgi style
21
- # "shogi" - Second player shōgi style
18
+ # "C" - First player, C style family
19
+ # "c" - Second player, C style family
20
+ # "S" - First player, S style family
21
+ # "s" - Second player, S style family
22
22
  #
23
23
  # See: https://sashite.dev/specs/snn/1.0.0/
24
24
  module Snn
@@ -28,8 +28,10 @@ module Sashite
28
28
  # @return [Boolean] true if valid SNN, false otherwise
29
29
  #
30
30
  # @example Validate various SNN formats
31
- # Sashite::Snn.valid?("CHESS") # => true
32
- # Sashite::Snn.valid?("Chess") # => false
31
+ # Sashite::Snn.valid?("C") # => true
32
+ # Sashite::Snn.valid?("c") # => true
33
+ # Sashite::Snn.valid?("CHESS") # => false (multi-character)
34
+ # Sashite::Snn.valid?("1") # => false (not a letter)
33
35
  def self.valid?(snn_string)
34
36
  Style.valid?(snn_string)
35
37
  end
@@ -37,27 +39,27 @@ module Sashite
37
39
  # Parse an SNN string into a Style object
38
40
  #
39
41
  # @param snn_string [String] SNN notation string
40
- # @return [Snn::Style] parsed style object with name and side attributes
42
+ # @return [Snn::Style] parsed style object with letter and side attributes
41
43
  # @raise [ArgumentError] if the SNN string is invalid
42
44
  # @example Parse different SNN formats
43
- # Sashite::Snn.parse("CHESS") # => #<Snn::Style name=:Chess side=:first>
44
- # Sashite::Snn.parse("chess") # => #<Snn::Style name=:Chess side=:second>
45
- # Sashite::Snn.parse("SHOGI") # => #<Snn::Style name=:Shogi side=:first>
45
+ # Sashite::Snn.parse("C") # => #<Snn::Style letter=:C side=:first>
46
+ # Sashite::Snn.parse("c") # => #<Snn::Style letter=:c side=:second>
47
+ # Sashite::Snn.parse("S") # => #<Snn::Style letter=:S side=:first>
46
48
  def self.parse(snn_string)
47
49
  Style.parse(snn_string)
48
50
  end
49
51
 
50
52
  # Create a new style instance
51
53
  #
52
- # @param name [Symbol] style name (with proper capitalization)
54
+ # @param letter [Symbol] style letter (single ASCII letter as symbol)
53
55
  # @param side [Symbol] player side (:first or :second)
54
56
  # @return [Snn::Style] new immutable style instance
55
57
  # @raise [ArgumentError] if parameters are invalid
56
58
  # @example Create styles directly
57
- # Sashite::Snn.style(:Chess, :first) # => #<Snn::Style name=:Chess side=:first>
58
- # Sashite::Snn.style(:Shogi, :second) # => #<Snn::Style name=:Shogi side=:second>
59
- def self.style(name, side)
60
- Style.new(name, side)
59
+ # Sashite::Snn.style(:C, :first) # => #<Snn::Style letter=:C side=:first>
60
+ # Sashite::Snn.style(:s, :second) # => #<Snn::Style letter=:s side=:second>
61
+ def self.style(letter, side)
62
+ Style.new(letter, side)
61
63
  end
62
64
  end
63
65
  end
data/lib/sashite-snn.rb CHANGED
@@ -5,9 +5,9 @@ require_relative "sashite/snn"
5
5
  # Sashité namespace for board game notation libraries
6
6
  #
7
7
  # Sashité provides a collection of libraries for representing and manipulating
8
- # board game concepts according to the Game Protocol specifications.
8
+ # board game concepts according to the Sashité Protocol specifications.
9
9
  #
10
- # @see https://sashite.dev/game-protocol/ Game Protocol Foundation
10
+ # @see https://sashite.dev/protocol/ Sashité Protocol
11
11
  # @see https://sashite.dev/specs/ Sashité Specifications
12
12
  # @author Sashité
13
13
  module Sashite
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: 1.1.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyril Kato
@@ -13,9 +13,10 @@ description: |
13
13
  SNN (Style Name Notation) provides a rule-agnostic format for identifying styles
14
14
  in abstract strategy board games. This gem implements the SNN Specification v1.0.0 with
15
15
  a modern Ruby interface featuring immutable style objects and functional programming
16
- principles. SNN uses standardized naming conventions with case-based side encoding,
17
- enabling clear distinction between different traditions in multi-style gaming environments.
18
- Perfect for cross-tradition matches, game engines, and hybrid gaming systems.
16
+ principles. SNN uses single ASCII letters with case-based side encoding (A-Z for first player,
17
+ a-z for second player), enabling clear distinction between different style families in
18
+ multi-style gaming environments. Perfect for cross-style matches, game engines, and hybrid
19
+ gaming systems requiring compact style identification.
19
20
  email: contact@cyril.email
20
21
  executables: []
21
22
  extensions: []
@@ -34,7 +35,7 @@ metadata:
34
35
  documentation_uri: https://rubydoc.info/github/sashite/snn.rb/main
35
36
  homepage_uri: https://github.com/sashite/snn.rb
36
37
  source_code_uri: https://github.com/sashite/snn.rb
37
- specification_uri: https://sashite.dev/documents/snn/1.0.0/
38
+ specification_uri: https://sashite.dev/specs/snn/1.0.0/
38
39
  rubygems_mfa_required: 'true'
39
40
  rdoc_options: []
40
41
  require_paths: