Class: Sashite::Pnn::Piece

Inherits:
Object
  • Object
show all
Defined in:
lib/sashite/pnn/piece.rb

Overview

Represents a piece in PNN (Piece Name Notation) format.

A piece 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

FOREIGN_SUFFIX =

Valid derivation suffixes

"'"
NATIVE_SUFFIX =
""
NATIVE =

Derivation constants

true
FOREIGN =
false
VALID_DERIVATIONS =

Valid derivations

[NATIVE, FOREIGN].freeze
ERROR_INVALID_PNN =

Error messages

"Invalid PNN 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::Piece::NORMAL_STATE, native = NATIVE) ⇒ Piece

Create a new piece instance

Examples:

Piece.new(:K, :first, :normal, true)
Piece.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::Piece::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/pnn/piece.rb', line 67

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

  @piece = Pin::Piece.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/pnn/piece.rb', line 55

def native
  @native
end

Class Method Details

.parse(pnn_string) ⇒ Piece

Parse a PNN string into a Piece object

Examples:

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

Parameters:

  • pnn_string (String)

    PNN notation string

Returns:

  • (Piece)

    new piece instance

Raises:

  • (ArgumentError)

    if the PNN 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/pnn/piece.rb', line 89

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

  # Check for derivation suffix
  if string_value.end_with?(FOREIGN_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_PNN, string_value) unless Pin::Piece.valid?(pin_part)

  pin_piece = Pin::Piece.parse(pin_part)
  piece_native = !foreign

  new(pin_piece.type, pin_piece.side, pin_piece.state, piece_native)
end

.valid?(pnn_string) ⇒ Boolean

Check if a string is a valid PNN notation

Examples:

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

Parameters:

  • pnn_string (String)

    The string to validate

Returns:

  • (Boolean)

    true if valid PNN, false otherwise



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

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

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

  # Validate the PIN part using existing PIN validation
  Pin::Piece.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/pnn/piece.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 pieces are equal



405
406
407
408
409
# File 'lib/sashite/pnn/piece.rb', line 405

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

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

#derivePiece

Create a new piece with foreign style (derivation marker)

Examples:

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

Returns:

  • (Piece)

    new piece instance with foreign style



238
239
240
241
242
# File 'lib/sashite/pnn/piece.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 piece has foreign style (derivation marker)

Returns:

  • (Boolean)

    true if foreign style



352
353
354
# File 'lib/sashite/pnn/piece.rb', line 352

def derived?
  native == FOREIGN
end

#diminishPiece

Create a new piece with diminished state

Examples:

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

Returns:

  • (Piece)

    new piece instance with diminished state



196
197
198
199
200
# File 'lib/sashite/pnn/piece.rb', line 196

def diminish
  return self if diminished?

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

#diminished?Boolean

Check if the piece has diminished state

Returns:

  • (Boolean)

    true if diminished



317
318
319
# File 'lib/sashite/pnn/piece.rb', line 317

def diminished?
  @piece.diminished?
end

#enhancePiece

Create a new piece with enhanced state

Examples:

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

Returns:

  • (Piece)

    new piece instance with enhanced state



174
175
176
177
178
# File 'lib/sashite/pnn/piece.rb', line 174

def enhance
  return self if enhanced?

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

#enhanced?Boolean

Check if the piece has enhanced state

Returns:

  • (Boolean)

    true if enhanced



310
311
312
# File 'lib/sashite/pnn/piece.rb', line 310

def enhanced?
  @piece.enhanced?
end

#first_player?Boolean

Check if the piece belongs to the first player

Returns:

  • (Boolean)

    true if first player



331
332
333
# File 'lib/sashite/pnn/piece.rb', line 331

def first_player?
  @piece.first_player?
end

#flipPiece

Create a new piece with opposite side

Examples:

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

Returns:

  • (Piece)

    new piece instance with opposite side



229
230
231
# File 'lib/sashite/pnn/piece.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/pnn/piece.rb', line 417

