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 +4 -4
- data/LICENSE.md +1 -1
- data/README.md +398 -165
- data/lib/sashite/gan/actor.rb +185 -0
- data/lib/sashite/gan.rb +70 -31
- data/lib/sashite-gan.rb +15 -3
- metadata +27 -101
- data/lib/sashite/gan/abbr.rb +0 -76
- data/lib/sashite/gan/error/string.rb +0 -10
- data/lib/sashite/gan/error/style.rb +0 -12
- data/lib/sashite/gan/error/type.rb +0 -12
- data/lib/sashite/gan/error.rb +0 -12
- data/lib/sashite/gan/parser.rb +0 -31
- data/lib/sashite/gan/piece.rb +0 -150
@@ -0,0 +1,185 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sashite
|
4
|
+
module Gan
|
5
|
+
# Represents a game actor in GAN format
|
6
|
+
#
|
7
|
+
# An actor combines a style identifier (SNN format) with a piece identifier (PNN format)
|
8
|
+
# to create an unambiguous representation of a game piece within its style context.
|
9
|
+
# The casing of both components determines player association and piece ownership:
|
10
|
+
# - Style casing determines which player uses that style tradition (fixed per game)
|
11
|
+
# - Piece casing determines current piece ownership (may change during gameplay)
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# # Traditional same-style game
|
15
|
+
# white_king = Sashite::Gan::Actor.new("CHESS", "K") # First player's chess king
|
16
|
+
# black_king = Sashite::Gan::Actor.new("chess", "k") # Second player's chess king
|
17
|
+
#
|
18
|
+
# # Cross-style game
|
19
|
+
# chess_king = Sashite::Gan::Actor.new("CHESS", "K") # First player uses chess
|
20
|
+
# shogi_king = Sashite::Gan::Actor.new("shogi", "k") # Second player uses shogi
|
21
|
+
#
|
22
|
+
# # Dynamic ownership (piece captured and converted)
|
23
|
+
# captured = Sashite::Gan::Actor.new("CHESS", "k") # Chess piece owned by second player
|
24
|
+
class Actor
|
25
|
+
# @return [Sashite::Snn::Style] The style component
|
26
|
+
attr_reader :style
|
27
|
+
|
28
|
+
# @return [Pnn::Piece] The piece component
|
29
|
+
attr_reader :piece
|
30
|
+
|
31
|
+
# Create a new actor instance
|
32
|
+
#
|
33
|
+
# @param style [String, Sashite::Snn::Style] The style identifier or style object
|
34
|
+
# @param piece [String, Pnn::Piece] The piece identifier or piece object
|
35
|
+
# @raise [ArgumentError] if the parameters are invalid
|
36
|
+
#
|
37
|
+
# @example
|
38
|
+
# # With strings
|
39
|
+
# actor = Sashite::Gan::Actor.new("CHESS", "K")
|
40
|
+
#
|
41
|
+
# # With objects
|
42
|
+
# style = Sashite::Snn::Style.new("CHESS")
|
43
|
+
# piece = Pnn::Piece.new("K")
|
44
|
+
# actor = Sashite::Gan::Actor.new(style, piece)
|
45
|
+
def initialize(style, piece)
|
46
|
+
@style = style.is_a?(Snn::Style) ? style : Snn::Style.new(style.to_s)
|
47
|
+
@piece = piece.is_a?(Pnn::Piece) ? piece : Pnn::Piece.parse(piece.to_s)
|
48
|
+
|
49
|
+
freeze
|
50
|
+
end
|
51
|
+
|
52
|
+
# Parse a GAN string into an actor object
|
53
|
+
#
|
54
|
+
# @param gan_string [String] GAN notation string
|
55
|
+
# @return [Actor] new actor instance
|
56
|
+
# @raise [ArgumentError] if the GAN string is invalid
|
57
|
+
#
|
58
|
+
# @example
|
59
|
+
# actor = Sashite::Gan::Actor.parse("CHESS:K")
|
60
|
+
# # => #<Sashite::Gan::Actor:0x... style="CHESS" piece="K">
|
61
|
+
#
|
62
|
+
# enhanced = Sashite::Gan::Actor.parse("SHOGI:+p'")
|
63
|
+
# # => #<Sashite::Gan::Actor:0x... style="SHOGI" piece="+p'">
|
64
|
+
def self.parse(gan_string)
|
65
|
+
style_string, piece_string = Gan.parse_components(gan_string)
|
66
|
+
new(style_string, piece_string)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Convert the actor to its GAN string representation
|
70
|
+
#
|
71
|
+
# @return [String] GAN notation string
|
72
|
+
#
|
73
|
+
# @example
|
74
|
+
# actor.to_s # => "CHESS:K"
|
75
|
+
def to_s
|
76
|
+
"#{style}:#{piece}"
|
77
|
+
end
|
78
|
+
|
79
|
+
# Get the style name as a string
|
80
|
+
#
|
81
|
+
# @return [String] The style identifier string
|
82
|
+
#
|
83
|
+
# @example
|
84
|
+
# actor.style_name # => "CHESS"
|
85
|
+
def style_name
|
86
|
+
style.to_s
|
87
|
+
end
|
88
|
+
|
89
|
+
# Get the piece name as a string
|
90
|
+
#
|
91
|
+
# @return [String] The piece identifier string
|
92
|
+
#
|
93
|
+
# @example
|
94
|
+
# actor.piece_name # => "K"
|
95
|
+
def piece_name
|
96
|
+
piece.to_s
|
97
|
+
end
|
98
|
+
|
99
|
+
# Create a new actor with an enhanced piece
|
100
|
+
#
|
101
|
+
# @return [Actor] new actor instance with enhanced piece
|
102
|
+
#
|
103
|
+
# @example
|
104
|
+
# actor.enhance_piece # SHOGI:P => SHOGI:+P
|
105
|
+
def enhance_piece
|
106
|
+
self.class.new(style, piece.enhance)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Create a new actor with a diminished piece
|
110
|
+
#
|
111
|
+
# @return [Actor] new actor instance with diminished piece
|
112
|
+
#
|
113
|
+
# @example
|
114
|
+
# actor.diminish_piece # CHESS:R => CHESS:-R
|
115
|
+
def diminish_piece
|
116
|
+
self.class.new(style, piece.diminish)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Create a new actor with an intermediate piece state
|
120
|
+
#
|
121
|
+
# @return [Actor] new actor instance with intermediate piece
|
122
|
+
#
|
123
|
+
# @example
|
124
|
+
# actor.set_piece_intermediate # CHESS:R => CHESS:R'
|
125
|
+
def set_piece_intermediate
|
126
|
+
self.class.new(style, piece.intermediate)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Create a new actor with a piece without modifiers
|
130
|
+
#
|
131
|
+
# @return [Actor] new actor instance with bare piece
|
132
|
+
#
|
133
|
+
# @example
|
134
|
+
# actor.bare_piece # SHOGI:+P' => SHOGI:P
|
135
|
+
def bare_piece
|
136
|
+
self.class.new(style, piece.bare)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Create a new actor with piece ownership flipped
|
140
|
+
#
|
141
|
+
# Changes the piece ownership (case) while keeping the style unchanged.
|
142
|
+
# This method is rule-agnostic and preserves all piece modifiers.
|
143
|
+
# If modifier removal is needed, it should be done explicitly.
|
144
|
+
#
|
145
|
+
# @return [Actor] new actor instance with ownership changed
|
146
|
+
#
|
147
|
+
# @example
|
148
|
+
# actor.change_piece_ownership # SHOGI:P => SHOGI:p
|
149
|
+
# enhanced.change_piece_ownership # SHOGI:+P => SHOGI:+p (modifiers preserved)
|
150
|
+
#
|
151
|
+
# # To remove modifiers explicitly:
|
152
|
+
# actor.bare_piece.change_piece_ownership # SHOGI:+P => SHOGI:p
|
153
|
+
# # or
|
154
|
+
# actor.change_piece_ownership.bare_piece # SHOGI:+P => SHOGI:p
|
155
|
+
def change_piece_ownership
|
156
|
+
self.class.new(style, piece.flip)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Custom equality comparison
|
160
|
+
#
|
161
|
+
# @param other [Object] The object to compare with
|
162
|
+
# @return [Boolean] true if both objects are Actor instances with the same components
|
163
|
+
def ==(other)
|
164
|
+
other.is_a?(Actor) && style == other.style && piece == other.piece
|
165
|
+
end
|
166
|
+
|
167
|
+
# Alias for equality comparison
|
168
|
+
alias eql? ==
|
169
|
+
|
170
|
+
# Hash code for use in hashes and sets
|
171
|
+
#
|
172
|
+
# @return [Integer] The hash code
|
173
|
+
def hash
|
174
|
+
[self.class, style, piece].hash
|
175
|
+
end
|
176
|
+
|
177
|
+
# String representation for debugging
|
178
|
+
#
|
179
|
+
# @return [String] A detailed string representation
|
180
|
+
def inspect
|
181
|
+
"#<#{self.class}:0x#{object_id.to_s(16)} style=#{style_name.inspect} piece=#{piece_name.inspect}>"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
data/lib/sashite/gan.rb
CHANGED
@@ -1,49 +1,88 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require "sashite/snn"
|
4
|
+
require "pnn"
|
5
|
+
require_relative "gan/actor"
|
4
6
|
|
5
7
|
module Sashite
|
6
|
-
#
|
8
|
+
# General Actor Notation (GAN) module
|
7
9
|
#
|
8
|
-
#
|
9
|
-
|
10
|
-
|
10
|
+
# GAN provides a consistent and rule-agnostic format for identifying game actors
|
11
|
+
# in abstract strategy board games. It combines Style Name Notation (SNN) with
|
12
|
+
# Piece Name Notation (PNN) to create unambiguous actor identification that
|
13
|
+
# eliminates collision problems when multiple piece styles are present.
|
14
|
+
#
|
15
|
+
# @see https://sashite.dev/documents/gan/1.0.0/ GAN Specification v1.0.0
|
16
|
+
module Gan
|
17
|
+
# GAN validation regular expression
|
18
|
+
# Matches: <snn>:<pnn> where snn and pnn follow their respective specifications
|
19
|
+
VALIDATION_REGEX = /\A([A-Z][A-Z0-9]*|[a-z][a-z0-9]*):[-+]?[a-zA-Z]'?\z/
|
11
20
|
|
12
|
-
#
|
13
|
-
#
|
14
|
-
# @example Chess (Western chess)'s Rook, White
|
15
|
-
# GAN.parse("C:R")
|
16
|
-
#
|
17
|
-
# @example Chess (Western chess)'s King, Black
|
18
|
-
# GAN.parse("c:-k")
|
21
|
+
# Check if a string is valid GAN notation
|
19
22
|
#
|
20
|
-
# @
|
21
|
-
#
|
23
|
+
# @param gan_string [String] The string to validate
|
24
|
+
# @return [Boolean] true if the string is valid GAN notation, false otherwise
|
22
25
|
#
|
23
|
-
# @example
|
24
|
-
#
|
26
|
+
# @example
|
27
|
+
# Sashite::Gan.valid?("CHESS:K") # => true
|
28
|
+
# Sashite::Gan.valid?("shogi:+p'") # => true
|
29
|
+
# Sashite::Gan.valid?("Chess:K") # => false (mixed case in style)
|
30
|
+
# Sashite::Gan.valid?("CHESS") # => false (missing piece)
|
31
|
+
# Sashite::Gan.valid?("") # => false (empty string)
|
32
|
+
def self.valid?(gan_string)
|
33
|
+
return false unless gan_string.is_a?(String)
|
34
|
+
return false if gan_string.empty?
|
35
|
+
|
36
|
+
# Quick regex check first
|
37
|
+
return false unless VALIDATION_REGEX.match?(gan_string)
|
38
|
+
|
39
|
+
# Split and validate components individually for more precise validation
|
40
|
+
parts = gan_string.split(":", 2)
|
41
|
+
return false unless parts.length == 2
|
42
|
+
|
43
|
+
style_part, piece_part = parts
|
44
|
+
|
45
|
+
# Validate SNN and PNN components using their respective libraries
|
46
|
+
Snn.valid?(style_part) && Pnn.valid?(piece_part)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Convenience method to create an actor object
|
25
50
|
#
|
26
|
-
# @
|
27
|
-
#
|
51
|
+
# @param style [String, Sashite::Snn::Style] The style identifier or style object
|
52
|
+
# @param piece [String, Pnn::Piece] The piece identifier or piece object
|
53
|
+
# @return [Sashite::Gan::Actor] A new actor object
|
54
|
+
# @raise [ArgumentError] if the parameters are invalid
|
28
55
|
#
|
29
|
-
# @example
|
30
|
-
#
|
56
|
+
# @example
|
57
|
+
# actor = Sashite::Gan.actor("CHESS", "K")
|
58
|
+
# # => #<Sashite::Gan::Actor:0x... style="CHESS" piece="K">
|
31
59
|
#
|
32
|
-
#
|
33
|
-
#
|
60
|
+
# # With objects
|
61
|
+
# style = Sashite::Snn::Style.new("CHESS")
|
62
|
+
# piece = Pnn::Piece.new("K")
|
63
|
+
# actor = Sashite::Gan.actor(style, piece)
|
64
|
+
def self.actor(style, piece)
|
65
|
+
Actor.new(style, piece)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Parse a GAN string into component parts
|
34
69
|
#
|
35
|
-
# @
|
36
|
-
#
|
70
|
+
# @param gan_string [String] The GAN string to parse
|
71
|
+
# @return [Array<String>] An array containing [style_string, piece_string]
|
72
|
+
# @raise [ArgumentError] if the string is invalid GAN notation
|
37
73
|
#
|
38
|
-
# @example
|
39
|
-
#
|
74
|
+
# @example
|
75
|
+
# Sashite::Gan.parse_components("CHESS:K")
|
76
|
+
# # => ["CHESS", "K"]
|
40
77
|
#
|
41
|
-
#
|
42
|
-
#
|
78
|
+
# Sashite::Gan.parse_components("shogi:+p'")
|
79
|
+
# # => ["shogi", "+p'"]
|
43
80
|
#
|
44
|
-
# @
|
45
|
-
def self.
|
46
|
-
|
81
|
+
# @api private
|
82
|
+
def self.parse_components(gan_string)
|
83
|
+
raise ArgumentError, "Invalid GAN format: #{gan_string.inspect}" unless valid?(gan_string)
|
84
|
+
|
85
|
+
gan_string.split(":", 2)
|
47
86
|
end
|
48
87
|
end
|
49
88
|
end
|
data/lib/sashite-gan.rb
CHANGED
@@ -1,6 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
#
|
4
|
-
module Sashite
|
3
|
+
# Sashité namespace for board game notation libraries
|
4
|
+
module Sashite
|
5
|
+
# General Actor Notation (GAN) implementation for Ruby
|
6
|
+
#
|
7
|
+
# GAN defines a consistent and rule-agnostic format for identifying game actors
|
8
|
+
# in abstract strategy board games. GAN provides unambiguous identification of
|
9
|
+
# pieces by combining Style Name Notation (SNN) with Piece Name Notation (PNN),
|
10
|
+
# eliminating collision problems when multiple piece styles are present in the
|
11
|
+
# same context.
|
12
|
+
#
|
13
|
+
# @see https://sashite.dev/documents/gan/1.0.0/ GAN Specification v1.0.0
|
14
|
+
# @author Sashité
|
15
|
+
# @since 1.0.0
|
16
|
+
end
|
5
17
|
|
6
|
-
require_relative
|
18
|
+
require_relative "sashite/gan"
|
metadata
CHANGED
@@ -1,114 +1,46 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sashite-gan
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cyril Kato
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
13
|
+
name: pnn
|
15
14
|
requirement: !ruby/object:Gem::Requirement
|
16
15
|
requirements:
|
17
|
-
- - "
|
16
|
+
- - "~>"
|
18
17
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
20
|
-
type: :
|
18
|
+
version: 2.0.0
|
19
|
+
type: :runtime
|
21
20
|
prerelease: false
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
23
22
|
requirements:
|
24
|
-
- - "
|
23
|
+
- - "~>"
|
25
24
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
25
|
+
version: 2.0.0
|
27
26
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
27
|
+
name: sashite-snn
|
29
28
|
requirement: !ruby/object:Gem::Requirement
|
30
29
|
requirements:
|
31
|
-
- - "
|
30
|
+
- - "~>"
|
32
31
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
34
|
-
type: :
|
32
|
+
version: 1.0.0
|
33
|
+
type: :runtime
|
35
34
|
prerelease: false
|
36
35
|
version_requirements: !ruby/object:Gem::Requirement
|
37
36
|
requirements:
|
38
|
-
- - "
|
37
|
+
- - "~>"
|
39
38
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: rubocop-performance
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: rubocop-thread_safety
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: simplecov
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: yard
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - ">="
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '0'
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - ">="
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
111
|
-
description: A Ruby interface for data serialization in GAN format ♟️
|
39
|
+
version: 1.0.0
|
40
|
+
description: A Ruby interface for serialization and deserialization of game actors
|
41
|
+
in GAN format. GAN is a consistent and rule-agnostic format for representing game
|
42
|
+
actors in abstract strategy board games, providing a standardized way to identify
|
43
|
+
pieces with their originating game.
|
112
44
|
email: contact@cyril.email
|
113
45
|
executables: []
|
114
46
|
extensions: []
|
@@ -118,22 +50,17 @@ files:
|
|
118
50
|
- README.md
|
119
51
|
- lib/sashite-gan.rb
|
120
52
|
- lib/sashite/gan.rb
|
121
|
-
- lib/sashite/gan/
|
122
|
-
|
123
|
-
- lib/sashite/gan/error/string.rb
|
124
|
-
- lib/sashite/gan/error/style.rb
|
125
|
-
- lib/sashite/gan/error/type.rb
|
126
|
-
- lib/sashite/gan/parser.rb
|
127
|
-
- lib/sashite/gan/piece.rb
|
128
|
-
homepage: https://developer.sashite.com/specs/general-actor-notation
|
53
|
+
- lib/sashite/gan/actor.rb
|
54
|
+
homepage: https://github.com/sashite/gan.rb
|
129
55
|
licenses:
|
130
56
|
- MIT
|
131
57
|
metadata:
|
132
58
|
bug_tracker_uri: https://github.com/sashite/gan.rb/issues
|
133
|
-
documentation_uri: https://rubydoc.info/
|
59
|
+
documentation_uri: https://rubydoc.info/github/sashite/gan.rb/main
|
60
|
+
homepage_uri: https://github.com/sashite/gan.rb
|
134
61
|
source_code_uri: https://github.com/sashite/gan.rb
|
135
|
-
|
136
|
-
|
62
|
+
specification_uri: https://sashite.dev/documents/gan/1.0.0/
|
63
|
+
rubygems_mfa_required: 'true'
|
137
64
|
rdoc_options: []
|
138
65
|
require_paths:
|
139
66
|
- lib
|
@@ -141,15 +68,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
141
68
|
requirements:
|
142
69
|
- - ">="
|
143
70
|
- !ruby/object:Gem::Version
|
144
|
-
version: 2.
|
71
|
+
version: 3.2.0
|
145
72
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
73
|
requirements:
|
147
74
|
- - ">="
|
148
75
|
- !ruby/object:Gem::Version
|
149
76
|
version: '0'
|
150
77
|
requirements: []
|
151
|
-
rubygems_version: 3.
|
152
|
-
signing_key:
|
78
|
+
rubygems_version: 3.6.7
|
153
79
|
specification_version: 4
|
154
|
-
summary:
|
80
|
+
summary: GAN (General Actor Notation) support for the Ruby language.
|
155
81
|
test_files: []
|
data/lib/sashite/gan/abbr.rb
DELETED
@@ -1,76 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Sashite
|
4
|
-
module GAN
|
5
|
-
# The piece's abbreviation.
|
6
|
-
class Abbr
|
7
|
-
# The piece's type.
|
8
|
-
#
|
9
|
-
# @!attribute [r] type
|
10
|
-
# @return [String] The type of the piece.
|
11
|
-
attr_reader :type
|
12
|
-
|
13
|
-
def initialize(type, is_promoted:, is_king:)
|
14
|
-
@type = TypeString(type)
|
15
|
-
@is_promoted = Boolean(is_promoted)
|
16
|
-
@is_king = Boolean(is_king)
|
17
|
-
|
18
|
-
freeze
|
19
|
-
end
|
20
|
-
|
21
|
-
# @return [Boolean] Is the piece a king?
|
22
|
-
def king?
|
23
|
-
@is_king
|
24
|
-
end
|
25
|
-
|
26
|
-
# @return [Boolean] Is the piece promoted?
|
27
|
-
def promoted?
|
28
|
-
@is_promoted
|
29
|
-
end
|
30
|
-
|
31
|
-
# @return [String] The abbreviation of the piece.
|
32
|
-
def to_s
|
33
|
-
str = type
|
34
|
-
str = "-#{str}" if king?
|
35
|
-
str = "+#{str}" if promoted?
|
36
|
-
str
|
37
|
-
end
|
38
|
-
|
39
|
-
def inspect
|
40
|
-
to_s
|
41
|
-
end
|
42
|
-
|
43
|
-
def ==(other)
|
44
|
-
other.to_s == to_s
|
45
|
-
end
|
46
|
-
|
47
|
-
def eql?(other)
|
48
|
-
self == other
|
49
|
-
end
|
50
|
-
|
51
|
-
private
|
52
|
-
|
53
|
-
# rubocop:disable Naming/MethodName
|
54
|
-
|
55
|
-
# Ensures `arg` is a boolean, and returns it. Otherwise, raises a
|
56
|
-
# `TypeError`.
|
57
|
-
def Boolean(arg)
|
58
|
-
raise ::TypeError, arg.class.inspect unless [false, true].include?(arg)
|
59
|
-
|
60
|
-
arg
|
61
|
-
end
|
62
|
-
|
63
|
-
# Ensures `arg` is a type, and returns it. Otherwise, raises an error.
|
64
|
-
def TypeString(arg)
|
65
|
-
raise ::TypeError, arg.class.inspect unless arg.is_a?(::String)
|
66
|
-
raise Error::Type, arg.inspect unless arg.match?(/\A[a-z]{1,2}\z/i)
|
67
|
-
|
68
|
-
arg
|
69
|
-
end
|
70
|
-
|
71
|
-
# rubocop:enable Naming/MethodName
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
require_relative 'error'
|
data/lib/sashite/gan/error.rb
DELETED
data/lib/sashite/gan/parser.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'error'
|
4
|
-
require_relative 'piece'
|
5
|
-
|
6
|
-
module Sashite
|
7
|
-
module GAN
|
8
|
-
# The notation parser.
|
9
|
-
module Parser
|
10
|
-
def self.call(arg)
|
11
|
-
raise Error::String, "Invalid: #{arg.inspect}" unless valid?(arg)
|
12
|
-
|
13
|
-
style, abbr = arg.split(SEPARATOR_CHAR)
|
14
|
-
|
15
|
-
Piece.new(
|
16
|
-
abbr.delete('-+'),
|
17
|
-
is_king: abbr.include?('-'),
|
18
|
-
is_promoted: abbr.include?('+'),
|
19
|
-
is_topside: style.downcase.eql?(style),
|
20
|
-
style: style
|
21
|
-
)
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.valid?(arg)
|
25
|
-
raise ::TypeError, arg.class.inspect unless arg.is_a?(::String)
|
26
|
-
|
27
|
-
arg.match?(/\A([a-z_]+:\+?-?[a-z]{1,2}|[A-Z_]+:\+?-?[A-Z]{1,2})\z/)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|