sashite-gan 2.2.0 → 4.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: 7ebee55a8937fadf15f9b2a4bcf03e672b96f832cd01482be1c0374a1314fb21
4
- data.tar.gz: 3f092d4f12e0b6a2e20d8981c76fad2be914a886a37ca95cb4d3437b9f18e67a
3
+ metadata.gz: b5469e58632f24a93eea22a4dd6d48c27028f84a2137469164eae9684274a7af
4
+ data.tar.gz: fa8fe045e1f5ced019b9bdf33369bc457e94187d7703ed13c7189c040e430502
5
5
  SHA512:
6
- metadata.gz: 472262b63c2401d6392dd65b1e39ca0600515b201cce85fcbbbacf8d245256b978c143d7313c23495cc143cf42c79a6631690a0f0eab51c0a9bf8a78cd64a259
7
- data.tar.gz: a5afb55bc54194750b0406646e0074a08f9dd5952f3c743c411a94b1cfa183aa263e7cb7b393fffce542ce5c2adf385f82ac5322e4ddfbfeb843d4a9ae0c063d
6
+ metadata.gz: 91d7fbc665bce2be60a37921398dea166684efa68ce55e22fd740d7d85907599d8812a491dbd561b73c1bb8aa1c625d453590842adb3edfb86b686e69c1f8ed8
7
+ data.tar.gz: 7761db8067e89586009fe4c52f06e632e7c153c5a7f370c613abd3066691d3d9b3fe214f71db1de1988a4a0d02df1d841b03a70b9aadb49ad89dcf3b940ef8bd
data/LICENSE.md CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014-2020 Sashite
1
+ Copyright (c) 2014-2025 Sashite
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,181 +1,414 @@
1
- # GAN.rb
1
+ # Gan.rb
2
2
 
