Class: Sashite::Epin::Identifier

Inherits:
Object
  • Object
show all
Defined in:
lib/sashite/epin/identifier.rb

Overview

Represents an identifier in EPIN (Extended Piece Identifier Notation) format.

An identifier consists of a PIN component with an optional derivation marker:

  • PIN component: [<state>]<letter> (from PIN specification)

  • Derivation marker: “‘” (foreign style) or none (native style)

The case of the letter determines ownership:

  • Uppercase (A-Z): first player

  • Lowercase (a-z): second player

Style derivation logic:

  • No suffix: piece has the native style of its current side

  • Apostrophe suffix: piece has the foreign style (opposite side’s native style)

All instances are immutable - state manipulation methods return new instances. This extends the Game Protocol’s piece model with Style support through derivation.

Constant Summary collapse

DERIVATION_SUFFIX =

Valid derivation suffixes

"'"
NATIVE_SUFFIX =
""
NATIVE =

Derivation constants

true
FOREIGN =
false
VALID_DERIVATIONS =

Valid derivations

[NATIVE, FOREIGN].freeze
ERROR_INVALID_EPIN =

Error messages

"Invalid EPIN string: %s"
ERROR_INVALID_DERIVATION =
"Derivation must be true (native) or false (foreign), got: %s"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(type, side, state = Pin::Identifier::NORMAL_STATE, native = NATIVE) ⇒ Identifier

Create a new identifier instance

Examples:

Identifier.new(:K, :first, :normal, true)
Identifier.new(:P, :second, :enhanced, false)

Parameters:

  • type (Symbol)

    piece type (:A to :Z)

  • side (Symbol)

    player side (:first or :second)

  • state (Symbol) (defaults to: Pin::Identifier::NORMAL_STATE)

    piece state (:normal, :enhanced, or :diminished)

  • native (Boolean) (defaults to: NATIVE)

    style derivation (true for native, false for foreign)

Raises:

  • (ArgumentError)

    if parameters are invalid



67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/sashite/epin/identifier.rb', line 67

def initialize(type, side, state = Pin::Identifier::NORMAL_STATE, native = NATIVE)
  # Validate using PIN class methods for type, side, and state
  Pin::Identifier.validate_type(type)
  Pin::Identifier.validate_side(side)
  Pin::Identifier.validate_state(state)
  self.class.validate_derivation(native)

  @pin_identifier = Pin::Identifier.new(type, side, state)
  @native = native

  freeze
end

Instance Attribute Details

#nativeBoolean (readonly)

Returns the style derivation (true for native, false for foreign).

Returns:

  • (Boolean)

    the style derivation (true for native, false for foreign)



55
56
57
# File 'lib/sashite/epin/identifier.rb', line 55

def native
  @native
end

Class Method Details

.parse(epin_string) ⇒ Identifier

Parse an EPIN string into an Identifier object

Examples:

Epin::Identifier.parse("k")     # => #<Epin::Identifier type=:K side=:second state=:normal native=true>
Epin::Identifier.parse("+R'")   # => #<Epin::Identifier type=:R side=:first state=:enhanced native=false>
Epin::Identifier.parse("-p")    # => #<Epin::Identifier type=:P side=:second state=:diminished native=true>

Parameters:

  • epin_string (String)

    EPIN notation string

Returns:

Raises:

  • (ArgumentError)

    if the EPIN string is invalid



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/sashite/epin/identifier.rb', line 89

def self.parse(epin_string)
  string_value = String(epin_string)

  # Check for derivation suffix
  if string_value.end_with?(DERIVATION_SUFFIX)
    pin_part = string_value[0...-1] # Remove the apostrophe
    foreign = true
  else
    pin_part = string_value
    foreign = false
  end

  # Validate and parse the PIN part using existing PIN logic
  raise ::ArgumentError, format(ERROR_INVALID_EPIN, string_value) unless Pin::Identifier.valid?(pin_part)

  pin_identifier = Pin::Identifier.parse(pin_part)
  identifier_native = !foreign

  new(pin_identifier.type, pin_identifier.side, pin_identifier.state, identifier_native)
end

.valid?(epin_string) ⇒ Boolean

Check if a string is a valid EPIN notation

Examples:

Sashite::Epin::Identifier.valid?("K")    # => true
Sashite::Epin::Identifier.valid?("+R'")  # => true
Sashite::Epin::Identifier.valid?("-p")   # => true
Sashite::Epin::Identifier.valid?("KK")   # => false
Sashite::Epin::Identifier.valid?("++K")  # => false

