Class: Sashite::Pin::Identifier

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

Overview

Represents an identifier in PIN (Piece Identifier Notation) format.

An identifier consists of a single ASCII letter with optional state modifiers:

  • Enhanced state: prefix ‘+’

  • Diminished state: prefix ‘-’

  • Normal state: no modifier

The case of the letter determines ownership:

  • Uppercase (A-Z): first player

  • Lowercase (a-z): second player

All instances are immutable - state manipulation methods return new instances. This follows the Game Protocol’s piece model with Type, Side, and State attributes.

Constant Summary collapse

PIN_PATTERN =

PIN validation pattern matching the specification

/\A(?<prefix>[-+])?(?<letter>[a-zA-Z])\z/
ENHANCED_PREFIX =

Valid state modifiers

"+"
DIMINISHED_PREFIX =
"-"
NORMAL_PREFIX =
""
ENHANCED_STATE =

State constants

:enhanced
DIMINISHED_STATE =
:diminished
NORMAL_STATE =
:normal
FIRST_PLAYER =

Player side constants

:first
SECOND_PLAYER =
:second
VALID_TYPES =

Valid types (A-Z)

(:A..:Z).to_a.freeze
VALID_SIDES =

Valid sides

[FIRST_PLAYER, SECOND_PLAYER].freeze
VALID_STATES =

Valid states

[NORMAL_STATE, ENHANCED_STATE, DIMINISHED_STATE].freeze
ERROR_INVALID_PIN =

Error messages

"Invalid PIN string: %s"
ERROR_INVALID_TYPE =
"Type must be a symbol from :A to :Z, got: %s"
ERROR_INVALID_SIDE =
"Side must be :first or :second, got: %s"
ERROR_INVALID_STATE =
"State must be :normal, :enhanced, or :diminished, got: %s"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(type, side, state = NORMAL_STATE) ⇒ Identifier

Create a new identifier instance

Parameters:

  • type (Symbol)

    piece type (:A to :Z)

  • side (Symbol)

    player side (:first or :second)

  • state (Symbol) (defaults to: NORMAL_STATE)

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

Raises:

  • (ArgumentError)

    if parameters are invalid



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

def initialize(type, side, state = NORMAL_STATE)
  self.class.validate_type(type)
  self.class.validate_side(side)
  self.class.validate_state(state)

  @type = type
  @side = side
  @state = state

  freeze
end

Instance Attribute Details

#sideSymbol (readonly)

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

Returns:

  • (Symbol)

    the player side (:first or :second)



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

def side
  @side
end

#stateSymbol (readonly)

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

Returns:

  • (Symbol)

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



58
59
60
# File 'lib/sashite/pin/identifier.rb', line 58

def state
  @state
end

#typeSymbol (readonly)

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

Returns:

  • (Symbol)

    the piece type (:A to :Z)



52
53
54
# File 'lib/sashite/pin/identifier.rb', line 52

def type
  @type
end

Class Method Details

.parse(pin_string) ⇒ Identifier

Parse a PIN string into an Identifier object

Examples:

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

Parameters:

  • pin_string (String)

    PIN notation string

Returns:

Raises:

  • (ArgumentError)

    if the PIN string is invalid



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

def self.parse(pin_string)
  string_value = String(pin_string)
  matches = match_pattern(string_value)

  letter = matches[:letter]
  enhanced = matches[:prefix] == ENHANCED_PREFIX
  diminished = matches[:prefix] == DIMINISHED_PREFIX

  type = letter.upcase.to_sym
  side = letter == letter.upcase ? FIRST_PLAYER : SECOND_PLAYER
  state = if enhanced
            ENHANCED_STATE
          elsif diminished
            DIMINISHED_STATE
          else
            NORMAL_STATE
          end

  new(type, side, state)
end

.valid?(pin_string) ⇒ Boolean

Check if a string is a valid PIN notation

Examples:

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

Parameters:

  • pin_string (String)

    The string to validate