def hash
  [self.class, @piece, 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/pnn/piece.rb', line 151

def letter
  @piece.letter
end

#native?Boolean

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

Returns:

  • (Boolean)

    true if native style



345
346
347
# File 'lib/sashite/pnn/piece.rb', line 345

def native?
  native == NATIVE
end

#normal?Boolean

Check if the piece has normal state (no modifiers)

Returns:

  • (Boolean)

    true if no modifiers are present



324
325
326
# File 'lib/sashite/pnn/piece.rb', line 324

def normal?
  @piece.normal?
end

#normalizePiece

Create a new piece with normal state (no modifiers)

Examples:

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

Returns:

  • (Piece)

    new piece instance with normal state



218
219
220
221
222
# File 'lib/sashite/pnn/piece.rb', line 218

def normalize
  return self if normal?

  self.class.new(type, side, Pin::Piece::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/pnn/piece.rb', line 158

def prefix
  @piece.prefix
end

#same_side?(other) ⇒ Boolean

Check if this piece belongs to the same side as another

Parameters:

  • other (Piece)

    piece to compare with

Returns:

  • (Boolean)

    true if same side



375
376
377
378
379
# File 'lib/sashite/pnn/piece.rb', line 375

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

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

#same_state?(other) ⇒ Boolean

Check if this piece has the same state as another

Parameters:

  • other (Piece)

    piece to compare with

Returns:

  • (Boolean)

    true if same state



385
386
387
388
389
# File 'lib/sashite/pnn/piece.rb', line 385

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

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

#same_style?(other) ⇒ Boolean

Check if this piece has the same style derivation as another

Parameters:

  • other (Piece)

    piece to compare with

Returns:

  • (Boolean)

    true if same style derivation



395
396
397
398
399
# File 'lib/sashite/pnn/piece.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 piece 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 (Piece)

    piece to compare with

Returns:

  • (Boolean)

    true if same type



365
366
367
368
369
# File 'lib/sashite/pnn/piece.rb', line 365

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

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

#second_player?Boolean

Check if the piece belongs to the second player

Returns:

  • (Boolean)

    true if second player



338
339
340
# File 'lib/sashite/pnn/piece.rb', line 338

def second_player?
  @piece.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/pnn/piece.rb', line 45

def side
  @piece.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/pnn/piece.rb', line 50

def state
  @piece.state
end

#suffixString

Get the suffix representation

Returns:

  • (String)

    suffix representing the derivation



165
166
167
# File 'lib/sashite/pnn/piece.rb', line 165

def suffix
  native? ? NATIVE_SUFFIX : FOREIGN_SUFFIX
end

#to_sString

Convert the piece to its PNN string representation

Examples:

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

Returns:

  • (String)

    PNN notation string



144
145
146
# File 'lib/sashite/pnn/piece.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/pnn/piece.rb', line 40

def type
  @piece.type
end

#underivePiece

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

Examples:

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

Returns:

  • (Piece)

    new piece instance with native style



249
250
251
252
253
# File 'lib/sashite/pnn/piece.rb', line 249

def underive
  return self if native?

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

#undiminishPiece

Create a new piece without diminished state

Examples:

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

Returns:

  • (Piece)

    new piece instance without diminished state



207
208
209
210
211
# File 'lib/sashite/pnn/piece.rb', line 207

def undiminish
  return self unless diminished?

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

#unenhancePiece

Create a new piece without enhanced state

Examples:

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

Returns:

  • (Piece)

    new piece instance without enhanced state



185
186
187
188
189
# File 'lib/sashite/pnn/piece.rb', line 185

def unenhance
  return self unless enhanced?

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

#with_derivation(new_native) ⇒ Piece

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

Examples:

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

Parameters:

  • new_native (Boolean)

    true for native, false for foreign

Returns:

  • (Piece)

    new piece instance with different derivation



300
301
302
303
304
305
# File 'lib/sashite/pnn/piece.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) ⇒ Piece

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

Examples:

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

Parameters:

  • new_side (Symbol)

    :first or :second

Returns:

  • (Piece)

    new piece instance with different side



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

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

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

#with_state(new_state) ⇒ Piece

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

Examples:

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

Parameters:

  • new_state (Symbol)

    :normal, :enhanced, or :diminished

Returns:

  • (Piece)

    new piece instance with different state



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

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

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

#with_type(new_type) ⇒ Piece

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

Examples:

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

Parameters:

  • new_type (Symbol)

    new type (:A to :Z)

Returns:

  • (Piece)

    new piece instance with different type



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

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

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