Parameters:

  • epin_string (String)

    The string to validate

Returns:

  • (Boolean)

    true if valid EPIN, false otherwise



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/sashite/epin/identifier.rb', line 121

def self.valid?(epin_string)
  return false unless epin_string.is_a?(::String)
  return false if epin_string.empty?

  # Check for derivation suffix
  if epin_string.end_with?(DERIVATION_SUFFIX)
    pin_part = epin_string[0...-1] # Remove the apostrophe
    return false if pin_part.empty? # Can't have just an apostrophe
  else
    pin_part = epin_string
  end

  # Validate the PIN part using existing PIN validation
  Pin::Identifier.valid?(pin_part)
end

.validate_derivation(derivation) ⇒ Object

Validate that the derivation is a valid boolean

Parameters:

  • derivation (Boolean)

    the derivation to validate

Raises:

  • (ArgumentError)

    if invalid



425
426
427
428
429
# File 'lib/sashite/epin/identifier.rb', line 425

def self.validate_derivation(derivation)
  return if VALID_DERIVATIONS.include?(derivation)

  raise ::ArgumentError, format(ERROR_INVALID_DERIVATION, derivation.inspect)
end

Instance Method Details

#==(other) ⇒ Boolean Also known as: eql?

Custom equality comparison

Parameters:

  • other (Object)

    object to compare with

Returns:

  • (Boolean)

    true if identifiers are equal



405
406
407
408
409
# File 'lib/sashite/epin/identifier.rb', line 405

def ==(other)
  return false unless other.is_a?(self.class)

  @pin_identifier == other.instance_variable_get(:@pin_identifier) && native == other.native
end

#deriveIdentifier

Create a new identifier with foreign style (derivation marker)

Examples:

identifier.derive  # (:K, :first, :normal, true) => (:K, :first, :normal, false)

Returns:

  • (Identifier)

    new identifier instance with foreign style



238
239
240
241
242
# File 'lib/sashite/epin/identifier.rb', line 238

def derive
  return self if derived?

  self.class.new(type, side, state, FOREIGN)
end

#derived?Boolean Also known as: foreign?

Check if the identifier has foreign style (derivation marker)

Returns:

  • (Boolean)

    true if foreign style



352
353
354
# File 'lib/sashite/epin/identifier.rb', line 352

def derived?
  native == FOREIGN
end

#diminishIdentifier

Create a new identifier with diminished state

Examples:

identifier.diminish  # (:K, :first, :normal, true) => (:K, :first, :diminished, true)

Returns:

  • (Identifier)

    new identifier instance with diminished state



196
197
198
199
200
# File 'lib/sashite/epin/identifier.rb', line 196

def diminish
  return self if diminished?

  self.class.new(type, side, Pin::Identifier::DIMINISHED_STATE, native)
end

#diminished?Boolean

Check if the identifier has diminished state

Returns:

  • (Boolean)

    true if diminished



317
318
319
# File 'lib/sashite/epin/identifier.rb', line 317

def diminished?
  @pin_identifier.diminished?
end

#enhanceIdentifier

Create a new identifier with enhanced state

Examples:

identifier.enhance  # (:K, :first, :normal, true) => (:K, :first, :enhanced, true)

Returns:

  • (Identifier)

    new identifier instance with enhanced state



174
175
176
177
178
# File 'lib/sashite/epin/identifier.rb', line 174

def enhance
  return self if enhanced?

  self.class.new(type, side, Pin::Identifier::ENHANCED_STATE, native)
end

#enhanced?Boolean

Check if the identifier has enhanced state

Returns:

  • (Boolean)

    true if enhanced



310
311
312
# File 'lib/sashite/epin/identifier.rb', line 310

def enhanced?
  @pin_identifier.enhanced?
end

#first_player?Boolean

Check if the identifier belongs to the first player

Returns:

  • (Boolean)

    true if first player



331
332
333
# File 'lib/sashite/epin/identifier.rb', line 331

def first_player?
  @pin_identifier.first_player?
end

#flipIdentifier

Create a new identifier with opposite side

Examples:

identifier.flip  # (:K, :first, :normal, true) => (:K, :second, :normal, true)

Returns:

  • (Identifier)

    new identifier instance with opposite side



229
230
231
# File 'lib/sashite/epin/identifier.rb', line 229

def flip
  self.class.new(type, opposite_side, state, native)
end

#hashInteger

Custom hash implementation for use in collections