Returns:

  • (Boolean)

    true if valid PIN, false otherwise



119
120
121
122
123
# File 'lib/sashite/pin/identifier.rb', line 119

def self.valid?(pin_string)
  return false unless pin_string.is_a?(::String)

  pin_string.match?(PIN_PATTERN)
end

.validate_side(side) ⇒ Object

Validate that the side is a valid symbol

Parameters:

  • side (Symbol)

    the side to validate

Raises:

  • (ArgumentError)

    if invalid



336
337
338
339
340
# File 'lib/sashite/pin/identifier.rb', line 336

def self.validate_side(side)
  return if VALID_SIDES.include?(side)

  raise ::ArgumentError, format(ERROR_INVALID_SIDE, side.inspect)
end

.validate_state(state) ⇒ Object

Validate that the state is a valid symbol

Parameters:

  • state (Symbol)

    the state to validate

Raises:

  • (ArgumentError)

    if invalid



346
347
348
349
350
# File 'lib/sashite/pin/identifier.rb', line 346

def self.validate_state(state)
  return if VALID_STATES.include?(state)

  raise ::ArgumentError, format(ERROR_INVALID_STATE, state.inspect)
end

.validate_type(type) ⇒ Object

Validate that the type is a valid symbol

Parameters:

  • type (Symbol)

    the type to validate

Raises:

  • (ArgumentError)

    if invalid



326
327
328
329
330
# File 'lib/sashite/pin/identifier.rb', line 326

