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