Returns:

  • (Integer)

    hash value



417
418
419
# File 'lib/sashite/epin/identifier.rb', line 417

def hash
  [self.class, @pin_identifier, native].hash
end

#letterString

Get the letter representation (inherited from PIN logic)

Returns:

  • (String)

    letter representation combining type and side



151
152
153
# File 'lib/sashite/epin/identifier.rb', line 151

def letter
  @pin_identifier.letter
end

#native?Boolean

Check if the identifier has native style (no derivation marker)

Returns:

  • (Boolean)

    true if native style



345
346
347
# File 'lib/sashite/epin/identifier.rb', line 345

def native?
  native == NATIVE
end

#normal?Boolean

Check if the identifier has normal state (no modifiers)

Returns:

  • (Boolean)

    true if no modifiers are present



324
325
326
# File 'lib/sashite/epin/identifier.rb', line 324

def normal?
  @pin_identifier.normal?
end

#normalizeIdentifier

Create a new identifier with normal state (no modifiers)

Examples:

identifier.normalize  # (:K, :first, :enhanced, true) => (:K, :first, :normal, true)

Returns:

  • (Identifier)

    new identifier instance with normal state



218
219
220
221
222
# File 'lib/sashite/epin/identifier.rb', line 218

def normalize
  return self if normal?

  self.class.new(type, side, Pin::Identifier::NORMAL_STATE, native)
end

#prefixString

Get the prefix representation (inherited from PIN logic)

Returns:

  • (String)

    prefix representing the state



158
159
160
# File 'lib/sashite/epin/identifier.rb', line 158

def prefix
  @pin_identifier.prefix
end

#same_side?(other) ⇒ Boolean

Check if this identifier belongs to the same side as another

Parameters:

  • other (Identifier)

    identifier to compare with

Returns:

  • (Boolean)

    true if same side



375
376
377
378
379
# File 'lib/sashite/epin/identifier.rb', line 375

def same_side?(other)
  return false unless other.is_a?(self.class)

  @pin_identifier.same_side?(other.instance_variable_get(:@pin_identifier))
end

#same_state?(other) ⇒ Boolean

Check if this identifier has the same state as another

Parameters:

  • other (Identifier)

    identifier to compare with

Returns:

  • (Boolean)

    true if same state



385
386
387
388
389
# File 'lib/sashite/epin/identifier.rb', line 385

def same_state?(other)
  return false unless other.is_a?(self.class)

  @pin_identifier.same_state?(other.instance_variable_get(:@pin_identifier))
end

#same_style?(other) ⇒ Boolean

Check if this identifier has the same style derivation as another

Parameters:

  • other (Identifier)

    identifier to compare with

Returns:

  • (Boolean)

    true if same style derivation



395
396
397
398
399
# File 'lib/sashite/epin/identifier.rb', line 395

def same_style?(other)
  return false unless other.is_a?(self.class)

  native == other.native
end

#same_type?(other) ⇒ Boolean

Check if this identifier is the same type as another (ignoring side, state, and derivation)

Examples:

king1.same_type?(king2)  # (:K, :first, :normal, true) and (:K, :second, :enhanced, false) => true

Parameters:

  • other (Identifier)

    identifier to compare with

Returns:

  • (Boolean)

    true if same type



365
366
367
368
369
# File 'lib/sashite/epin/identifier.rb', line 365

def same_type?(other)
  return false unless other.is_a?(self.class)

  @pin_identifier.same_type?(other.instance_variable_get(:@pin_identifier))
end

#second_player?Boolean

Check if the identifier belongs to the second player

Returns:

  • (Boolean)

    true if second player



338
339
340
# File 'lib/sashite/epin/identifier.rb', line 338

def second_player?
  @pin_identifier.second_player?
end

#sideSymbol

Returns the player side (:first or :second).

Returns:

  • (Symbol)

    the player side (:first or :second)



45
46
47
# File 'lib/sashite/epin/identifier.rb', line 45

def side
  @pin_identifier.side
end

#stateSymbol

Returns the piece state (:normal, :enhanced, or :diminished).

Returns:

  • (Symbol)

    the piece state (:normal, :enhanced, or :diminished)



50
51
52
# File 'lib/sashite/epin/identifier.rb', line 50

def state
  @pin_identifier.state
end

#suffixString

Get the suffix representation

Returns:

  • (String)

    suffix representing the derivation



165
166
167
# File 'lib/sashite/epin/identifier.rb', line 165

def suffix
  native? ? NATIVE_SUFFIX : DERIVATION_SUFFIX
