sashite-gan 3.0.0 → 5.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.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sashite-gan
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 5.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyril Kato
@@ -10,23 +10,40 @@ cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
- name: pnn
13
+ name: sashite-pin
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: 1.1.0
18
+ version: 2.0.2
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: 1.1.0
26
- description: A Ruby interface for serialization and deserialization of game actors
27
- in GAN format. GAN is a consistent and rule-agnostic format for representing game
28
- actors in abstract strategy board games, providing a standardized way to identify
29
- pieces with their originating game.
25
+ version: 2.0.2
26
+ - !ruby/object:Gem::Dependency
27
+ name: sashite-snn
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: 1.1.1
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 1.1.1
40
+ description: 'A Ruby implementation of GAN (General Actor Notation) v1.0.0 specification
41
+ for identifying game actors in abstract strategy board games. GAN combines Style
42
+ Name Notation (SNN) and Piece Identifier Notation (PIN) with a colon separator to
43
+ provide complete, unambiguous piece identification. Represents all four fundamental
44
+ piece attributes: Type, Side, State, and Style. Enables cross-style gaming, immutable
45
+ transformations, and component extraction with to_pin/to_snn methods. Built on sashite-snn
46
+ and sashite-pin gems.'
30
47
  email: contact@cyril.email
31
48
  executables: []
32
49
  extensions: []
@@ -36,9 +53,7 @@ files:
36
53
  - README.md
37
54
  - lib/sashite-gan.rb
38
55
  - lib/sashite/gan.rb
39
- - lib/sashite/gan/dumper.rb
40
- - lib/sashite/gan/parser.rb
41
- - lib/sashite/gan/validator.rb
56
+ - lib/sashite/gan/actor.rb
42
57
  homepage: https://github.com/sashite/gan.rb
43
58
  licenses:
44
59
  - MIT
@@ -47,7 +62,7 @@ metadata:
47
62
  documentation_uri: https://rubydoc.info/github/sashite/gan.rb/main
48
63
  homepage_uri: https://github.com/sashite/gan.rb
49
64
  source_code_uri: https://github.com/sashite/gan.rb
50
- specification_uri: https://sashite.dev/documents/gan/1.0.0/
65
+ specification_uri: https://sashite.dev/specs/gan/1.0.0/
51
66
  rubygems_mfa_required: 'true'
52
67
  rdoc_options: []
53
68
  require_paths:
@@ -63,7 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
63
78
  - !ruby/object:Gem::Version
64
79
  version: '0'
65
80
  requirements: []
66
- rubygems_version: 3.6.7
81
+ rubygems_version: 3.6.9
67
82
  specification_version: 4
68
- summary: GAN (General Actor Notation) support for the Ruby language.
83
+ summary: GAN (General Actor Notation) implementation for Ruby - board game piece identification
69
84
  test_files: []
