sashite-ggn 0.3.0 → 0.6.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.
@@ -6,8 +6,11 @@ module Sashite
6
6
  #
7
7
  # This schema defines the structure and constraints for GGN documents,
8
8
  # which describe pseudo-legal moves in abstract strategy board games.
9
- # GGN is rule-agnostic and focuses on basic movement constraints rather
10
- # than game-specific legality (e.g., check, ko, repetition).
9
+ # GGN is rule-agnostic and focuses exclusively on board-to-board transformations:
10
+ # pieces moving, capturing, or transforming on the game board.
11
+ #
12
+ # The schema has been updated to reflect GGN's focus on board transformations only.
13
+ # Hand management, piece drops, and captures-to-hand are outside the scope of GGN.
11
14
  #
12
15
  # @example Basic GGN document structure
13
16
  # {
@@ -23,15 +26,28 @@ module Sashite
23
26
  # }
24
27
  # }
25
28
  #
26
- # @example Complex move with capture and piece gain
29
+ # @example Complex move with multiple conditions
27
30
  # {
28
- # "OGI:P": {
29
- # "e4": {
30
- # "e5": [
31
+ # "CHESS:P": {
32
+ # "d5": {
33
+ # "e6": [
31
34
  # {
32
- # "require": { "e5": "enemy" },
33
- # "perform": { "e4": null, "e5": "OGI:P" },
34
- # "gain": "OGI:P"
35
+ # "require": { "e5": "chess:p", "e6": "empty" },
36
+ # "perform": { "d5": null, "e5": null, "e6": "CHESS:P" }
37
+ # }
38
+ # ]
39
+ # }
40
+ # }
41
+ # }
42
+ #
43
+ # @example Multi-square move (castling)
44
+ # {
45
+ # "CHESS:K": {
46
+ # "e1": {
47
+ # "g1": [
48
+ # {
49
+ # "require": { "f1": "empty", "g1": "empty", "h1": "CHESS:R" },
50
+ # "perform": { "e1": null, "f1": "CHESS:R", "g1": "CHESS:K", "h1": null }
35
51
  # }
36
52
  # ]
37
53
  # }
@@ -45,7 +61,7 @@ module Sashite
45
61
  "$schema": "https://json-schema.org/draft/2020-12/schema",
46
62
  "$id": "https://sashite.dev/schemas/ggn/1.0.0/schema.json",
47
63
  "title": "General Gameplay Notation (GGN)",
48
- "description": "JSON Schema for pseudo-legal moves in abstract board games using the GGN format.",
64
+ "description": "JSON Schema for pseudo-legal moves in abstract board games using the GGN format. GGN focuses exclusively on board-to-board transformations.",
49
65
  "type": "object",
50
66
 
51
67
  # Optional schema reference property
@@ -66,82 +82,85 @@ module Sashite
66
82
  "type": "object",
67
83
  "minProperties": 1,
68
84
 
