sashite-epin 2.1.0 → 2.2.1
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 +151 -298
- data/lib/sashite/epin/constants.rb +15 -0
- data/lib/sashite/epin/errors/argument/messages.rb +25 -0
- data/lib/sashite/epin/errors/argument.rb +16 -0
- data/lib/sashite/epin/errors.rb +3 -0
- data/lib/sashite/epin/identifier.rb +202 -0
- data/lib/sashite/epin/parser.rb +103 -0
- data/lib/sashite/epin.rb +45 -266
- data/lib/sashite-epin.rb +0 -11
- metadata +10 -5
- data/LICENSE.md +0 -21
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "sashite/pin"
|
|
4
|
+
|
|
5
|
+
require_relative "constants"
|
|
6
|
+
require_relative "errors"
|
|
7
|
+
|
|
8
|
+
module Sashite
|
|
9
|
+
module Epin
|
|
10
|
+
# Represents a parsed EPIN (Extended Piece Identifier Notation) identifier.
|
|
11
|
+
#
|
|
12
|
+
# An Identifier combines a PIN component with a derivation status:
|
|
13
|
+
# - PIN: encodes abbr, side, state, and terminal status
|
|
14
|
+
# - Derived: indicates whether the piece uses native or derived style
|
|
15
|
+
#
|
|
16
|
+
# Instances are immutable (frozen after creation).
|
|
17
|
+
#
|
|
18
|
+
# @example Creating identifiers
|
|
19
|
+
# pin = Sashite::Pin.parse("K^")
|
|
20
|
+
# epin = Identifier.new(pin)
|
|
21
|
+
# epin = Identifier.new(pin, derived: true)
|
|
22
|
+
#
|
|
23
|
+
# @example String conversion
|
|
24
|
+
# Identifier.new(pin).to_s # => "K^"
|
|
25
|
+
# Identifier.new(pin, derived: true).to_s # => "K^'"
|
|
26
|
+
#
|
|
27
|
+
# @see https://sashite.dev/specs/epin/1.0.0/
|
|
28
|
+
class Identifier
|
|
29
|
+
# @return [Sashite::Pin::Identifier] PIN component
|
|
30
|
+
attr_reader :pin
|
|
31
|
+
|
|
32
|
+
# Creates a new Identifier instance.
|
|
33
|
+
#
|
|
34
|
+
# @param pin [Sashite::Pin::Identifier] PIN component
|
|
35
|
+
# @param derived [Boolean] Derived status
|
|
36
|
+
# @return [Identifier] A new frozen Identifier instance
|
|
37
|
+
# @raise [Errors::Argument] If any attribute is invalid
|
|
38
|
+
#
|
|
39
|
+
# @example
|
|
40
|
+
# pin = Sashite::Pin.parse("K^")
|
|
41
|
+
# Identifier.new(pin)
|
|
42
|
+
# Identifier.new(pin, derived: true)
|
|
43
|
+
def initialize(pin, derived: false)
|
|
44
|
+
validate_pin!(pin)
|
|
45
|
+
validate_derived!(derived)
|
|
46
|
+
|
|
47
|
+
@pin = pin
|
|
48
|
+
@derived = derived
|
|
49
|
+
|
|
50
|
+
freeze
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Returns the derived status.
|
|
54
|
+
#
|
|
55
|
+
# @return [Boolean] true if derived style, false otherwise
|
|
56
|
+
#
|
|
57
|
+
# @example
|
|
58
|
+
# Identifier.new(pin).derived? # => false
|
|
59
|
+
# Identifier.new(pin, derived: true).derived? # => true
|
|
60
|
+
def derived?
|
|
61
|
+
@derived
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Returns the native status.
|
|
65
|
+
#
|
|
66
|
+
# @return [Boolean] true if native style, false otherwise
|
|
67
|
+
#
|
|
68
|
+
# @example
|
|
69
|
+
# Identifier.new(pin).native? # => true
|
|
70
|
+
# Identifier.new(pin, derived: true).native? # => false
|
|
71
|
+
def native?
|
|
72
|
+
!@derived
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# ========================================================================
|
|
76
|
+
# String Conversion
|
|
77
|
+
# ========================================================================
|
|
78
|
+
|
|
79
|
+
# Returns the EPIN string representation.
|
|
80
|
+
#
|
|
81
|
+
# @return [String] The EPIN string
|
|
82
|
+
#
|
|
83
|
+
# @example
|
|
84
|
+
# Identifier.new(pin).to_s # => "K^"
|
|
85
|
+
# Identifier.new(pin, derived: true).to_s # => "K^'"
|
|
86
|
+
def to_s
|
|
87
|
+
derived? ? "#{pin}#{Constants::DERIVATION_SUFFIX}" : pin.to_s
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# ========================================================================
|
|
91
|
+
# Transformations
|
|
92
|
+
# ========================================================================
|
|
93
|
+
|
|
94
|
+
# Returns a new Identifier with a different PIN component.
|
|
95
|
+
#
|
|
96
|
+
# @param new_pin [Sashite::Pin::Identifier] The new PIN component
|
|
97
|
+
# @return [Identifier] A new Identifier with the specified PIN
|
|
98
|
+
# @raise [Errors::Argument] If the PIN is invalid
|
|
99
|
+
#
|
|
100
|
+
# @example
|
|
101
|
+
# epin = Identifier.new(pin, derived: true)
|
|
102
|
+
# new_pin = Sashite::Pin.parse("+Q^")
|
|
103
|
+
# epin.with_pin(new_pin).to_s # => "+Q^'"
|
|
104
|
+
def with_pin(new_pin)
|
|
105
|
+
return self if pin == new_pin
|
|
106
|
+
|
|
107
|
+
self.class.new(new_pin, derived: @derived)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Returns a new Identifier marked as derived.
|
|
111
|
+
#
|
|
112
|
+
# @return [Identifier] A new Identifier with derived: true
|
|
113
|
+
#
|
|
114
|
+
# @example
|
|
115
|
+
# epin = Identifier.new(pin)
|
|
116
|
+
# epin.derive.to_s # => "K^'"
|
|
117
|
+
def derive
|
|
118
|
+
return self if derived?
|
|
119
|
+
|
|
120
|
+
self.class.new(pin, derived: true)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Returns a new Identifier marked as native.
|
|
124
|
+
#
|
|
125
|
+
# @return [Identifier] A new Identifier with derived: false
|
|
126
|
+
#
|
|
127
|
+
# @example
|
|
128
|
+
# epin = Identifier.new(pin, derived: true)
|
|
129
|
+
# epin.native.to_s # => "K^"
|
|
130
|
+
def native
|
|
131
|
+
return self if native?
|
|
132
|
+
|
|
133
|
+
self.class.new(pin, derived: false)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# ========================================================================
|
|
137
|
+
# Comparison Queries
|
|
138
|
+
# ========================================================================
|
|
139
|
+
|
|
140
|
+
# Checks if two Identifiers have the same derived status.
|
|
141
|
+
#
|
|
142
|
+
# @param other [Identifier] The other Identifier to compare
|
|
143
|
+
# @return [Boolean] true if same derived status
|
|
144
|
+
#
|
|
145
|
+
# @example
|
|
146
|
+
# epin1 = Identifier.new(pin1, derived: true)
|
|
147
|
+
# epin2 = Identifier.new(pin2, derived: true)
|
|
148
|
+
# epin1.same_derived?(epin2) # => true
|
|
149
|
+
def same_derived?(other)
|
|
150
|
+
@derived == other.derived?
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# ========================================================================
|
|
154
|
+
# Equality
|
|
155
|
+
# ========================================================================
|
|
156
|
+
|
|
157
|
+
# Checks equality with another Identifier.
|
|
158
|
+
#
|
|
159
|
+
# @param other [Object] The object to compare
|
|
160
|
+
# @return [Boolean] true if equal
|
|
161
|
+
def ==(other)
|
|
162
|
+
return false unless self.class === other
|
|
163
|
+
|
|
164
|
+
pin == other.pin && @derived == other.derived?
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
alias eql? ==
|
|
168
|
+
|
|
169
|
+
# Returns a hash code for the Identifier.
|
|
170
|
+
#
|
|
171
|
+
# @return [Integer] Hash code
|
|
172
|
+
def hash
|
|
173
|
+
[pin, @derived].hash
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Returns an inspect string for the Identifier.
|
|
177
|
+
#
|
|
178
|
+
# @return [String] Inspect representation
|
|
179
|
+
def inspect
|
|
180
|
+
"#<#{self.class} #{self}>"
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
private
|
|
184
|
+
|
|
185
|
+
# ========================================================================
|
|
186
|
+
# Private Validation
|
|
187
|
+
# ========================================================================
|
|
188
|
+
|
|
189
|
+
def validate_pin!(pin)
|
|
190
|
+
return if ::Sashite::Pin::Identifier === pin
|
|
191
|
+
|
|
192
|
+
raise Errors::Argument, Errors::Argument::Messages::INVALID_PIN
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def validate_derived!(derived)
|
|
196
|
+
return if ::TrueClass === derived || ::FalseClass === derived
|
|
197
|
+
|
|
198
|
+
raise Errors::Argument, Errors::Argument::Messages::INVALID_DERIVED
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "sashite/pin"
|
|
4
|
+
|
|
5
|
+
require_relative "constants"
|
|
6
|
+
require_relative "errors"
|
|
7
|
+
|
|
8
|
+
module Sashite
|
|
9
|
+
module Epin
|
|
10
|
+
# Parser for EPIN (Extended Piece Identifier Notation) strings.
|
|
11
|
+
#
|
|
12
|
+
# This parser extracts the derivation marker and delegates PIN parsing
|
|
13
|
+
# to the sashite-pin library.
|
|
14
|
+
#
|
|
15
|
+
# @example
|
|
16
|
+
# Parser.parse("K") # => { pin: { abbr: :K, side: :first, ... }, derived: false }
|
|
17
|
+
# Parser.parse("K^'") # => { pin: { abbr: :K, side: :first, ..., terminal: true }, derived: true }
|
|
18
|
+
#
|
|
19
|
+
# @see https://sashite.dev/specs/epin/1.0.0/
|
|
20
|
+
module Parser
|
|
21
|
+
# Parses an EPIN string into its components.
|
|
22
|
+
#
|
|
23
|
+
# @param input [String] The EPIN string to parse
|
|
24
|
+
# @return [Hash] A hash with :pin (PIN components hash) and :derived keys
|
|
25
|
+
# @raise [Errors::Argument] If the input is not a valid EPIN string
|
|
26
|
+
def self.parse(input)
|
|
27
|
+
validate_string!(input)
|
|
28
|
+
|
|
29
|
+
derived = has_derivation_marker?(input)
|
|
30
|
+
|
|
31
|
+
if derived
|
|
32
|
+
validate_derivation_marker!(input)
|
|
33
|
+
pin_string = input.chop
|
|
34
|
+
else
|
|
35
|
+
pin_string = input
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
pin_components = parse_pin_component(pin_string)
|
|
39
|
+
|
|
40
|
+
{ pin: pin_components, derived: derived }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Validates an EPIN string without raising an exception.
|
|
44
|
+
#
|
|
45
|
+
# @param input [String] The EPIN string to validate
|
|
46
|
+
# @return [Boolean] true if valid, false otherwise
|
|
47
|
+
def self.valid?(input)
|
|
48
|
+
return false unless ::String === input
|
|
49
|
+
|
|
50
|
+
parse(input)
|
|
51
|
+
true
|
|
52
|
+
rescue Errors::Argument
|
|
53
|
+
false
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class << self
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
# Validates that input is a String.
|
|
60
|
+
#
|
|
61
|
+
# @param input [Object] The input to validate
|
|
62
|
+
# @raise [Errors::Argument] If input is not a String
|
|
63
|
+
def validate_string!(input)
|
|
64
|
+
return if ::String === input
|
|
65
|
+
|
|
66
|
+
raise Errors::Argument, "invalid PIN component: must contain exactly one letter"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Checks if the input contains a derivation marker.
|
|
70
|
+
#
|
|
71
|
+
# @param input [String] The input to check
|
|
72
|
+
# @return [Boolean] true if contains derivation marker
|
|
73
|
+
def has_derivation_marker?(input)
|
|
74
|
+
input.include?(Constants::DERIVATION_SUFFIX)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Validates derivation marker position and uniqueness.
|
|
78
|
+
#
|
|
79
|
+
# @param input [String] The input to validate
|
|
80
|
+
# @raise [Errors::Argument] If derivation marker is invalid
|
|
81
|
+
def validate_derivation_marker!(input)
|
|
82
|
+
count = input.count(Constants::DERIVATION_SUFFIX)
|
|
83
|
+
last_char = input[-1]
|
|
84
|
+
|
|
85
|
+
return if count == 1 && last_char == Constants::DERIVATION_SUFFIX
|
|
86
|
+
|
|
87
|
+
raise Errors::Argument, Errors::Argument::Messages::INVALID_DERIVATION_MARKER
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Parses the PIN component using sashite-pin.
|
|
91
|
+
#
|
|
92
|
+
# @param pin_string [String] The PIN string to parse
|
|
93
|
+
# @return [Hash] PIN components hash
|
|
94
|
+
# @raise [Errors::Argument] If PIN parsing fails
|
|
95
|
+
def parse_pin_component(pin_string)
|
|
96
|
+
::Sashite::Pin::Parser.parse(pin_string)
|
|
97
|
+
rescue ::Sashite::Pin::Errors::Argument => e
|
|
98
|
+
raise Errors::Argument, "invalid PIN component: #{e.message}"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
data/lib/sashite/epin.rb
CHANGED
|
@@ -2,300 +2,79 @@
|
|
|
2
2
|
|
|
3
3
|
require "sashite/pin"
|
|
4
4
|
|
|
5
|
+
require_relative "epin/constants"
|
|
6
|
+
require_relative "epin/errors"
|
|
7
|
+
require_relative "epin/identifier"
|
|
8
|
+
require_relative "epin/parser"
|
|
9
|
+
|
|
5
10
|
module Sashite
|
|
6
11
|
# EPIN (Extended Piece Identifier Notation) implementation for Ruby.
|
|
7
12
|
#
|
|
8
|
-
# EPIN extends PIN
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# **EPIN is simply: PIN + optional style derivation marker (`'`)**
|
|
13
|
+
# EPIN extends PIN with an optional derivation marker (') that flags
|
|
14
|
+
# whether a piece uses a native or derived style.
|
|
12
15
|
#
|
|
13
16
|
# == Format
|
|
14
17
|
#
|
|
15
|
-
# <pin
|
|
16
|
-
#
|
|
17
|
-
# Where +<pin-token>+ is a valid PIN token and +<derivation-marker>+ is
|
|
18
|
-
# an optional trailing apostrophe (<tt>'</tt>).
|
|
18
|
+
# <pin>[']
|
|
19
19
|
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
# EPIN exposes all five attributes from the Sashité Game Protocol:
|
|
23
|
-
#
|
|
24
|
-
# - *Piece Name* â†' +epin.pin.type+
|
|
25
|
-
# - *Piece Side* â†' +epin.pin.side+
|
|
26
|
-
# - *Piece State* â†' +epin.pin.state+
|
|
27
|
-
# - *Terminal Status* â†' +epin.pin.terminal+
|
|
28
|
-
# - *Piece Style* â†' +epin.derived+ (native vs derived)
|
|
20
|
+
# - *PIN*: Any valid PIN token (abbr, side, state, terminal)
|
|
21
|
+
# - *Derivation marker*: <tt>'</tt> (derived) or absent (native)
|
|
29
22
|
#
|
|
30
23
|
# == Examples
|
|
31
24
|
#
|
|
32
25
|
# epin = Sashite::Epin.parse("K^'")
|
|
33
|
-
# epin.pin.
|
|
34
|
-
# epin.pin.
|
|
35
|
-
# epin.
|
|
26
|
+
# epin.pin.abbr # => :K
|
|
27
|
+
# epin.pin.side # => :first
|
|
28
|
+
# epin.pin.terminal? # => true
|
|
29
|
+
# epin.derived? # => true
|
|
36
30
|
#
|
|
37
|
-
#
|
|
38
|
-
# epin
|
|
39
|
-
# epin.to_s # => "K^'"
|
|
31
|
+
# epin = Sashite::Epin.parse("+R")
|
|
32
|
+
# epin.to_s # => "+R"
|
|
40
33
|
#
|
|
41
|
-
# Sashite::Epin.valid?("K^'")
|
|
42
|
-
# Sashite::Epin.valid?("
|
|
34
|
+
# Sashite::Epin.valid?("K^'") # => true
|
|
35
|
+
# Sashite::Epin.valid?("invalid") # => false
|
|
43
36
|
#
|
|
44
|
-
#
|
|
45
|
-
|
|
46
|
-
#
|
|
47
|
-
EPIN_PATTERN = /\A(?<pin>[-+]?[A-Za-z]\^?)(?<derived>')?\z/
|
|
48
|
-
|
|
49
|
-
# @return [Sashite::Pin] The underlying PIN component
|
|
50
|
-
attr_reader :pin
|
|
51
|
-
|
|
52
|
-
# @return [Boolean] Derivation status (true = derived, false = native)
|
|
53
|
-
attr_reader :derived
|
|
54
|
-
|
|
55
|
-
# ========================================================================
|
|
56
|
-
# Creation and Parsing
|
|
57
|
-
# ========================================================================
|
|
58
|
-
|
|
59
|
-
# Creates a new EPIN instance from a PIN component.
|
|
37
|
+
# @see https://sashite.dev/specs/epin/1.0.0/
|
|
38
|
+
module Epin
|
|
39
|
+
# Parses an EPIN string into an Identifier.
|
|
60
40
|
#
|
|
61
|
-
# @param
|
|
62
|
-
# @
|
|
63
|
-
# @
|
|
64
|
-
#
|
|
65
|
-
# @example
|
|
66
|
-
# pin = Sashite::Pin.parse("K^")
|
|
67
|
-
# Sashite::Epin.new(pin)
|
|
68
|
-
# # => #<Sashite::Epin K^>
|
|
69
|
-
#
|
|
70
|
-
# Sashite::Epin.new(pin, derived: true)
|
|
71
|
-
# # => #<Sashite::Epin K^'>
|
|
72
|
-
def initialize(pin, derived: false)
|
|
73
|
-
raise ArgumentError, "Expected a Sashite::Pin instance, got: #{pin.inspect}" unless pin.is_a?(Pin)
|
|
74
|
-
|
|
75
|
-
@pin = pin
|
|
76
|
-
@derived = !!derived
|
|
77
|
-
|
|
78
|
-
freeze
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
# Parses an EPIN string into an Epin instance.
|
|
82
|
-
#
|
|
83
|
-
# @param epin_string [String] The EPIN string to parse
|
|
84
|
-
# @return [Epin] A new Epin instance
|
|
85
|
-
# @raise [ArgumentError] If the string is not a valid EPIN
|
|
41
|
+
# @param string [String] The EPIN string to parse
|
|
42
|
+
# @return [Identifier] A new Identifier instance
|
|
43
|
+
# @raise [Errors::Argument] If the string is not a valid EPIN
|
|
86
44
|
#
|
|
87
45
|
# @example
|
|
88
46
|
# Sashite::Epin.parse("K")
|
|
89
|
-
# # => #<Sashite::Epin K>
|
|
90
|
-
#
|
|
91
|
-
# Sashite::Epin.parse("K'")
|
|
92
|
-
# # => #<Sashite::Epin K'>
|
|
47
|
+
# # => #<Sashite::Epin::Identifier K>
|
|
93
48
|
#
|
|
94
|
-
# Sashite::Epin.parse("
|
|
95
|
-
# # => #<Sashite::Epin
|
|
49
|
+
# Sashite::Epin.parse("K^'")
|
|
50
|
+
# # => #<Sashite::Epin::Identifier K^'>
|
|
96
51
|
#
|
|
97
52
|
# Sashite::Epin.parse("invalid")
|
|
98
|
-
# # =>
|
|
99
|
-
def self.parse(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
derived
|
|
110
|
-
|
|
111
|
-
new(pin, derived: derived)
|
|
53
|
+
# # => raises Errors::Argument
|
|
54
|
+
def self.parse(string)
|
|
55
|
+
components = Parser.parse(string)
|
|
56
|
+
|
|
57
|
+
pin = ::Sashite::Pin::Identifier.new(
|
|
58
|
+
components[:pin][:abbr],
|
|
59
|
+
components[:pin][:side],
|
|
60
|
+
components[:pin][:state],
|
|
61
|
+
terminal: components[:pin][:terminal]
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
Identifier.new(pin, derived: components[:derived])
|
|
112
65
|
end
|
|
113
66
|
|
|
114
67
|
# Checks if a string is a valid EPIN notation.
|
|
115
68
|
#
|
|
116
|
-
# @param
|
|
69
|
+
# @param string [String] The string to validate
|
|
117
70
|
# @return [Boolean] true if valid, false otherwise
|
|
118
71
|
#
|
|
119
72
|
# @example
|
|
120
|
-
# Sashite::Epin.valid?("K")
|
|
121
|
-
# Sashite::Epin.valid?("K'")
|
|
122
|
-
# Sashite::Epin.valid?("
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
# Sashite::Epin.valid?("invalid") # => false
|
|
126
|
-
def self.valid?(epin_string)
|
|
127
|
-
return false unless epin_string.is_a?(String)
|
|
128
|
-
|
|
129
|
-
EPIN_PATTERN.match?(epin_string)
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
# ========================================================================
|
|
133
|
-
# Conversion
|
|
134
|
-
# ========================================================================
|
|
135
|
-
|
|
136
|
-
# Converts the Epin to its string representation.
|
|
137
|
-
#
|
|
138
|
-
# @return [String] The EPIN string
|
|
139
|
-
#
|
|
140
|
-
# @example
|
|
141
|
-
# pin = Sashite::Pin.parse("K^")
|
|
142
|
-
# Sashite::Epin.new(pin).to_s
|
|
143
|
-
# # => "K^"
|
|
144
|
-
#
|
|
145
|
-
# Sashite::Epin.new(pin, derived: true).to_s
|
|
146
|
-
# # => "K^'"
|
|
147
|
-
def to_s
|
|
148
|
-
"#{pin}#{derivation_suffix}"
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
# ========================================================================
|
|
152
|
-
# Transformations
|
|
153
|
-
# ========================================================================
|
|
154
|
-
|
|
155
|
-
# Returns a new Epin with a different PIN component.
|
|
156
|
-
#
|
|
157
|
-
# @param new_pin [Sashite::Pin] The new PIN component
|
|
158
|
-
# @return [Epin] A new Epin with the specified PIN
|
|
159
|
-
#
|
|
160
|
-
# @example
|
|
161
|
-
# epin = Sashite::Epin.parse("K^'")
|
|
162
|
-
# new_pin = epin.pin.with_type(:Q)
|
|
163
|
-
# epin.with_pin(new_pin).to_s
|
|
164
|
-
# # => "Q^'"
|
|
165
|
-
def with_pin(new_pin)
|
|
166
|
-
return self if pin == new_pin
|
|
167
|
-
|
|
168
|
-
self.class.new(new_pin, derived: derived)
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
# Returns a new Epin with a different derivation status.
|
|
172
|
-
#
|
|
173
|
-
# @param new_derived [Boolean] The new derivation status
|
|
174
|
-
# @return [Epin] A new Epin with the specified derivation status
|
|
175
|
-
#
|
|
176
|
-
# @example
|
|
177
|
-
# epin = Sashite::Epin.parse("K^")
|
|
178
|
-
# epin.with_derived(true).to_s
|
|
179
|
-
# # => "K^'"
|
|
180
|
-
#
|
|
181
|
-
# epin = Sashite::Epin.parse("K^'")
|
|
182
|
-
# epin.with_derived(false).to_s
|
|
183
|
-
# # => "K^"
|
|
184
|
-
def with_derived(new_derived)
|
|
185
|
-
return self if derived == !!new_derived
|
|
186
|
-
|
|
187
|
-
self.class.new(pin, derived: !!new_derived)
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
# Returns a new Epin marked as derived.
|
|
191
|
-
#
|
|
192
|
-
# @return [Epin] A new Epin with derived: true
|
|
193
|
-
#
|
|
194
|
-
# @example
|
|
195
|
-
# epin = Sashite::Epin.parse("K^")
|
|
196
|
-
# epin.mark_derived.derived
|
|
197
|
-
# # => true
|
|
198
|
-
def mark_derived
|
|
199
|
-
return self if derived
|
|
200
|
-
|
|
201
|
-
self.class.new(pin, derived: true)
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
# Returns a new Epin marked as native (not derived).
|
|
205
|
-
#
|
|
206
|
-
# @return [Epin] A new Epin with derived: false
|
|
207
|
-
#
|
|
208
|
-
# @example
|
|
209
|
-
# epin = Sashite::Epin.parse("K^'")
|
|
210
|
-
# epin.unmark_derived.derived
|
|
211
|
-
# # => false
|
|
212
|
-
def unmark_derived
|
|
213
|
-
return self unless derived
|
|
214
|
-
|
|
215
|
-
self.class.new(pin, derived: false)
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
# ========================================================================
|
|
219
|
-
# Queries
|
|
220
|
-
# ========================================================================
|
|
221
|
-
|
|
222
|
-
# Checks if the Epin is derived (uses opponent's style).
|
|
223
|
-
#
|
|
224
|
-
# @return [Boolean] true if derived
|
|
225
|
-
#
|
|
226
|
-
# @example
|
|
227
|
-
# Sashite::Epin.parse("K^'").derived? # => true
|
|
228
|
-
# Sashite::Epin.parse("K^").derived? # => false
|
|
229
|
-
def derived?
|
|
230
|
-
derived
|
|
231
|
-
end
|
|
232
|
-
|
|
233
|
-
# Checks if the Epin is native (uses own side's style).
|
|
234
|
-
#
|
|
235
|
-
# @return [Boolean] true if native
|
|
236
|
-
#
|
|
237
|
-
# @example
|
|
238
|
-
# Sashite::Epin.parse("K^").native? # => true
|
|
239
|
-
# Sashite::Epin.parse("K^'").native? # => false
|
|
240
|
-
def native?
|
|
241
|
-
!derived
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
# Checks if two Epins have the same derivation status.
|
|
245
|
-
#
|
|
246
|
-
# @param other [Epin] The other Epin to compare
|
|
247
|
-
# @return [Boolean] true if same derivation status
|
|
248
|
-
#
|
|
249
|
-
# @example
|
|
250
|
-
# epin1 = Sashite::Epin.parse("K^'")
|
|
251
|
-
# epin2 = Sashite::Epin.parse("Q'")
|
|
252
|
-
# epin1.same_derived?(epin2)
|
|
253
|
-
# # => true
|
|
254
|
-
#
|
|
255
|
-
# epin3 = Sashite::Epin.parse("K^")
|
|
256
|
-
# epin1.same_derived?(epin3)
|
|
257
|
-
# # => false
|
|
258
|
-
def same_derived?(other)
|
|
259
|
-
derived == other.derived
|
|
260
|
-
end
|
|
261
|
-
|
|
262
|
-
# ========================================================================
|
|
263
|
-
# Comparison
|
|
264
|
-
# ========================================================================
|
|
265
|
-
|
|
266
|
-
# Checks equality with another Epin.
|
|
267
|
-
#
|
|
268
|
-
# @param other [Object] The object to compare
|
|
269
|
-
# @return [Boolean] true if equal
|
|
270
|
-
def ==(other)
|
|
271
|
-
return false unless other.is_a?(self.class)
|
|
272
|
-
|
|
273
|
-
pin == other.pin && derived == other.derived
|
|
274
|
-
end
|
|
275
|
-
|
|
276
|
-
alias eql? ==
|
|
277
|
-
|
|
278
|
-
# Returns a hash code for the Epin.
|
|
279
|
-
#
|
|
280
|
-
# @return [Integer] Hash code
|
|
281
|
-
def hash
|
|
282
|
-
[pin, derived].hash
|
|
283
|
-
end
|
|
284
|
-
|
|
285
|
-
# Returns an inspect string for the Epin.
|
|
286
|
-
#
|
|
287
|
-
# @return [String] Inspect representation
|
|
288
|
-
def inspect
|
|
289
|
-
"#<#{self.class} #{self}>"
|
|
290
|
-
end
|
|
291
|
-
|
|
292
|
-
private
|
|
293
|
-
|
|
294
|
-
# Returns the derivation suffix for string representation.
|
|
295
|
-
#
|
|
296
|
-
# @return [String] "'" if derived, "" otherwise
|
|
297
|
-
def derivation_suffix
|
|
298
|
-
derived ? "'" : ""
|
|
73
|
+
# Sashite::Epin.valid?("K") # => true
|
|
74
|
+
# Sashite::Epin.valid?("K^'") # => true
|
|
75
|
+
# Sashite::Epin.valid?("invalid") # => false
|
|
76
|
+
def self.valid?(string)
|
|
77
|
+
Parser.valid?(string)
|
|
299
78
|
end
|
|
300
79
|
end
|
|
301
80
|
end
|
data/lib/sashite-epin.rb
CHANGED
|
@@ -1,14 +1,3 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "sashite/epin"
|
|
4
|
-
|
|
5
|
-
# Sashité namespace for board game notation libraries
|
|
6
|
-
#
|
|
7
|
-
# Sashité provides a collection of libraries for representing and manipulating
|
|
8
|
-
# board game concepts according to the Game Protocol specifications.
|
|
9
|
-
#
|
|
10
|
-
# @see https://sashite.dev/game-protocol/ Game Protocol Foundation
|
|
11
|
-
# @see https://sashite.dev/specs/ Sashité Specifications
|
|
12
|
-
# @author Sashité
|
|
13
|
-
module Sashite
|
|
14
|
-
end
|