3
- [![Build Status](https://travis-ci.org/sashite/gan.rb.svg?branch=master)](https://travis-ci.org/sashite/gan.rb)
3
+ [![Version](https://img.shields.io/github/v/tag/sashite/gan.rb?label=Version&logo=github)](https://github.com/sashite/gan.rb/tags)
4
+ [![Yard documentation](https://img.shields.io/badge/Yard-documentation-blue.svg?logo=github)](https://rubydoc.info/github/sashite/gan.rb/main)
5
+ ![Ruby](https://github.com/sashite/gan.rb/actions/workflows/main.yml/badge.svg?branch=main)
6
+ [![License](https://img.shields.io/github/license/sashite/gan.rb?label=License&logo=github)](https://github.com/sashite/gan.rb/raw/main/LICENSE.md)
4
7
 
5
- > A Ruby interface for data serialization in [General Actor Notation](https://developer.sashite.com/specs/general-actor-notation) format ♟️
8
+ > **GAN** (General Actor Notation) support for the Ruby language.
9
+
10
+ ## What is GAN?
11
+
12
+ GAN (General Actor Notation) defines a consistent and rule-agnostic format for identifying game actors in abstract strategy board games. GAN provides unambiguous identification of pieces by combining Style Name Notation (SNN) with Piece Name Notation (PNN), eliminating collision problems when multiple piece styles are present in the same context.
13
+
14
+ This gem implements the [GAN Specification v1.0.0](https://sashite.dev/documents/gan/1.0.0/), providing a Ruby interface for working with game actors through a clean and modular API that builds upon the existing [sashite-snn](https://rubygems.org/gems/sashite-snn) and [pnn](https://rubygems.org/gems/pnn) gems.
6
15
 
7
16
  ## Installation
8
17
 
9
- 1. Add the dependency to your `Gemfile`:
18
+ ```ruby
19
+ # In your Gemfile
20
+ gem "sashite-gan"
21
+ ```
22
+
23
+ Or install manually:
24
+
25
+ ```sh
26
+ gem install sashite-gan
27
+ ```
28
+
29
+ ## GAN Format
30
+
31
+ A GAN record consists of a style identifier (SNN format), followed by a colon separator, followed by a piece identifier (PNN format):
32
+
33
+ ```
34
+ <style-id>:<piece-id>
35
+ ```
36
+
37
+ Where:
38
+ - `<style-id>` is a Style Name Notation (SNN) identifier conforming to SNN specification
39
+ - `:` is a literal colon character serving as a separator
40
+ - `<piece-id>` is a Piece Name Notation (PNN) identifier conforming to PNN specification
10
41
 
11
- ```ruby
12
- gem 'sashite-gan'
13
- ```
42
+ ## Basic Usage
14
43
 
15
- 2. Run `bundle install`
44
+ ### Creating Actor Objects
16
45
 
17
- ## Usage
46
+ The primary interface is the `Sashite::Gan::Actor` class, which represents a game actor in GAN format:
18
47
 
19
48
  ```ruby
20
- require 'sashite-gan'
21
-
22
-
23
- # Chess (Western chess)'s Rook, White
24
- piece = Sashite::GAN.parse("C:R")
25
-
26
- piece.abbr.to_s # => "R"
27
- piece.style # => "C"
28
- piece.topside? # => false
29
- piece.bottomside? # => true
30
- piece.to_s # => "C:R"
31
- piece.topside.to_s # => "c:r"
32
- piece.bottomside.to_s # => "C:R"
33
- piece.oppositeside.to_s # => "c:r"
34
- piece.promote.to_s # => "C:+R"
35
- piece.unpromote.to_s # => "C:R"
36
-
37
-
38
- # Chess (Western chess)'s King, Black
39
- piece = Sashite::GAN.parse("c:-k")
40
-
41
- piece.abbr.to_s # => "-k"
42
- piece.style # => "c"
43
- piece.topside? # => true
44
- piece.bottomside? # => false
45
- piece.to_s # => "c:-k"
46
- piece.topside.to_s # => "c:-k"
47
- piece.bottomside.to_s # => "C:-K"
48
- piece.oppositeside.to_s # => "C:-K"
49
- piece.promote.to_s # => "c:+-k"
50
- piece.unpromote.to_s # => "c:-k"
51
-
52
-
53
- # Makruk (Thai chess)'s Bishop, White
54
- piece = Sashite::GAN.parse("M:B")
55
-
56
- piece.abbr.to_s # => "B"
57
- piece.style # => "M"
58
- piece.topside? # => false
59
- piece.bottomside? # => true
60
- piece.to_s # => "M:B"
61
- piece.topside.to_s # => "m:b"
62
- piece.bottomside.to_s # => "M:B"
63
- piece.oppositeside.to_s # => "m:b"
64
- piece.promote.to_s # => "M:+B"
65
- piece.unpromote.to_s # => "M:B"
66
-
67
-
68
- # Shogi (Japanese chess)'s King, Gote
69
- piece = Sashite::GAN.parse("s:-k")
70
-
71
- piece.abbr.to_s # => "-k"
72
- piece.style # => "s"
73
- piece.topside? # => true
74
- piece.bottomside? # => false
75
- piece.to_s # => "s:-k"
76
- piece.topside.to_s # => "s:-k"
77
- piece.bottomside.to_s # => "S:-K"
78
- piece.oppositeside.to_s # => "S:-K"
79
- piece.promote.to_s # => "s:+-k"
80
- piece.unpromote.to_s # => "s:-k"
81
-
82
-
83
- # Shogi (Japanese chess)'s King, Sente
84
- piece = Sashite::GAN.parse("S:-K")
85
-
86
- piece.abbr.to_s # => "-K"
87
- piece.style # => "S"
88
- piece.topside? # => false
89
- piece.bottomside? # => true
90
- piece.to_s # => "S:-K"
91
- piece.topside.to_s # => "s:-k"
92
- piece.bottomside.to_s # => "S:-K"
93
- piece.oppositeside.to_s # => "s:-k"
94
- piece.promote.to_s # => "S:+-K"
95
- piece.unpromote.to_s # => "S:-K"
96
-
97
-
98
- # Shogi (Japanese chess)'s promoted Pawn, Sente
99
- piece = Sashite::GAN.parse("S:+P")
100
-
101
- piece.abbr.to_s # => "+P"
102
- piece.style # => "S"
103
- piece.topside? # => false
104
- piece.bottomside? # => true
105
- piece.to_s # => "S:+P"
106
- piece.topside.to_s # => "s:+p"
107
- piece.bottomside.to_s # => "S:+P"
108
- piece.oppositeside.to_s # => "s:+p"
109
- piece.promote.to_s # => "S:+P"
110
- piece.unpromote.to_s # => "S:P"
111
-
112
-
113
- # Xiangqi (Chinese chess)'s General, Red
114
- piece = Sashite::GAN.parse("X:-G")
115
-
116
- piece.abbr.to_s # => "-G"
117
- piece.style # => "X"
118
- piece.topside? # => false
119
- piece.bottomside? # => true
120
- piece.to_s # => "X:-G"
121
- piece.topside.to_s # => "x:-g"
122
- piece.bottomside.to_s # => "X:-G"
123
- piece.oppositeside.to_s # => "x:-g"
124
- piece.promote.to_s # => "X:+-G"
125
- piece.unpromote.to_s # => "X:-G"
126
-
127
-
128
- # Xiangqi (Chinese chess)'s Flying General, Red
129
- piece = Sashite::GAN.parse("X:+-G")
130
-
131
- piece.abbr.to_s # => "+-G"
132
- piece.style # => "X"
133
- piece.topside? # => false
134
- piece.bottomside? # => true
135
- piece.to_s # => "X:+-G"
136
- piece.topside.to_s # => "x:+-g"
137
- piece.bottomside.to_s # => "X:+-G"
138
- piece.oppositeside.to_s # => "x:+-g"
139
- piece.promote.to_s # => "X:+-G"
140
- piece.unpromote.to_s # => "X:-G"
141
-
142
-
143
- # Dai Dai Shogi (huge Japanese chess)'s Phoenix, Sente
144
- piece = Sashite::GAN.parse("DAI_DAI_SHOGI:PH")
145
-
146
- piece.abbr.to_s # => "PH"
147
- piece.style # => "DAI_DAI_SHOGI"
148
- piece.topside? # => false
149
- piece.bottomside? # => true
150
- piece.to_s # => "DAI_DAI_SHOGI:PH"
151
- piece.topside.to_s # => "dai_dai_shogi:ph"
152
- piece.bottomside.to_s # => "DAI_DAI_SHOGI:PH"
153
- piece.oppositeside.to_s # => "dai_dai_shogi:ph"
154
- piece.promote.to_s # => "DAI_DAI_SHOGI:+PH"
155
- piece.unpromote.to_s # => "DAI_DAI_SHOGI:PH"
156
-
157
-
158
- # A random FOO chess variant's promoted Z piece, Bottom-side
159
- piece = Sashite::GAN.parse("FOO:+Z")
160
-
161
- piece.abbr.to_s # => "+Z"
162
- piece.style # => "FOO"
163
- piece.topside? # => false
164
- piece.bottomside? # => true
165
- piece.to_s # => "FOO:+Z"
166
- piece.topside.to_s # => "foo:+z"
167
- piece.bottomside.to_s # => "FOO:+Z"
168
- piece.oppositeside.to_s # => "foo:+z"
169
- piece.promote.to_s # => "FOO:+Z"
170
- piece.unpromote.to_s # => "FOO:Z"
49
+ require "sashite/gan"
50
+
51
+ # Parse a GAN string into an actor object
52
+ actor = Sashite::Gan::Actor.parse("CHESS:K")
53
+ # => #<Sashite::Gan::Actor:0x... @style="CHESS" @piece="K">
54
+
55
+ # With piece modifiers
56
+ enhanced_actor = Sashite::Gan::Actor.parse("SHOGI:+P")
57
+ # => #<Sashite::Gan::Actor:0x... @style="SHOGI" @piece="+P">
58
+
59
+ # Create directly with constructor
60
+ actor = Sashite::Gan::Actor.new("CHESS", "K")
61
+ enhanced_actor = Sashite::Gan::Actor.new("SHOGI", "+P")
62
+
63
+ # Create with style and piece objects
64
+ style = Sashite::Snn::Style.new("CHESS")
65
+ piece = Pnn::Piece.new("K")
66
+ actor = Sashite::Gan::Actor.new(style, piece)
67
+
68
+ # Convenience method
69
+ actor = Sashite::Gan.actor("CHESS", "K")
171
70
  ```
172
71
 
173
- ## License
72
+ ### Converting to GAN String
73
+
74
+ Convert an actor object back to its GAN string representation:
75
+
76
+ ```ruby
77
+ actor = Sashite::Gan::Actor.parse("CHESS:K")
78
+ actor.to_s
79
+ # => "CHESS:K"
80
+
81
+ enhanced_actor = Sashite::Gan::Actor.parse("SHOGI:+p'")
82
+ enhanced_actor.to_s
83
+ # => "SHOGI:+p'"
84
+ ```
85
+
86
+ ### Accessing Components
87
+
88
+ Access the style and piece components of an actor:
89
+
90
+ ```ruby
91
+ actor = Sashite::Gan::Actor.parse("CHESS:K")
92
+
93
+ # Access as strings
94
+ actor.style_name # => "CHESS"
95
+ actor.piece_name # => "K"
96
+
97
+ # Access as objects
98
+ actor.style # => #<Sashite::Snn::Style:0x... @identifier="CHESS">
99
+ actor.piece # => #<Pnn::Piece:0x... @letter="K">
100
+
101
+ # Check player associations
102
+ actor.style.first_player? # => true
103
+ actor.piece.uppercase? # => true
104
+ ```
105
+
106
+ ## Casing Combinations and Player Association
107
+
108
+ GAN allows all four combinations of case between style and piece identifiers to support dynamic ownership changes:
109
+
110
+ ```ruby
111
+ # First player's style, first player's piece
112
+ actor1 = Sashite::Gan::Actor.parse("CHESS:K")
113
+ actor1.style.first_player? # => true
114
+ actor1.piece.uppercase? # => true
115
+
116
+ # First player's style, second player's piece (piece was captured and converted)
117
+ actor2 = Sashite::Gan::Actor.parse("CHESS:k")
118
+ actor2.style.first_player? # => true
119
+ actor2.piece.lowercase? # => true
120
+
121
+ # Second player's style, first player's piece (piece was captured and converted)
122
+ actor3 = Sashite::Gan::Actor.parse("chess:K")
123
+ actor3.style.second_player? # => true
124
+ actor3.piece.uppercase? # => true
125
+
126
+ # Second player's style, second player's piece
127
+ actor4 = Sashite::Gan::Actor.parse("chess:k")
128
+ actor4.style.second_player? # => true
129
+ actor4.piece.lowercase? # => true
130
+ ```
131
+
132
+ ## Dynamic Ownership Changes
133
+
134
+ While style assignment remains fixed throughout a game, piece ownership may change during gameplay:
135
+
136
+ ```ruby
137
+ # Original piece owned by first player
138
+ original = Sashite::Gan::Actor.parse("SHOGI:P")
139
+
140
+ # After capture by second player (modifiers preserved by default)
141
+ captured = original.change_piece_ownership
142
+ captured.to_s # => "SHOGI:p"
143
+
144
+ # Or create the captured version directly
145
+ captured = Sashite::Gan::Actor.new(original.style, "p")
146
+
147
+ # Example with enhanced piece - modifiers are preserved
148
+ enhanced = Sashite::Gan::Actor.parse("SHOGI:+P")
149
+ captured_enhanced = enhanced.change_piece_ownership
150
+ captured_enhanced.to_s # => "SHOGI:+p" (modifiers preserved)
151
+
152
+ # To remove modifiers explicitly (if game rules require it):
153
+ bare_captured = enhanced.bare_piece.change_piece_ownership
154
+ bare_captured.to_s # => "SHOGI:p" (modifiers removed)
155
+ ```
156
+
157
+ ## Traditional Same-Style Games
158
+
159
+ In traditional games where both players use the same piece style:
160
+
161
+ ```ruby
162
+ # Chess pieces
163
+ white_king = Sashite::Gan::Actor.parse("CHESS:K")
164
+ black_king = Sashite::Gan::Actor.parse("chess:k")
165
+
166
+ white_queen = Sashite::Gan::Actor.parse("CHESS:Q")
167
+ black_queen = Sashite::Gan::Actor.parse("chess:q")
168
+
169
+ # Shogi pieces
170
+ first_king = Sashite::Gan::Actor.parse("SHOGI:K")
171
+ second_king = Sashite::Gan::Actor.parse("shogi:k")
172
+
173
+ first_gold = Sashite::Gan::Actor.parse("SHOGI:G")
174
+ second_gold = Sashite::Gan::Actor.parse("shogi:g")
175
+ ```
176
+
177
+ ## Cross-Style Games
178
+
179
+ In games where players use different piece styles:
180
+
181
+ ```ruby
182
+ # Chess vs Makruk
183
+ chess_king = Sashite::Gan::Actor.parse("CHESS:K")
184
+ makruk_king = Sashite::Gan::Actor.parse("makruk:k")
185
+
186
+ chess_queen = Sashite::Gan::Actor.parse("CHESS:Q")
187
+ makruk_queen = Sashite::Gan::Actor.parse("makruk:q")
188
+
189
+ # Shogi vs Xiangqi
190
+ shogi_king = Sashite::Gan::Actor.parse("SHOGI:K")
191
+ xiangqi_general = Sashite::Gan::Actor.parse("xiangqi:g")
192
+
193
+ shogi_gold = Sashite::Gan::Actor.parse("SHOGI:G")
194
+ xiangqi_advisor = Sashite::Gan::Actor.parse("xiangqi:a")
195
+ ```
196
+
197
+ ## Pieces with States and Ownership Changes
198
+
199
+ ```ruby
200
+ # Original enhanced piece
201
+ original = Sashite::Gan::Actor.parse("CHESS:R'")
202
+
203
+ # After capture (modifiers preserved by default)
204
+ captured = original.change_piece_ownership
205
+ captured.to_s # => "chess:R'"
206
+
207
+ # If game rules require modifier removal during capture:
208
+ captured_bare = original.bare_piece.change_piece_ownership
209
+ captured_bare.to_s # => "chess:R"
210
+
211
+ # Promoted shogi piece captured
212
+ promoted_pawn = Sashite::Gan::Actor.parse("shogi:+p")
213
+ captured_promoted = promoted_pawn.change_piece_ownership
214
+ captured_promoted.to_s # => "SHOGI:+p" (modifiers preserved)
215
+
216
+ # With explicit modifier removal:
217
+ captured_demoted = promoted_pawn.bare_piece.change_piece_ownership
218
+ captured_demoted.to_s # => "SHOGI:p"
219
+ ```
220
+
221
+ ## Collision Resolution
222
+
223
+ GAN resolves naming conflicts between different styles:
224
+
225
+ ```ruby
226
+ # All different actors despite similar piece types
227
+ chess_rook = Sashite::Gan::Actor.parse("CHESS:R")
228
+ shogi_rook = Sashite::Gan::Actor.parse("SHOGI:R")
229
+ makruk_rook = Sashite::Gan::Actor.parse("MAKRUK:R")
230
+ xiangqi_chariot = Sashite::Gan::Actor.parse("xiangqi:r")
231
+
232
+ # They can all coexist in the same context
233
+ pieces = [chess_rook, shogi_rook, makruk_rook, xiangqi_chariot]
234
+ puts pieces.map(&:to_s)
235
+ # => ["CHESS:R", "SHOGI:R", "MAKRUK:R", "xiangqi:r"]
236
+ ```
237
+
238
+ ## Advanced Usage
239
+
240
+ ### Working with Collections
241
+
242
+ ```ruby
243
+ # Group actors by style
244
+ actors = [
245
+ Sashite::Gan::Actor.parse("CHESS:K"),
246
+ Sashite::Gan::Actor.parse("CHESS:Q"),
247
+ Sashite::Gan::Actor.parse("shogi:k"),
248
+ Sashite::Gan::Actor.parse("shogi:g")
249
+ ]
250
+
251
+ grouped = actors.group_by { |actor| actor.style_name.downcase }
252
+ # => {"chess" => [...], "shogi" => [...]}
253
+
254
+ # Filter by player
255
+ first_player_actors = actors.select { |actor| actor.style.first_player? }
256
+ second_player_actors = actors.select { |actor| actor.style.second_player? }
257
+
258
+ # Find actors by piece type
259
+ kings = actors.select { |actor| actor.piece_name.downcase == "k" }
260
+ ```
261
+
262
+ ### State Manipulation
263
+
264
+ ```ruby
265
+ actor = Sashite::Gan::Actor.parse("SHOGI:P")
266
+
267
+ # Enhance the piece
268
+ enhanced = actor.enhance_piece
269
+ enhanced.to_s # => "SHOGI:+P"
270
+
271
+ # Add intermediate state
272
+ intermediate = actor.set_piece_intermediate
273
+ intermediate.to_s # => "SHOGI:P'"
274
+
275
+ # Chain operations
276
+ complex = actor.enhance_piece.set_piece_intermediate
277
+ complex.to_s # => "SHOGI:+P'"
278
+
279
+ # Remove all modifiers
280
+ bare = complex.bare_piece
281
+ bare.to_s # => "SHOGI:P"
282
+ ```
283
+
284
+ ### Validation
285
+
286
+ All parsing automatically validates input according to the GAN specification:
287
+
288
+ ```ruby
289
+ # Valid GAN strings
290
+ Sashite::Gan::Actor.parse("CHESS:K") # ✓
291
+ Sashite::Gan::Actor.parse("shogi:+p") # ✓
292
+ Sashite::Gan::Actor.parse("XIANGQI:r'") # ✓
293
+
294
+ # Valid constructor calls
295
+ Sashite::Gan::Actor.new("CHESS", "K") # ✓
296
+ Sashite::Gan::Actor.new("shogi", "+p") # ✓
297
+
298
+ # Convenience method
299
+ Sashite::Gan.actor("MAKRUK", "Q") # ✓
300
+
301
+ # Check validity
302
+ Sashite::Gan.valid?("CHESS:K") # => true
303
+ Sashite::Gan.valid?("Chess:K") # => false (mixed case in style)
304
+ Sashite::Gan.valid?("CHESS") # => false (missing piece)
305
+ Sashite::Gan.valid?("") # => false (empty string)
306
+
307
+ # Invalid GAN strings raise ArgumentError
308
+ Sashite::Gan::Actor.parse("") # ✗ ArgumentError
309
+ Sashite::Gan::Actor.parse("Chess:K") # ✗ ArgumentError (mixed case)
310
+ Sashite::Gan::Actor.parse("CHESS") # ✗ ArgumentError (missing piece)
311
+ Sashite::Gan::Actor.parse("CHESS:++K") # ✗ ArgumentError (invalid piece)
312
+ ```
313
+
314
+ ### Inspection and Debugging
315
+
316
+ ```ruby
317
+ actor = Sashite::Gan::Actor.parse("SHOGI:+p'")
318
+
319
+ # Get detailed information
320
+ actor.inspect
321
+ # => "#<Sashite::Gan::Actor:0x... style=\"SHOGI\" piece=\"+p'\">"
322
+
323
+ # Check components
324
+ actor.style_name # => "SHOGI"
325
+ actor.piece_name # => "+p'"
326
+ actor.piece.enhanced? # => true
327
+ actor.piece.intermediate? # => true
328
+ ```
329
+
330
+ ## API Reference
331
+
332
+ ### Module Methods
333
+
334
+ - `Sashite::Gan.valid?(gan_string)` - Check if a string is valid GAN notation
335
+ - `Sashite::Gan.actor(style, piece)` - Convenience method to create actors
336
+
337
+ ### Sashite::Gan::Actor Class Methods
338
+
339
+ - `Sashite::Gan::Actor.parse(gan_string)` - Parse a GAN string into an actor object
340
+ - `Sashite::Gan::Actor.new(style, piece)` - Create a new actor instance
341
+
342
+ ### Instance Methods
343
+
344
+ #### Component Access
345
+ - `#style` - Get the style object (Sashite::Snn::Style)
346
+ - `#piece` - Get the piece object (Pnn::Piece)
347
+ - `#style_name` - Get the style name as string
348
+ - `#piece_name` - Get the piece name as string
349
+
350
+ #### Piece State Manipulation
351
+ - `#enhance_piece` - Create actor with enhanced piece
352
+ - `#diminish_piece` - Create actor with diminished piece
353
+ - `#set_piece_intermediate` - Create actor with intermediate piece state
354
+ - `#bare_piece` - Create actor with piece without modifiers
355
+ - `#change_piece_ownership` - Create actor with piece ownership flipped
356
+
357
+ #### Conversion
358
+ - `#to_s` - Convert to GAN string representation
359
+ - `#inspect` - Detailed string representation for debugging
174
360
 
175
- The code is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
361
+ ## Properties of GAN
362
+
363
+ * **Rule-agnostic**: GAN does not encode game states, legality, validity, or game-specific conditions
364
+ * **Unambiguous identification**: Different piece styles can coexist without naming conflicts
365
+ * **Canonical representation**: Equivalent actors yield identical strings
366
+ * **Cross-style support**: Enables games where pieces from multiple traditions may be present
367
+ * **Dynamic ownership**: Supports games where piece ownership can change during gameplay
368
+ * **Compositional architecture**: Built on independent SNN and PNN specifications
369
+
370
+ ## Constraints
371
+
372
+ * GAN supports exactly **two players**
373
+ * Players are distinguished through the combination of SNN and PNN casing
374
+ * Style assignment to players remains **fixed throughout a game**
375
+ * Piece ownership may change during gameplay through casing changes
376
+ * Both style and piece identifiers must conform to their respective specifications
377
+
378
+ ## Use Cases
379
+
380
+ GAN is particularly useful in the following scenarios:
381
+
382
+ 1. **Multi-style environments**: When positions or analyses involve pieces from multiple style traditions
383
+ 2. **Game engine development**: When implementing engines that need to distinguish between similar pieces from different styles while tracking ownership changes
384
+ 3. **Hybrid games**: When creating or analyzing positions from games that combine elements from different piece traditions
385
+ 4. **Database systems**: When storing game data that must avoid naming conflicts between similar pieces from different styles
386
+ 5. **Cross-style analysis**: When comparing or analyzing strategic elements across different piece traditions
387
+ 6. **Capture-conversion games**: When implementing games like shōgi where pieces change ownership and require clear ownership tracking
388
+
389
+ ## Dependencies
390
+
391
+ This gem depends on:
392
+
393
+ - [sashite-snn](https://github.com/sashite/snn.rb) (~> 1.0.0) - Style Name Notation implementation
394
+ - [pnn](https://github.com/sashite/pnn.rb) (~> 2.0.0) - Piece Name Notation implementation
395
+
396
+ ## Specification
397
+
398
+ - [GAN Specification](https://sashite.dev/documents/gan/1.0.0/)
399
+ - [SNN Specification](https://sashite.dev/documents/snn/1.0.0/)
400
+ - [PNN Specification](https://sashite.dev/documents/pnn/1.0.0/)
401
+
402
+ ## Documentation
403
+
404
+ - [GAN Documentation](https://rubydoc.info/github/sashite/gan.rb/main)
405
+ - [SNN Documentation](https://rubydoc.info/github/sashite/snn.rb/main)
406
+ - [PNN Documentation](https://rubydoc.info/github/sashite/pnn.rb/main)
407
+
408
+ ## License
176
409
 
177
- ## About Sashite
410
+ The [gem](https://rubygems.org/gems/sashite-gan) is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
178
411
 
179
- This [gem](https://rubygems.org/gems/sashite-gan) is maintained by [Sashite](https://sashite.com/).
412
+ ## About Sashité
180
413
 
181
- With some [lines of code](https://github.com/sashite/), let's share the beauty of Chinese, Japanese and Western cultures through the game of chess!
414
+ This project is maintained by [Sashité](https://sashite.com/) promoting chess variants and sharing the beauty of Chinese, Japanese, and Western chess cultures.