69
- # Source squares: where the piece starts (or "*" for drops)
70
- "additionalProperties": {
71
- "type": "object",
72
- "minProperties": 1,
85
+ # Source squares: where the piece starts (regular board squares only)
86
+ "patternProperties": {
87
+ ".+": {
88
+ "type": "object",
89
+ "minProperties": 1,
73
90
 
74
- # Destination squares: where the piece can move to
75
- "additionalProperties": {
76
- "type": "array",
77
- "minItems": 0,
78
-
79
- # Array of conditional transitions for this source->destination pair
80
- "items": {
81
- "type": "object",
82
- "properties": {
83
- # Conditions that MUST be satisfied before the move (logical AND)
84
- "require": {
85
- "type": "object",
86
- "minProperties": 1,
87
- "additionalProperties": {
88
- "type": "string",
89
- # Occupation states: "empty", "enemy", or exact GAN identifier
90
- "pattern": "^empty$|^enemy$|([A-Z]+:[-+]?[A-Z][']?|[a-z]+:[-+]?[a-z][']?)$"
91
- }
92
- },
91
+ # Destination squares: where the piece can move to (regular board squares only)
92
+ "patternProperties": {
93
+ ".+": {
94
+ "type": "array",
95
+ "minItems": 1,
93
96
 
94
- # Conditions that MUST NOT be satisfied before the move (logical OR)
95
- "prevent": {
97
+ # Array of conditional transitions for this source->destination pair
98
+ "items": {
96
99
  "type": "object",
97
- "minProperties": 1,
98
- "additionalProperties": {
99
- "type": "string",
100
- # Same occupation states as require
101
- "pattern": "^empty$|^enemy$|([A-Z]+:[-+]?[A-Z][']?|[a-z]+:[-+]?[a-z][']?)$"
102
- }
103
- },
100
+ "properties": {
101
+ # Conditions that MUST be satisfied before the move (logical AND)
102
+ "require": {
103
+ "type": "object",
104
+ "minProperties": 1,
105
+ "patternProperties": {
106
+ ".+": {
107
+ "type": "string",
108
+ # Occupation states: "empty", "enemy", or exact GAN identifier
109
+ "pattern": "^(empty|enemy|[A-Z]+:[-+]?[A-Z][']?|[a-z]+:[-+]?[a-z][']?)$"
110
+ }
111
+ },
112
+ "additionalProperties": false
113
+ },
104
114
 
105
- # Board state changes after the move (REQUIRED field)
106
- "perform": {
107
- "type": "object",
108
- "minProperties": 1,
109
- "additionalProperties": {
110
- "anyOf": [
111
- {
112
- # Square contains a piece (GAN identifier)
113
- "type": "string",
114
- "pattern": "^([A-Z]+:[-+]?[A-Z][']?|[a-z]+:[-+]?[a-z][']?)$"
115
+ # Conditions that MUST NOT be satisfied before the move (logical OR)
116
+ "prevent": {
117
+ "type": "object",
118
+ "minProperties": 1,
119
+ "patternProperties": {
120
+ ".+": {
121
+ "type": "string",
122
+ # Same occupation states as require
123
+ "pattern": "^(empty|enemy|[A-Z]+:[-+]?[A-Z][']?|[a-z]+:[-+]?[a-z][']?)$"
124
+ }
115
125
  },
116
- {
117
- # Square becomes empty (null)
118
- "type": "null"
119
- }
120
- ]
121
- }
122
- },
126
+ "additionalProperties": false
127
+ },
123
128
 
124
- # Piece added to player's hand (base GAN only, no modifiers)
125
- "gain": {
126
- "type": ["string", "null"],
127
- # Base form GAN pattern (no prefixes/suffixes for hand pieces)
128
- "pattern": "^([A-Z]+:[A-Z]|[a-z]+:[a-z])$"
129
- },
129
+ # Board state changes after the move (REQUIRED field)
130
+ # This is the core of GGN: describing board transformations
131
+ "perform": {
132
+ "type": "object",
133
+ "minProperties": 1,
134
+ "patternProperties": {
135
+ ".+": {
136
+ "anyOf": [
137
+ {
138
+ # Square contains a piece (GAN identifier)
139
+ "type": "string",
140
+ "pattern": "^([A-Z]+:[-+]?[A-Z][']?|[a-z]+:[-+]?[a-z][']?)$"
141
+ },
142
+ {
143
+ # Square becomes empty (null)
144
+ "type": "null"
145
+ }
146
+ ]
147
+ }
148
+ },
149
+ "additionalProperties": false
150
+ }
151
+ },
130
152
 
131
- # Piece removed from player's hand (base GAN only, no modifiers)
132
- "drop": {
133
- "type": ["string", "null"],
134
- # Base form GAN pattern (no prefixes/suffixes for hand pieces)
135
- "pattern": "^([A-Z]+:[A-Z]|[a-z]+:[a-z])$"
153
+ # Only "perform" is mandatory; "require" and "prevent" are optional
154
+ # NOTE: "gain" and "drop" fields are no longer supported in GGN
155
+ "required": ["perform"],
156
+ "additionalProperties": false
136
157
  }
137
- },
138
-
139
- # Only "perform" is mandatory; other fields are optional
140
- "required": ["perform"],
141
- "additionalProperties": false
142
- }
158
+ }
159
+ },
160
+ "additionalProperties": false
143
161
  }
144
- }
162
+ },
163
+ "additionalProperties": false
145
164
  }
146
165
  },
147
166
 
@@ -8,22 +8,47 @@ module Sashite
8
8
  # the JSON Schema, contain malformed data, or encounter processing errors
9
9
  # during parsing and evaluation of pseudo-legal moves.
10
10
  #
11
+ # Since GGN focuses exclusively on board-to-board transformations, validation
12
+ # errors typically relate to:
13
+ # - Invalid board position representations
14
+ # - Malformed GAN identifiers or square labels
15
+ # - Logical contradictions in require/prevent conditions
16
+ # - Missing or invalid perform actions
17
+ #
11
18
  # Common scenarios that raise ValidationError:
12
19
  # - Invalid JSON syntax in GGN files
13
20
  # - Schema validation failures (missing required fields, invalid patterns)
14
21
  # - File system errors (file not found, permission denied)
15
22
  # - Malformed GAN identifiers or square labels
16
23
  # - Logical contradictions in require/prevent conditions
24
+ # - Invalid board transformation specifications
17
25
  #
18
26
  # @example Handling validation errors during file loading
19
27
  # begin
20
- # piece = Sashite::Ggn.load_file('invalid_moves.json')
28
+ # piece_data = Sashite::Ggn.load_file('invalid_moves.json')
21
29
  # rescue Sashite::Ggn::ValidationError => e
22
30
  # puts "GGN validation failed: #{e.message}"
23
31
  # # Handle the error appropriately
24
32
  # end
25
33
  #
34
+ # @example Handling validation errors during move evaluation
35
+ # begin
36
+ # transitions = engine.where(board_state, 'CHESS')
37
+ # rescue Sashite::Ggn::ValidationError => e
38
+ # puts "Move evaluation failed: #{e.message}"
39
+ # # Handle invalid board state or parameters
40
+ # end
41
+ #
42
+ # @example Handling schema validation errors
43
+ # begin
44
+ # Sashite::Ggn.validate!(ggn_data)
45
+ # rescue Sashite::Ggn::ValidationError => e
46
+ # puts "Schema validation failed: #{e.message}"
47
+ # # The data doesn't conform to GGN specification
48
+ # end
49
+ #
26
50
  # @see Sashite::Ggn.load_file Main method that can raise this exception
51
+ # @see Sashite::Ggn.validate! Schema validation method
27
52
  # @see Sashite::Ggn::Schema JSON Schema used for validation
28
53
  class ValidationError < ::StandardError
29
54
  end
data/lib/sashite/ggn.rb CHANGED
@@ -4,7 +4,7 @@ require 'json'
4
4
  require 'json_schemer'
5
5
  require 'pathname'
6
6
 
7
- require_relative File.join("ggn", "piece")
7
+ require_relative File.join("ggn", "ruleset")
8
8
  require_relative File.join("ggn", "schema")
9
9
  require_relative File.join("ggn", "validation_error")
10
10
 
@@ -12,21 +12,22 @@ module Sashite
12
12
  # General Gameplay Notation (GGN) module for parsing, validating, and working with
13
13
  # JSON documents that describe pseudo-legal moves in abstract strategy board games.
14
14
  #
15
- # GGN is a rule-agnostic format that focuses on basic movement constraints rather
16
- # than game-specific legality rules. It answers the fundamental question: "Can this
17
- # piece, currently on this square, reach that square?" while remaining neutral about
18
- # higher-level game rules like check, ko, repetition, or castling paths.
15
+ # GGN is a rule-agnostic format that focuses exclusively on board-to-board transformations.
16
+ # It answers the fundamental question: "Can this piece, currently on this square, reach
17
+ # that square?" while remaining neutral about higher-level game rules like check, ko,
18
+ # repetition, or castling paths.
19
19
  #
20
- # ## Key Features
20
+ # = Key Features
21
21
  #
22
22
  # - **Rule-agnostic**: Works with any abstract strategy board game
23
- # - **Pseudo-legal focus**: Describes basic movement constraints only
23
+ # - **Board-focused**: Describes only board transformations, no hand management
24
+ # - **Pseudo-legal** focus: Describes basic movement constraints only
24
25
  # - **JSON-based**: Structured, machine-readable format
25
- # - **Validation support**: Built-in schema validation
26
- # - **Performance optimized**: Optional validation for large datasets
27
- # - **Cross-game compatible**: Supports hybrid games and variants
26
+ # - **Validation** support: Built-in schema validation
27
+ # - **Performance** optimized: Optional validation for large datasets
28
+ # - **Cross-game** compatible: Supports hybrid games and variants
28
29
  #
29
- # ## Related Specifications
30
+ # = Related Specifications
30
31
  #
31
32
  # GGN works alongside other Sashité specifications:
32
33
  # - **GAN** (General Actor Notation): Unique piece identifiers
@@ -45,7 +46,7 @@ module Sashite
45
46
  # 1. Reads the JSON file from the filesystem with proper encoding
46
47
  # 2. Parses the JSON content into a Ruby Hash with error handling
47
48
  # 3. Optionally validates the structure against the GGN JSON Schema
48
- # 4. Creates and returns a Piece instance for querying moves
49
+ # 4. Creates and returns a Ruleset instance for querying moves
49
50
  #
50
51
  # @param filepath [String, Pathname] Path to the GGN JSON file to load.
51
52
  # Supports both relative and absolute paths.
@@ -54,7 +55,7 @@ module Sashite
54
55
  # @param encoding [String] File encoding to use when reading (default: 'UTF-8').
55
56
  # Most GGN files should use UTF-8 encoding.
56
57
  #
57
- # @return [Piece] A Piece instance containing the parsed and validated GGN data.
58
+ # @return [Ruleset] A Ruleset instance containing the parsed and validated GGN data.
58
59
  # Use this instance to query pseudo-legal moves for specific pieces and positions.
59
60
  #
60
61
  # @raise [ValidationError] If any of the following conditions occur:
@@ -66,7 +67,7 @@ module Sashite
66
67
  # @example Loading a chess piece definition with full validation
67
68
  # begin
68
69
  # piece_data = Sashite::Ggn.load_file('data/chess_pieces.json')
69
- # chess_king_source = piece_data.fetch('CHESS:K')
70
+ # chess_king_source = piece_data.select('CHESS:K')
70
71
  # puts "Loaded chess king movement rules successfully"
71
72
  # rescue Sashite::Ggn::ValidationError => e
72
73
  # puts "Failed to load chess pieces: #{e.message}"
@@ -75,13 +76,13 @@ module Sashite
75
76
  # @example Complete workflow with move evaluation
76
77
  # begin
77
78
  # piece_data = Sashite::Ggn.load_file('data/chess.json')
78
- # source = piece_data.fetch('CHESS:K')
79
- # destinations = source.fetch('e1')
80
- # engine = destinations.fetch('e2')
79
+ # source = piece_data.select('CHESS:K')
80
+ # destinations = source.from('e1')
81
+ # engine = destinations.to('e2')
81
82
  #
82
83
  # board_state = { 'e1' => 'CHESS:K', 'e2' => nil }
83
- # result = engine.evaluate(board_state, {}, 'CHESS')
84
- # puts "King can move from e1 to e2" if result
84
+ # transitions = engine.where(board_state, 'CHESS')
85
+ # puts "King can move from e1 to e2" if transitions.any?
85
86
  # rescue Sashite::Ggn::ValidationError => e
86
87
  # puts "Failed to process move: #{e.message}"
87
88
  # end
@@ -122,8 +123,8 @@ module Sashite
122
123
  # Validate against GGN schema if requested
123
124
  validate_schema(data, file_path) if validate
124
125
 
125
- # Create and return Piece instance
126
- Piece.new(data)
126
+ # Create and return Ruleset instance
127
+ Ruleset.new(data)
127
128
  end
128
129
 
129
130
  # Loads GGN data directly from a JSON string.
@@ -134,7 +135,7 @@ module Sashite
134
135
  # @param json_string [String] JSON string containing GGN data
135
136
  # @param validate [Boolean] Whether to validate against GGN schema (default: true)
136
137
  #
137
- # @return [Piece] A Piece instance containing the parsed GGN data
138
+ # @return [Ruleset] A Ruleset instance containing the parsed GGN data
138
139
  #
139
140
  # @raise [ValidationError] If the JSON is invalid or doesn't conform to GGN schema
140
141
  #
@@ -143,7 +144,7 @@ module Sashite
143
144
  #
144
145
  # begin
145
146
  # piece_data = Sashite::Ggn.load_string(ggn_json)
146
- # pawn_source = piece_data.fetch('CHESS:P')
147
+ # pawn_source = piece_data.select('CHESS:P')
147
148
  # puts "Loaded pawn with move from e2 to e4"
148
149
  # rescue Sashite::Ggn::ValidationError => e
149
150
  # puts "Invalid GGN data: #{e.message}"
@@ -163,34 +164,34 @@ module Sashite
163
164
  # Validate against GGN schema if requested
164
165
  validate_schema(data, "<string>") if validate
165
166
 
166
- # Create and return Piece instance
167
- Piece.new(data)
167
+ # Create and return Ruleset instance
168
+ Ruleset.new(data)
168
169
  end
169
170
 
170
171
  # Loads GGN data from a Ruby Hash.
171
172
  #
172
173
  # This method is useful when you already have parsed JSON data as a Hash
173
- # and want to create a GGN Piece instance with optional validation.
174
+ # and want to create a GGN Ruleset instance with optional validation.
174
175
  #
175
176
  # @param data [Hash] Ruby Hash containing GGN data structure
176
177
  # @param validate [Boolean] Whether to validate against GGN schema (default: true)
177
178
  #
178
- # @return [Piece] A Piece instance containing the GGN data
179
+ # @return [Ruleset] A Ruleset instance containing the GGN data
179
180
  #
180
181
  # @raise [ValidationError] If the data doesn't conform to GGN schema (when validation enabled)
181
182
  #
182
183
  # @example Creating from existing Hash data
183
184
  # ggn_data = {
184
- # "SHOGI:K" => {
185
- # "5i" => {
186
- # "4i" => [{ "require" => { "4i" => "empty" }, "perform" => { "5i" => nil, "4i" => "SHOGI:K" } }],
187
- # "6i" => [{ "require" => { "6i" => "empty" }, "perform" => { "5i" => nil, "6i" => "SHOGI:K" } }]
185
+ # "CHESS:K" => {
186
+ # "e1" => {
187
+ # "e2" => [{ "require" => { "e2" => "empty" }, "perform" => { "e1" => nil, "e2" => "CHESS:K" } }],
188
+ # "f1" => [{ "require" => { "f1" => "empty" }, "perform" => { "e1" => nil, "f1" => "CHESS:K" } }]
188
189
  # }
189
190
  # }
190
191
  # }
191
192
  #
192
193
  # piece_data = Sashite::Ggn.load_hash(ggn_data)
193
- # shogi_king = piece_data.fetch('SHOGI:K')
194
+ # chess_king = piece_data.select('CHESS:K')
194
195
  def load_hash(data, validate: true)
195
196
  unless data.is_a?(Hash)
196
197
  raise ValidationError, "Expected Hash, got #{data.class}"
@@ -199,14 +200,14 @@ module Sashite
199
200
  # Validate against GGN schema if requested
200
201
  validate_schema(data, "<hash>") if validate
201
202
 
202
- # Create and return Piece instance
203
- Piece.new(data)
203
+ # Create and return Ruleset instance
204
+ Ruleset.new(data)
204
205
  end
205
206
 
206
207
  # Validates a data structure against the GGN JSON Schema.
207
208
  #
208
209
  # This method can be used independently to validate GGN data without
209
- # creating a Piece instance. Useful for pre-validation or testing.
210
+ # creating a Ruleset instance. Useful for pre-validation or testing.
210
211
  #
211
212
  # @param data [Hash] The data structure to validate
212
213
  # @param context [String] Context information for error messages (default: "<data>")
data/lib/sashite-ggn.rb CHANGED
@@ -6,6 +6,10 @@
6
6
  # specification, which is a rule-agnostic, JSON-based format for describing pseudo-legal
7
7
  # moves in abstract strategy board games.
8
8
  #
9
+ # GGN focuses exclusively on board-to-board transformations: pieces moving, capturing,
10
+ # or transforming on the game board. Hand management, drops, and captures-to-hand are
11
+ # outside the scope of this specification.
12
+ #
9
13
  # GGN works alongside other Sashité specifications:
10
14
  # - GAN (General Actor Notation): Unique piece identifiers
11
15
  # - FEEN (Forsyth-Edwards Enhanced Notation): Board position representation
@@ -21,7 +25,7 @@
21
25
  # require "sashite/ggn"
22
26
  #
23
27
  # piece_data = Sashite::Ggn.load_file("chess_moves.json")
24
- # engine = piece_data.fetch("CHESS:P").fetch("e2").fetch("e4")
28
+ # engine = piece_data.select("CHESS:P").from("e2").to("e4")
25
29
  #
26
30
  # # Check if the move is valid given current board state
27
31
  # board_state = {
@@ -30,59 +34,56 @@
30
34
  # "e4" => nil # Empty square
31
35
  # }
32
36
  #
33
- # result = engine.evaluate(board_state, {}, "CHESS")
37
+ # transitions = engine.where(board_state, "CHESS")
34
38
  #
35
- # if result
39
+ # if transitions.any?
40
+ # transition = transitions.first
36
41
  # puts "Move is valid!"
37
- # puts "Board changes: #{result.diff}"
42
+ # puts "Board changes: #{transition.diff}"
38
43
  # # => { "e2" => nil, "e4" => "CHESS:P" }
39
- # puts "Piece gained: #{result.gain}" # => nil (no capture)
40
- # puts "Piece dropped: #{result.drop}" # => nil (not a drop move)
41
44
  # else
42
45
  # puts "Move is not valid under current conditions"
43
46
  # end
44
47
  #
45
- # @example Piece drops in Shogi
46
- # # Shogi allows captured pieces to be dropped back onto the board
47
- # piece_data = Sashite::Ggn.load_file("shogi_moves.json")
48
- # engine = piece_data.fetch("SHOGI:P").fetch("*").fetch("5e")
49
- #
50
- # # Player has captured pawns available
51
- # captures = { "SHOGI:P" => 2 }
48
+ # @example Piece promotion with multiple variants
49
+ # # Chess pawn promotion offers multiple choices
50
+ # piece_data = Sashite::Ggn.load_file("chess_moves.json")
51
+ # engine = piece_data.select("CHESS:P").from("e7").to("e8")
52
52
  #
53
- # # Current board state (5th file is clear of unpromoted pawns)
53
+ # # Board with pawn ready to promote
54
54
  # board_state = {
55
- # "5e" => nil, # Target square is empty
56
- # "5a" => nil, "5b" => nil, "5c" => nil, "5d" => nil,
57
- # "5f" => nil, "5g" => nil, "5h" => nil, "5i" => nil
55
+ # "e7" => "CHESS:P", # White pawn on 7th rank
56
+ # "e8" => nil # Empty promotion square
58
57
  # }
59
58
  #
60
- # result = engine.evaluate(board_state, captures, "SHOGI")
59
+ # transitions = engine.where(board_state, "CHESS")
61
60
  #
62
- # if result
63
- # puts "Pawn drop is valid!"
64
- # puts "Board changes: #{result.diff}" # => { "5e" => "SHOGI:P" }
65
- # puts "Piece dropped from hand: #{result.drop}" # => "SHOGI:P"
61
+ # transitions.each_with_index do |transition, i|
62
+ # promoted_piece = transition.diff["e8"]
63
+ # puts "Promotion choice #{i + 1}: #{promoted_piece}"
66
64
  # end
65
+ # # Output: CHESS:Q, CHESS:R, CHESS:B, CHESS:N
67
66
  #
68
- # @example Captures with piece promotion
69
- # # A chess pawn capturing and promoting to queen
67
+ # @example Complex multi-square moves like castling
68
+ # # Castling involves both king and rook movement
70
69
  # piece_data = Sashite::Ggn.load_file("chess_moves.json")
71
- # engine = piece_data.fetch("CHESS:P").fetch("g7").fetch("h8")
70
+ # engine = piece_data.select("CHESS:K").from("e1").to("g1")
72
71
  #
73
- # # Board with enemy piece on h8
72
+ # # Board state allowing kingside castling
74
73
  # board_state = {
75
- # "g7" => "CHESS:P", # Our pawn ready to promote
76
- # "h8" => "chess:r" # Enemy rook (lowercase = opponent)
74
+ # "e1" => "CHESS:K", # King on starting square
75
+ # "f1" => nil, # Empty square
76
+ # "g1" => nil, # Empty destination
77
+ # "h1" => "CHESS:R" # Rook on starting square
77
78
  # }
78
79
  #
79
- # result = engine.evaluate(board_state, {}, "CHESS")
80
+ # transitions = engine.where(board_state, "CHESS")
80
81
  #
81
- # if result
82
- # puts "Pawn promotes and captures!"
83
- # puts "Final position: #{result.diff}"
84
- # # => { "g7" => nil, "h8" => "CHESS:Q" }
85
- # puts "Captured piece: #{result.gain}" # => nil (no capture _in hand_)
82
+ # if transitions.any?
83
+ # transition = transitions.first
84
+ # puts "Castling is possible!"
85
+ # puts "Final position: #{transition.diff}"
86
+ # # => { "e1" => nil, "f1" => "CHESS:R", "g1" => "CHESS:K", "h1" => nil }
86
87
  # end
87
88
  #
88
89
  # @example Loading GGN data from different sources
@@ -96,6 +97,19 @@
96
97
  # # From Hash
97
98
  # ggn_hash = { "CHESS:K" => { "e1" => { "e2" => [{ "perform" => { "e1" => nil, "e2" => "CHESS:K" } }] } } }
98
99
  # piece_data = Sashite::Ggn.load_hash(ggn_hash)
100
+ #
101
+ # @example Generating all possible moves
102
+ # # Get all pseudo-legal moves for the current position
103
+ # board_state = {
104
+ # "e1" => "CHESS:K", "d1" => "CHESS:Q", "a1" => "CHESS:R",
105
+ # "e2" => "CHESS:P", "d2" => "CHESS:P"
106
+ # }
107
+ #
108
+ # all_moves = piece_data.pseudo_legal_transitions(board_state, "CHESS")
109
+ #
110
+ # all_moves.each do |actor, origin, target, transitions|
111
+ # puts "#{actor}: #{origin} → #{target} (#{transitions.size} variants)"
112
+ # end
99
113
  module Sashite
100
114
  # Base namespace for all Sashité notation libraries.
101
115
  #
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sashite-ggn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyril Kato
@@ -24,10 +24,11 @@ dependencies:
24
24
  - !ruby/object:Gem::Version
25
25
  version: 2.4.0
26
26
  description: A Ruby implementation of the General Gameplay Notation (GGN) specification.
27
- GGN is a rule-agnostic, JSON-based format for describing pseudo-legal moves in abstract
28
- strategy board games. This library provides parsing, validation, and evaluation
29
- capabilities for GGN documents, enabling game engines to work with movement rules
30
- across different board games including Chess, Shogi, Xiangqi, and custom variants.
27
+ GGN is a rule-agnostic, JSON-based format for describing pseudo-legal board-to-board
28
+ transformations in abstract strategy board games. This library provides parsing,
29
+ validation, and evaluation capabilities for GGN documents, focusing exclusively
30
+ on piece movements, captures, and transformations on the game board. Supports Chess,
31
+ Shogi, Xiangqi, and custom variants without hand management or piece drops.
31
32
  email: contact@cyril.email
32
33
  executables: []
33
34
  extensions: []
@@ -37,11 +38,12 @@ files:
37
38
  - README.md
38
39
  - lib/sashite-ggn.rb
39
40
  - lib/sashite/ggn.rb
40
- - lib/sashite/ggn/piece.rb
41
- - lib/sashite/ggn/piece/source.rb
42
- - lib/sashite/ggn/piece/source/destination.rb
43
- - lib/sashite/ggn/piece/source/destination/engine.rb
44
- - lib/sashite/ggn/piece/source/destination/engine/transition.rb
41
+ - lib/sashite/ggn/move_validator.rb
42
+ - lib/sashite/ggn/ruleset.rb
43
+ - lib/sashite/ggn/ruleset/source.rb
44
+ - lib/sashite/ggn/ruleset/source/destination.rb
45
+ - lib/sashite/ggn/ruleset/source/destination/engine.rb
46
+ - lib/sashite/ggn/ruleset/source/destination/engine/transition.rb
45
47
  - lib/sashite/ggn/schema.rb
46
48
  - lib/sashite/ggn/validation_error.rb
47
49
  homepage: https://github.com/sashite/ggn.rb
@@ -70,5 +72,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
70
72
  requirements: []
71
73
  rubygems_version: 3.6.9
72
74
  specification_version: 4
73
- summary: General Gameplay Notation (GGN) parser and validator for Ruby
75
+ summary: General Gameplay Notation (GGN) library for board-to-board game transformations
74
76
  test_files: []