def self.validate_type(type)
  return if VALID_TYPES.include?(type)

  raise ::ArgumentError, format(ERROR_INVALID_TYPE, type.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



306
307
308
309
310
# File 'lib/sashite/pin/identifier.rb', line 306

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

  type == other.type && side == other.side && state == other.state
end

#diminishIdentifier

Create a new identifier with diminished state

Returns:

  • (Identifier)

    new identifier instance with diminished state



173
174
175
176
177
# File 'lib/sashite/pin/identifier.rb', line 173

def diminish
  return self if diminished?

  self.class.new(type, side, DIMINISHED_STATE)
end

#diminished?Boolean

Check if the identifier has diminished state

Returns:

  • (Boolean)

    true if diminished



247
248
249
# File 'lib/sashite/pin/identifier.rb', line 247

def diminished?
  state == DIMINISHED_STATE
end

#enhanceIdentifier

Create a new identifier with enhanced state

Returns:

  • (Identifier)

    new identifier instance with enhanced state



155
156
157
158
159
# File 'lib/sashite/pin/identifier.rb', line 155

def enhance
  return self if enhanced?

  self.class.new(type, side, ENHANCED_STATE)
end

#enhanced?Boolean

Check if the identifier has enhanced state

Returns:

  • (Boolean)

    true if enhanced



240
241
242
# File 'lib/sashite/pin/identifier.rb', line 240

def enhanced?
  state == ENHANCED_STATE
end

#first_player?Boolean

Check if the identifier belongs to the first player

Returns:

  • (Boolean)

    true if first player



261
262
263
# File 'lib/sashite/pin/identifier.rb', line 261

def first_player?
  side == FIRST_PLAYER
end

#flipIdentifier

Create a new identifier with opposite side

Returns:

  • (Identifier)

    new identifier instance with opposite side



200
201
202
# File 'lib/sashite/pin/identifier.rb', line 200

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

#hashInteger

Custom hash implementation for use in collections

Returns:

  • (Integer)

    hash value



318
319
320
# File 'lib/sashite/pin/identifier.rb', line 318

def hash
  [self.class, type, side, state].hash
end

#letterString

Get the letter representation

Returns:

  • (String)

    letter representation combining type and side



137
138
139
# File 'lib/sashite/pin/identifier.rb', line 137

def letter
  first_player? ? type.to_s.upcase : type.to_s.downcase
end

#normal?Boolean

Check if the identifier has normal state

Returns:

  • (Boolean)

    true if normal



254
255
256
# File 'lib/sashite/pin/identifier.rb', line 254

def normal?
  state == NORMAL_STATE
end

#normalizeIdentifier

Create a new identifier with normal state (no modifiers)

Returns:

  • (Identifier)

    new identifier instance with normal state



191
192
193
194
195
# File 'lib/sashite/pin/identifier.rb', line 191

def normalize
  return self if normal?

  self.class.new(type, side, NORMAL_STATE)
end

#prefixString

Get the prefix representation

Returns:

  • (String)

    prefix representing the state



144
145
146
147
148
149
150
# File 'lib/sashite/pin/identifier.rb', line 144

def prefix
  case state
  when ENHANCED_STATE then ENHANCED_PREFIX
  when DIMINISHED_STATE then DIMINISHED_PREFIX
  else NORMAL_PREFIX
  end
end

#same_side?(other) ⇒ Boolean

Check if this identifier has the same side as another

Parameters:

  • other (Identifier)

    identifier to compare with

Returns:

  • (Boolean)

    true if same side



286
287
288
289
290
# File 'lib/sashite/pin/identifier.rb', line 286

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

  side == other.side
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



296
297
298
299
300
# File 'lib/sashite/pin/identifier.rb', line 296

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

  state == other.state
end

#same_type?(other) ⇒ Boolean

Check if this identifier is the same type as another

Parameters:

  • other (Identifier)

    identifier to compare with

Returns:

  • (Boolean)

    true if same type



276
277
278
279
280
# File 'lib/sashite/pin/identifier.rb', line 276

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

  type == other.type
end

#second_player?Boolean

Check if the identifier belongs to the second player

Returns:

  • (Boolean)

    true if second player



268
269
270
# File 'lib/sashite/pin/identifier.rb', line 268

def second_player?
  side == SECOND_PLAYER
end

#to_sString

Convert the identifier to its PIN string representation

Examples:

identifier.to_s  # => "+R"

Returns:

  • (String)

    PIN notation string



130
131
132
# File 'lib/sashite/pin/identifier.rb', line 130

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

#undiminishIdentifier

Create a new identifier without diminished state

Returns:

  • (Identifier)

    new identifier instance with normal state



182
183
184
185
186
# File 'lib/sashite/pin/identifier.rb', line 182

def undiminish
  return self unless diminished?

  self.class.new(type, side, NORMAL_STATE)
end

#unenhanceIdentifier

Create a new identifier without enhanced state

Returns:

  • (Identifier)

    new identifier instance with normal state



164
165
166
167
168
# File 'lib/sashite/pin/identifier.rb', line 164

def unenhance
  return self unless enhanced?

  self.class.new(type, side, NORMAL_STATE)
end

#with_side(new_side) ⇒ Identifier

Create a new identifier with a different side

Parameters:

  • new_side (Symbol)

    new side (:first or :second)

Returns:

  • (Identifier)

    new identifier instance with new side



219
220
221
222
223
224
# File 'lib/sashite/pin/identifier.rb', line 219

def with_side(new_side)
  self.class.validate_side(new_side)
  return self if side == new_side

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

#with_state(new_state) ⇒ Identifier

Create a new identifier with a different state

Parameters:

  • new_state (Symbol)

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

Returns:

  • (Identifier)

    new identifier instance with new state



230
231
232
233
234
235
# File 'lib/sashite/pin/identifier.rb', line 230

def with_state(new_state)
  self.class.validate_state(new_state)
  return self if state == new_state

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

#with_type(new_type) ⇒ Identifier

Create a new identifier with a different type

Parameters:

  • new_type (Symbol)

    new type (:A to :Z)

Returns:

  • (Identifier)

    new identifier instance with new type



208
209
210
211
212
213
# File 'lib/sashite/pin/identifier.rb', line 208

def with_type(new_type)
  self.class.validate_type(new_type)
  return self if type == new_type

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