@@ -1,94 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "pnn"
4
-
5
- module Sashite
6
- module Gan
7
- # Serializes actor components into GAN (General Actor Notation) strings.
8
- #
9
- # The dumper transforms piece data and game identifiers into properly
10
- # formatted GAN strings, ensuring consistency between game ID casing
11
- # and piece letter casing according to the GAN specification.
12
- #
13
- # According to the specification, game IDs must be either all uppercase
14
- # or all lowercase, and their casing must match the casing of the piece letter.
15
- class Dumper
16
- # Pattern for validating game identifiers - must be all uppercase OR all lowercase
17
- GAME_ID_PATTERN = /\A([A-Z]+|[a-z]+)\z/
18
-
19
- # Error message templates
20
- INVALID_GAME_ID_ERROR = "Game ID must be a non-empty string containing only ASCII letters and must be either all uppercase or all lowercase: %s"
21
- CASING_MISMATCH_ERROR = "Game ID casing (%s) must match piece letter casing (%s)"
22
-
23
- # Serializes actor components into a GAN string
24
- #
25
- # @param game_id [String] The game identifier (e.g., "CHESS", "shogi")
26
- # @param piece_params [Hash] Piece parameters as accepted by Pnn.dump:
27
- # @option piece_params [String] :letter The single ASCII letter identifier (required)
28
- # @option piece_params [String, nil] :prefix Optional prefix modifier for the piece ("+", "-")
29
- # @option piece_params [String, nil] :suffix Optional suffix modifier for the piece ("'")
30
- # @return [String] A properly formatted GAN notation string (e.g., "CHESS:K'")
31
- # @raise [ArgumentError] If game_id is invalid or casing is inconsistent with piece letter
32
- # @example Create a GAN string for a white chess king with castling rights
33
- # Dumper.dump(game_id: "CHESS", letter: "K", suffix: "'")
34
- # # => "CHESS:K'"
35
- # @example Create a GAN string for a promoted shogi pawn
36
- # Dumper.dump(game_id: "SHOGI", letter: "P", prefix: "+")
37
- # # => "SHOGI:+P"
38
- def self.dump(game_id:, **piece_params)
39
- game_id = String(game_id)
40
- validate_game_id!(game_id)
41
-
42
- # Build the piece string using the PNN gem
43
- pnn_string = ::Pnn.dump(**piece_params)
44
-
45
- # Verify casing consistency
46
- validate_casing_consistency!(game_id, pnn_string)
47
-
48
- "#{game_id}:#{pnn_string}"
49
- end
50
-
51
- # @api private
52
- # Validates that the game_id contains only ASCII letters
53
- #
54
- # @param game_id [String] The game identifier to validate
55
- # @return [void]
56
- # @raise [ArgumentError] If game_id contains non-letter characters
57
- def self.validate_game_id!(game_id)
58
- return if game_id.match?(GAME_ID_PATTERN)
59
-
60
- raise ::ArgumentError, format(INVALID_GAME_ID_ERROR, game_id)
61
- end
62
- private_class_method :validate_game_id!
63
-
64
- # @api private
65
- # Validates that the casing of the game_id is consistent with the piece letter
66
- #
67
- # According to GAN specification, if game_id is uppercase, piece letter must be uppercase,
68
- # and if game_id is lowercase, piece letter must be lowercase.
69
- #
70
- # @param game_id [String] The game identifier
71
- # @param pnn_string [String] The PNN string
72
- # @return [void]
73
- # @raise [ArgumentError] If casing is inconsistent
74
- def self.validate_casing_consistency!(game_id, pnn_string)
75
- return if casing_consistent?(game_id, pnn_string)
76
-
77
- raise ::ArgumentError, format(CASING_MISMATCH_ERROR, game_id, pnn_string)
78
- end
79
- private_class_method :validate_casing_consistency!
80
-
81
- # @api private
82
- # Verifies that the casing of the game_id matches the casing of the piece letter
83
- #
84
- # @param game_id [String] The game identifier
85
- # @param pnn_string [String] The PNN string
86
- # @return [Boolean] True if casing is consistent
87
- def self.casing_consistent?(game_id, pnn_string)
88
- # Both must be uppercase or both must be lowercase
89
- (game_id == game_id.upcase) == (pnn_string == pnn_string.upcase)
90
- end
91
- private_class_method :casing_consistent?
92
- end
93
- end
94
- end
@@ -1,58 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "pnn"
4
-
5
- module Sashite
6
- module Gan
7
- # Parses GAN strings into their component parts
8
- class Parser
9
- # GAN regex pattern for parsing
10
- PATTERN = /\A(?<game_id>[a-zA-Z]+):(?<pnn_part>[-+]?[a-zA-Z][']?)\z/
11
-
12
- # Parse a GAN string into its components
13
- #
14
- # @param gan_string [String] The GAN string to parse
15
- # @return [Hash] Hash containing the parsed components
16
- # @raise [ArgumentError] If the GAN string is invalid
17
- def self.parse(gan_string)
18
- gan_string = String(gan_string)
19
-
20
- matches = PATTERN.match(gan_string)
21
- raise ArgumentError, "Invalid GAN string: #{gan_string}" if matches.nil?
22
-
23
- game_id = matches[:game_id]
24
- pnn_part = matches[:pnn_part]
25
-
26
- # Parse the PNN part using the PNN gem
27
- pnn_result = Pnn.parse(pnn_part)
28
-
29
- # Verify casing consistency
30
- unless casing_consistent?(game_id, pnn_result[:letter])
31
- raise ArgumentError, "Game ID casing (#{game_id}) must match piece letter casing (#{pnn_result[:letter]})"
32
- end
33
-
34
- # Merge the game_id with the piece parameters for a flatter structure
35
- { game_id: game_id }.merge(pnn_result)
36
- end
37
-
38
- # Safely parse a GAN string without raising exceptions
39
- #
40
- # @param gan_string [String] The GAN string to parse
41
- # @return [Hash, nil] Hash containing the parsed components or nil if invalid
42
- def self.safe_parse(gan_string)
43
- parse(gan_string)
44
- rescue ArgumentError
45
- nil
46
- end
47
-
48
- # Verifies that the casing of the game_id matches the casing of the piece letter
49
- #
50
- # @param game_id [String] The game identifier
51
- # @param letter [String] The piece letter
52
- # @return [Boolean] True if casing is consistent
53
- def self.casing_consistent?(game_id, letter)
54
- (game_id == game_id.upcase) == (letter == letter.upcase)
55
- end
56
- end
57
- end
58
- end
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "pnn"
4
-
5
- module Sashite
6
- module Gan
7
- # Validates GAN strings
8
- class Validator
9
- # GAN regex pattern for validation
10
- PATTERN = /\A([A-Z]+:[-+]?[A-Z][']?|[a-z]+:[-+]?[a-z][']?)\z/
11
-
12
- # Validates if the given string is a valid GAN string
13
- #
14
- # @param gan_string [String] The GAN string to validate
15
- # @return [Boolean] True if the string is a valid GAN string
16
- def self.valid?(gan_string)
17
- return false unless gan_string.is_a?(String)
18
-
19
- PATTERN.match?(gan_string)
20
- end
21
- end
22
- end
23
- end