sashite-ggn 0.5.0 → 0.7.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 +4 -4
- data/README.md +690 -63
- data/lib/sashite/ggn/move_validator.rb +97 -69
- data/lib/sashite/ggn/ruleset/source/destination/engine/transition.rb +41 -50
- data/lib/sashite/ggn/ruleset/source/destination/engine.rb +92 -172
- data/lib/sashite/ggn/ruleset/source/destination.rb +53 -7
- data/lib/sashite/ggn/ruleset/source.rb +42 -14
- data/lib/sashite/ggn/ruleset.rb +205 -107
- data/lib/sashite/ggn/schema.rb +96 -77
- data/lib/sashite/ggn/validation_error.rb +26 -1
- data/lib/sashite/ggn.rb +48 -25
- data/lib/sashite-ggn.rb +47 -33
- metadata +11 -6
data/lib/sashite/ggn/schema.rb
CHANGED
@@ -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
|
10
|
-
#
|
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
|
29
|
+
# @example Complex move with multiple conditions
|
27
30
|
# {
|
28
|
-
# "
|
29
|
-
# "
|
30
|
-
# "
|
31
|
+
# "CHESS:P": {
|
32
|
+
# "d5": {
|
33
|
+
# "e6": [
|
31
34
|
# {
|
32
|
-
# "require": { "e5": "
|
33
|
-
# "perform": { "
|
34
|
-
#
|
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 (
|
70
|
-
"
|
71
|
-
"
|
72
|
-
|
85
|
+
# Source squares: where the piece starts (regular board squares only)
|
86
|
+
"patternProperties": {
|
87
|
+
".+": {
|
88
|
+
"type": "object",
|
89
|
+
"minProperties": 1,
|
73
90
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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
|
-
#
|
95
|
-
"
|
97
|
+
# Array of conditional transitions for this source->destination pair
|
98
|
+
"items": {
|
96
99
|
"type": "object",
|
97
|
-
"
|
98
|
-
|
99
|
-
"
|
100
|
-
|
101
|
-
|
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
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
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
|
-
|
118
|
-
"type": "null"
|
119
|
-
}
|
120
|
-
]
|
121
|
-
}
|
122
|
-
},
|
126
|
+
"additionalProperties": false
|
127
|
+
},
|
123
128
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
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
|
-
|
132
|
-
|
133
|
-
"
|
134
|
-
|
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
|
-
|
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
|
-
#
|
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
@@ -12,20 +12,30 @@ 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
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
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
20
|
# = Key Features
|
21
21
|
#
|
22
22
|
# - **Rule-agnostic**: Works with any abstract strategy board game
|
23
|
+
# - **Board-focused**: Describes only board transformations, no hand management
|
23
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
|
+
# - **Validation** support: Built-in schema validation and logical consistency checks
|
26
27
|
# - **Performance** optimized: Optional validation for large datasets
|
27
28
|
# - **Cross-game** compatible: Supports hybrid games and variants
|
28
29
|
#
|
30
|
+
# = Validation Levels
|
31
|
+
#
|
32
|
+
# When `validate: true` (default), performs:
|
33
|
+
# - JSON Schema validation against GGN specification
|
34
|
+
# - Logical contradiction detection in require/prevent conditions
|
35
|
+
# - Implicit requirement duplication detection
|
36
|
+
#
|
37
|
+
# When `validate: false`, skips all validations for maximum performance.
|
38
|
+
#
|
29
39
|
# = Related Specifications
|
30
40
|
#
|
31
41
|
# GGN works alongside other Sashité specifications:
|
@@ -45,16 +55,18 @@ module Sashite
|
|
45
55
|
# 1. Reads the JSON file from the filesystem with proper encoding
|
46
56
|
# 2. Parses the JSON content into a Ruby Hash with error handling
|
47
57
|
# 3. Optionally validates the structure against the GGN JSON Schema
|
48
|
-
# 4.
|
58
|
+
# 4. Optionally performs logical consistency validation
|
59
|
+
# 5. Creates and returns a Ruleset instance for querying moves
|
49
60
|
#
|
50
61
|
# @param filepath [String, Pathname] Path to the GGN JSON file to load.
|
51
62
|
# Supports both relative and absolute paths.
|
52
|
-
# @param validate [Boolean] Whether to
|
53
|
-
#
|
63
|
+
# @param validate [Boolean] Whether to perform all validations (default: true).
|
64
|
+
# When false, skips JSON schema validation AND internal logical validations
|
65
|
+
# for maximum performance.
|
54
66
|
# @param encoding [String] File encoding to use when reading (default: 'UTF-8').
|
55
67
|
# Most GGN files should use UTF-8 encoding.
|
56
68
|
#
|
57
|
-
# @return [Ruleset] A Ruleset instance containing the parsed
|
69
|
+
# @return [Ruleset] A Ruleset instance containing the parsed GGN data.
|
58
70
|
# Use this instance to query pseudo-legal moves for specific pieces and positions.
|
59
71
|
#
|
60
72
|
# @raise [ValidationError] If any of the following conditions occur:
|
@@ -62,6 +74,7 @@ module Sashite
|
|
62
74
|
# - File contains invalid JSON syntax
|
63
75
|
# - File permissions prevent reading
|
64
76
|
# - When validation is enabled: data doesn't conform to GGN schema
|
77
|
+
# - When validation is enabled: logical contradictions or implicit duplications found
|
65
78
|
#
|
66
79
|
# @example Loading a chess piece definition with full validation
|
67
80
|
# begin
|
@@ -80,15 +93,15 @@ module Sashite
|
|
80
93
|
# engine = destinations.to('e2')
|
81
94
|
#
|
82
95
|
# board_state = { 'e1' => 'CHESS:K', 'e2' => nil }
|
83
|
-
#
|
84
|
-
# puts "King can move from e1 to e2" if
|
96
|
+
# transitions = engine.where(board_state, 'CHESS')
|
97
|
+
# puts "King can move from e1 to e2" if transitions.any?
|
85
98
|
# rescue Sashite::Ggn::ValidationError => e
|
86
99
|
# puts "Failed to process move: #{e.message}"
|
87
100
|
# end
|
88
101
|
#
|
89
102
|
# @example Loading large datasets without validation for performance
|
90
103
|
# begin
|
91
|
-
# # Skip
|
104
|
+
# # Skip all validations for large files to improve loading performance
|
92
105
|
# large_dataset = Sashite::Ggn.load_file('data/all_variants.json', validate: false)
|
93
106
|
# puts "Loaded GGN data without validation"
|
94
107
|
# rescue Sashite::Ggn::ValidationError => e
|
@@ -122,8 +135,8 @@ module Sashite
|
|
122
135
|
# Validate against GGN schema if requested
|
123
136
|
validate_schema(data, file_path) if validate
|
124
137
|
|
125
|
-
# Create and return Ruleset instance
|
126
|
-
Ruleset.new(data)
|
138
|
+
# Create and return Ruleset instance with validation option
|
139
|
+
Ruleset.new(data, validate: validate)
|
127
140
|
end
|
128
141
|
|
129
142
|
# Loads GGN data directly from a JSON string.
|
@@ -132,7 +145,8 @@ module Sashite
|
|
132
145
|
# database, API response, or embedded in your application) rather than a file.
|
133
146
|
#
|
134
147
|
# @param json_string [String] JSON string containing GGN data
|
135
|
-
# @param validate [Boolean] Whether to
|
148
|
+
# @param validate [Boolean] Whether to perform all validations (default: true).
|
149
|
+
# When false, skips JSON schema validation AND internal logical validations.
|
136
150
|
#
|
137
151
|
# @return [Ruleset] A Ruleset instance containing the parsed GGN data
|
138
152
|
#
|
@@ -163,8 +177,8 @@ module Sashite
|
|
163
177
|
# Validate against GGN schema if requested
|
164
178
|
validate_schema(data, "<string>") if validate
|
165
179
|
|
166
|
-
# Create and return Ruleset instance
|
167
|
-
Ruleset.new(data)
|
180
|
+
# Create and return Ruleset instance with validation option
|
181
|
+
Ruleset.new(data, validate: validate)
|
168
182
|
end
|
169
183
|
|
170
184
|
# Loads GGN data from a Ruby Hash.
|
@@ -173,7 +187,8 @@ module Sashite
|
|
173
187
|
# and want to create a GGN Ruleset instance with optional validation.
|
174
188
|
#
|
175
189
|
# @param data [Hash] Ruby Hash containing GGN data structure
|
176
|
-
# @param validate [Boolean] Whether to
|
190
|
+
# @param validate [Boolean] Whether to perform all validations (default: true).
|
191
|
+
# When false, skips JSON schema validation AND internal logical validations.
|
177
192
|
#
|
178
193
|
# @return [Ruleset] A Ruleset instance containing the GGN data
|
179
194
|
#
|
@@ -181,16 +196,16 @@ module Sashite
|
|
181
196
|
#
|
182
197
|
# @example Creating from existing Hash data
|
183
198
|
# ggn_data = {
|
184
|
-
# "
|
185
|
-
# "
|
186
|
-
# "
|
187
|
-
# "
|
199
|
+
# "CHESS:K" => {
|
200
|
+
# "e1" => {
|
201
|
+
# "e2" => [{ "require" => { "e2" => "empty" }, "perform" => { "e1" => nil, "e2" => "CHESS:K" } }],
|
202
|
+
# "f1" => [{ "require" => { "f1" => "empty" }, "perform" => { "e1" => nil, "f1" => "CHESS:K" } }]
|
188
203
|
# }
|
189
204
|
# }
|
190
205
|
# }
|
191
206
|
#
|
192
207
|
# piece_data = Sashite::Ggn.load_hash(ggn_data)
|
193
|
-
#
|
208
|
+
# chess_king = piece_data.select('CHESS:K')
|
194
209
|
def load_hash(data, validate: true)
|
195
210
|
unless data.is_a?(Hash)
|
196
211
|
raise ValidationError, "Expected Hash, got #{data.class}"
|
@@ -199,14 +214,16 @@ module Sashite
|
|
199
214
|
# Validate against GGN schema if requested
|
200
215
|
validate_schema(data, "<hash>") if validate
|
201
216
|
|
202
|
-
# Create and return Ruleset instance
|
203
|
-
Ruleset.new(data)
|
217
|
+
# Create and return Ruleset instance with validation option
|
218
|
+
Ruleset.new(data, validate: validate)
|
204
219
|
end
|
205
220
|
|
206
221
|
# Validates a data structure against the GGN JSON Schema.
|
207
222
|
#
|
208
223
|
# This method can be used independently to validate GGN data without
|
209
224
|
# creating a Ruleset instance. Useful for pre-validation or testing.
|
225
|
+
# Note: This only performs JSON Schema validation, not the internal
|
226
|
+
# logical consistency checks that Ruleset.new performs.
|
210
227
|
#
|
211
228
|
# @param data [Hash] The data structure to validate
|
212
229
|
# @param context [String] Context information for error messages (default: "<data>")
|
@@ -229,6 +246,9 @@ module Sashite
|
|
229
246
|
|
230
247
|
# Checks if a data structure is valid GGN format.
|
231
248
|
#
|
249
|
+
# Note: This only performs JSON Schema validation, not the internal
|
250
|
+
# logical consistency checks that Ruleset.new performs.
|
251
|
+
#
|
232
252
|
# @param data [Hash] The data structure to validate
|
233
253
|
#
|
234
254
|
# @return [Boolean] true if valid, false otherwise
|
@@ -246,6 +266,9 @@ module Sashite
|
|
246
266
|
|
247
267
|
# Returns detailed validation errors for a data structure.
|
248
268
|
#
|
269
|
+
# Note: This only performs JSON Schema validation, not the internal
|
270
|
+
# logical consistency checks that Ruleset.new performs.
|
271
|
+
#
|
249
272
|
# @param data [Hash] The data structure to validate
|
250
273
|
#
|
251
274
|
# @return [Array<String>] Array of validation error messages (empty if valid)
|
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
|
@@ -30,59 +34,56 @@
|
|
30
34
|
# "e4" => nil # Empty square
|
31
35
|
# }
|
32
36
|
#
|
33
|
-
#
|
37
|
+
# transitions = engine.where(board_state, "CHESS")
|
34
38
|
#
|
35
|
-
# if
|
39
|
+
# if transitions.any?
|
40
|
+
# transition = transitions.first
|
36
41
|
# puts "Move is valid!"
|
37
|
-
# puts "Board changes: #{
|
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
|
46
|
-
# #
|
47
|
-
# piece_data = Sashite::Ggn.load_file("
|
48
|
-
# engine = piece_data.select("
|
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
|
-
# #
|
53
|
+
# # Board with pawn ready to promote
|
54
54
|
# board_state = {
|
55
|
-
# "
|
56
|
-
# "
|
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
|
-
#
|
59
|
+
# transitions = engine.where(board_state, "CHESS")
|
61
60
|
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
# puts "
|
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
|
69
|
-
# #
|
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.select("CHESS:
|
70
|
+
# engine = piece_data.select("CHESS:K").from("e1").to("g1")
|
72
71
|
#
|
73
|
-
# # Board
|
72
|
+
# # Board state allowing kingside castling
|
74
73
|
# board_state = {
|
75
|
-
# "
|
76
|
-
# "
|
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
|
-
#
|
80
|
+
# transitions = engine.where(board_state, "CHESS")
|
80
81
|
#
|
81
|
-
# if
|
82
|
-
#
|
83
|
-
# puts "
|
84
|
-
#
|
85
|
-
#
|
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.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cyril Kato
|
@@ -24,10 +24,12 @@ 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
|
28
|
-
strategy board games. This library provides parsing,
|
29
|
-
capabilities for GGN documents,
|
30
|
-
|
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. Features flexible
|
31
|
+
validation system for both safety and performance. Supports Chess, Shogi, Xiangqi,
|
32
|
+
and custom variants without hand management or piece drops.
|
31
33
|
email: contact@cyril.email
|
32
34
|
executables: []
|
33
35
|
extensions: []
|
@@ -55,6 +57,9 @@ metadata:
|
|
55
57
|
source_code_uri: https://github.com/sashite/ggn.rb
|
56
58
|
specification_uri: https://sashite.dev/documents/ggn/1.0.0/
|
57
59
|
rubygems_mfa_required: 'true'
|
60
|
+
keywords: board-game, chess, game, gameplay, json, makruk, notation, performance,
|
61
|
+
pseudo-legal-move, rule-agnostic, serialization, shogi, strategy, validation,
|
62
|
+
xiangqi
|
58
63
|
rdoc_options: []
|
59
64
|
require_paths:
|
60
65
|
- lib
|
@@ -71,5 +76,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
71
76
|
requirements: []
|
72
77
|
rubygems_version: 3.6.9
|
73
78
|
specification_version: 4
|
74
|
-
summary: General Gameplay Notation (GGN)
|
79
|
+
summary: General Gameplay Notation (GGN) library for board-to-board game transformations
|
75
80
|
test_files: []
|