end

#to_sString

Convert the identifier to its EPIN string representation

Examples:

identifier.to_s  # => "+R'"
identifier.to_s  # => "-p"
identifier.to_s  # => "K"

Returns:

  • (String)

    EPIN notation string



144
145
146
# File 'lib/sashite/epin/identifier.rb', line 144

def to_s
  "#{prefix}#{letter}#{suffix}"
end

#typeSymbol

Returns the piece type (:A to :Z).

Returns:

  • (Symbol)

    the piece type (:A to :Z)



40
41
42
# File 'lib/sashite/epin/identifier.rb', line 40

def type
  @pin_identifier.type
end

#underiveIdentifier

Create a new identifier with native style (no derivation marker)

Examples:

identifier.underive  # (:K, :first, :normal, false) => (:K, :first, :normal, true)

Returns:

  • (Identifier)

    new identifier instance with native style



249
250
251
252
253
# File 'lib/sashite/epin/identifier.rb', line 249

def underive
  return self if native?

  self.class.new(type, side, state, NATIVE)
end

#undiminishIdentifier

Create a new identifier without diminished state

Examples:

identifier.undiminish  # (:K, :first, :diminished, true) => (:K, :first, :normal, true)

Returns:

  • (Identifier)

    new identifier instance without diminished state



207
208
209
210
211
# File 'lib/sashite/epin/identifier.rb', line 207

def undiminish
  return self unless diminished?

  self.class.new(type, side, Pin::Identifier::NORMAL_STATE, native)
end

#unenhanceIdentifier

Create a new identifier without enhanced state

Examples:

identifier.unenhance  # (:K, :first, :enhanced, true) => (:K, :first, :normal, true)

Returns:

  • (Identifier)

    new identifier instance without enhanced state



185
186
187
188
189
# File 'lib/sashite/epin/identifier.rb', line 185

def unenhance
  return self unless enhanced?

  self.class.new(type, side, Pin::Identifier::NORMAL_STATE, native)
end

#with_derivation(new_native) ⇒ Identifier

Create a new identifier with a different derivation (keeping same type, side, and state)

Examples:

identifier.with_derivation(false)  # (:K, :first, :normal, true) => (:K, :first, :normal, false)

Parameters:

  • new_native (Boolean)

    true for native, false for foreign

Returns:

  • (Identifier)

    new identifier instance with different derivation



300
301
302
303
304
305
# File 'lib/sashite/epin/identifier.rb', line 300

def with_derivation(new_native)
  self.class.validate_derivation(new_native)
  return self if native == new_native

  self.class.new(type, side, state, new_native)
end

#with_side(new_side) ⇒ Identifier

Create a new identifier with a different side (keeping same type, state, and derivation)

Examples:

identifier.with_side(:second)  # (:K, :first, :normal, true) => (:K, :second, :normal, true)

Parameters:

  • new_side (Symbol)

    :first or :second

Returns:

  • (Identifier)

    new identifier instance with different side



274
275
276
277
278
279
# File 'lib/sashite/epin/identifier.rb', line 274

def with_side(new_side)
  Pin::Identifier.validate_side(new_side)
  return self if side == new_side

  self.class.new(type, new_side, state, native)
end

#with_state(new_state) ⇒ Identifier

Create a new identifier with a different state (keeping same type, side, and derivation)

Examples:

identifier.with_state(:enhanced)  # (:K, :first, :normal, true) => (:K, :first, :enhanced, true)

Parameters:

  • new_state (Symbol)

    :normal, :enhanced, or :diminished

Returns:

  • (Identifier)

    new identifier instance with different state



287
288
289
290
291
292
# File 'lib/sashite/epin/identifier.rb', line 287

def with_state(new_state)
  Pin::Identifier.validate_state(new_state)
  return self if state == new_state

  self.class.new(type, side, new_state, native)
end

#with_type(new_type) ⇒ Identifier

Create a new identifier with a different type (keeping same side, state, and derivation)

Examples:

identifier.with_type(:Q)  # (:K, :first, :normal, true) => (:Q, :first, :normal, true)

Parameters:

  • new_type (Symbol)

    new type (:A to :Z)

Returns:

  • (Identifier)

    new identifier instance with different type



261
262
263
264
265
266
# File 'lib/sashite/epin/identifier.rb', line 261

def with_type(new_type)
  Pin::Identifier.validate_type(new_type)
  return self if type == new_type

  self.class.new(new_type, side, state, native)
end