Class: Sashite::Gan::Actor
- Inherits:
-
Object
- Object
- Sashite::Gan::Actor
- Defined in:
- lib/sashite/qpi/actor.rb
Overview
Represents a game actor in GAN (General Actor Notation) format.
An actor combines a style identifier (SNN format) with a piece identifier (PIN format) using a colon separator and consistent case encoding to create an unambiguous representation of a game piece within its style context.
GAN represents all four fundamental piece attributes from the Game Protocol:
-
Type → PIN component (ASCII letter choice)
-
Side → Consistent case encoding across both SNN and PIN components
-
State → PIN component (optional prefix modifier)
-
Style → SNN component (explicit style identifier)
All instances are immutable - transformation methods return new instances. This follows the Game Protocol’s actor model with complete attribute representation.
Constant Summary collapse
- SEPARATOR =
Colon separator character
":"- FIRST_PLAYER =
Player side constants
:first- SECOND_PLAYER =
:second- NORMAL_STATE =
State constants
:normal- ENHANCED_STATE =
:enhanced- DIMINISHED_STATE =
:diminished- VALID_SIDES =
Valid sides
[FIRST_PLAYER, SECOND_PLAYER].freeze
- VALID_STATES =
Valid states
[NORMAL_STATE, ENHANCED_STATE, DIMINISHED_STATE].freeze
- VALID_TYPES =
Valid types (A-Z)
(:A..:Z).to_a.freeze
- ERROR_INVALID_GAN =
Error messages
"Invalid GAN format: %s"- ERROR_CASE_MISMATCH =
"Case mismatch between SNN and PIN components in GAN string: %s"- ERROR_INVALID_NAME =
"Name must be a symbol with proper capitalization, got: %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"
Class Method Summary collapse
-
.parse(gan_string) ⇒ Actor
Parse a GAN string into an Actor object.
-
.valid?(gan_string) ⇒ Boolean
Check if a string is a valid GAN notation.
-
.validate_name(name) ⇒ Object
Validate that the name is a valid symbol with proper capitalization.
-
.validate_side(side) ⇒ Object
Validate that the side is a valid symbol.
-
.validate_state(state) ⇒ Object
Validate that the state is a valid symbol.
-
.validate_type(type) ⇒ Object
Validate that the type is a valid symbol.
Instance Method Summary collapse
-
#==(other) ⇒ Boolean
(also: #eql?)
Custom equality comparison.
-
#diminish ⇒ Actor
Create a new actor with diminished piece state.
-
#diminished? ⇒ Boolean
Check if the actor has diminished state.
-
#enhance ⇒ Actor
Create a new actor with enhanced piece state.
-
#enhanced? ⇒ Boolean
Check if the actor has enhanced state.
-
#first_player? ⇒ Boolean
Check if the actor belongs to the first player.
-
#flip ⇒ Actor
Create a new actor with opposite ownership (side).
-
#hash ⇒ Integer
Custom hash implementation for use in collections.
-
#initialize(name, type, side, state = NORMAL_STATE) ⇒ Actor
constructor
Create a new actor instance.
-
#name ⇒ Symbol
Get the style name.
-
#normal? ⇒ Boolean
Check if the actor has normal state (no modifiers).
-
#normalize ⇒ Actor
Create a new actor with normal piece state (no modifiers).
-
#same_name?(other) ⇒ Boolean
Check if this actor has the same style name as another.
-
#same_side?(other) ⇒ Boolean
Check if this actor belongs to the same side as another.
-
#same_state?(other) ⇒ Boolean
Check if this actor has the same state as another.
-
#same_type?(other) ⇒ Boolean
Check if this actor is the same type as another (ignoring name, side, and state).
-
#second_player? ⇒ Boolean
Check if the actor belongs to the second player.
-
#side ⇒ Symbol
Get the player side.
-
#state ⇒ Symbol
Get the piece state.
-
#to_pin ⇒ String
Convert the actor to its PIN representation (piece component only).
-
#to_s ⇒ String
Convert the actor to its GAN string representation.
-
#to_snn ⇒ String
Convert the actor to its SNN representation (style component only).
-
#type ⇒ Symbol
Get the piece type.
-
#with_name(new_name) ⇒ Actor
Create a new actor with a different style name (keeping same type, side, and state).
-
#with_side(new_side) ⇒ Actor
Create a new actor with a different side (keeping same name, type, and state).
-
#with_state(new_state) ⇒ Actor
Create a new actor with a different piece state (keeping same name, type, and side).
-
#with_type(new_type) ⇒ Actor
Create a new actor with a different piece type (keeping same name, side, and state).
Constructor Details
#initialize(name, type, side, state = NORMAL_STATE) ⇒ Actor
Create a new actor instance
62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/sashite/qpi/actor.rb', line 62 def initialize(name, type, side, state = NORMAL_STATE) self.class.validate_name(name) self.class.validate_type(type) self.class.validate_side(side) self.class.validate_state(state) @style = Snn::Style.new(name, side) @piece = Pin::Piece.new(type, side, state) freeze end |
Class Method Details
.parse(gan_string) ⇒ Actor
Parse a GAN string into an Actor object
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/sashite/qpi/actor.rb', line 83 def self.parse(gan_string) string_value = String(gan_string) # Split into SNN and PIN components snn_part, pin_part = string_value.split(SEPARATOR, 2) # Validate basic format unless snn_part && pin_part && string_value.count(SEPARATOR) == 1 raise ::ArgumentError, format(ERROR_INVALID_GAN, string_value) end # Validate case consistency validate_case_consistency(snn_part, pin_part, string_value) # Parse components - let SNN and PIN handle their own validation parsed_style = Snn::Style.parse(snn_part) parsed_piece = Pin::Piece.parse(pin_part) # Create actor with parsed components new(parsed_style.name, parsed_piece.type, parsed_style.side, parsed_piece.state) end |
.valid?(gan_string) ⇒ Boolean
Check if a string is a valid GAN notation
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/sashite/qpi/actor.rb', line 117 def self.valid?(gan_string) return false unless gan_string.is_a?(::String) return false if gan_string.empty? # Split into SNN and PIN components parts = gan_string.split(SEPARATOR, 2) return false unless parts.length == 2 snn_part, pin_part = parts # Validate each component with its specific regex return false unless snn_part.match?(Snn::Style::SNN_PATTERN) return false unless pin_part.match?(Pin::Piece::PIN_PATTERN) # Check case consistency between components case_consistent?(snn_part, pin_part) end |
.validate_name(name) ⇒ Object
Validate that the name is a valid symbol with proper capitalization
405 406 407 408 409 |
# File 'lib/sashite/qpi/actor.rb', line 405 def self.validate_name(name) return if valid_name?(name) raise ::ArgumentError, format(ERROR_INVALID_NAME, name.inspect) end |
.validate_side(side) ⇒ Object
Validate that the side is a valid symbol
425 426 427 428 429 |
# File 'lib/sashite/qpi/actor.rb', line 425 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
435 436 437 438 439 |
# File 'lib/sashite/qpi/actor.rb', line 435 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
415 416 417 418 419 |
# File 'lib/sashite/qpi/actor.rb', line 415 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
385 386 387 388 389 |
# File 'lib/sashite/qpi/actor.rb', line 385 def ==(other) return false unless other.is_a?(self.class) name == other.name && type == other.type && side == other.side && state == other.state end |
#diminish ⇒ Actor
Create a new actor with diminished piece state
220 221 222 223 224 |
# File 'lib/sashite/qpi/actor.rb', line 220 def diminish return self if diminished? self.class.new(name, type, side, DIMINISHED_STATE) end |
#diminished? ⇒ Boolean
Check if the actor has diminished state
312 313 314 |
# File 'lib/sashite/qpi/actor.rb', line 312 def diminished? piece.diminished? end |
#enhance ⇒ Actor
Create a new actor with enhanced piece state
209 210 211 212 213 |
# File 'lib/sashite/qpi/actor.rb', line 209 def enhance return self if enhanced? self.class.new(name, type, side, ENHANCED_STATE) end |
#enhanced? ⇒ Boolean
Check if the actor has enhanced state
305 306 307 |
# File 'lib/sashite/qpi/actor.rb', line 305 def enhanced? piece.enhanced? end |
#first_player? ⇒ Boolean
Check if the actor belongs to the first player
326 327 328 |
# File 'lib/sashite/qpi/actor.rb', line 326 def first_player? style.first_player? end |
#flip ⇒ Actor
Create a new actor with opposite ownership (side)
Changes both the style and piece sides consistently. This method is rule-agnostic and preserves all piece modifiers.
246 247 248 |
# File 'lib/sashite/qpi/actor.rb', line 246 def flip self.class.new(name, type, opposite_side, state) end |
#hash ⇒ Integer
Custom hash implementation for use in collections
397 398 399 |
# File 'lib/sashite/qpi/actor.rb', line 397 def hash [self.class, name, type, side, state].hash end |
#name ⇒ Symbol
Get the style name
173 174 175 |
# File 'lib/sashite/qpi/actor.rb', line 173 def name style.name end |
#normal? ⇒ Boolean
Check if the actor has normal state (no modifiers)
319 320 321 |
# File 'lib/sashite/qpi/actor.rb', line 319 def normal? piece.normal? end |
#normalize ⇒ Actor
Create a new actor with normal piece state (no modifiers)
231 232 233 234 235 |
# File 'lib/sashite/qpi/actor.rb', line 231 def normalize return self if normal? self.class.new(name, type, side, NORMAL_STATE) end |
#same_name?(other) ⇒ Boolean
Check if this actor has the same style name as another
343 344 345 346 347 |
# File 'lib/sashite/qpi/actor.rb', line 343 def same_name?(other) return false unless other.is_a?(self.class) name == other.name end |
#same_side?(other) ⇒ Boolean
Check if this actor belongs to the same side as another
365 366 367 368 369 |
# File 'lib/sashite/qpi/actor.rb', line 365 def same_side?(other) return false unless other.is_a?(self.class) side == other.side end |
#same_state?(other) ⇒ Boolean
Check if this actor has the same state as another
375 376 377 378 379 |
# File 'lib/sashite/qpi/actor.rb', line 375 def same_state?(other) return false unless other.is_a?(self.class) state == other.state end |
#same_type?(other) ⇒ Boolean
Check if this actor is the same type as another (ignoring name, side, and state)
355 356 357 358 359 |
# File 'lib/sashite/qpi/actor.rb', line 355 def same_type?(other) return false unless other.is_a?(self.class) type == other.type end |
#second_player? ⇒ Boolean
Check if the actor belongs to the second player
333 334 335 |
# File 'lib/sashite/qpi/actor.rb', line 333 def second_player? style.second_player? end |
#side ⇒ Symbol
Get the player side
191 192 193 |
# File 'lib/sashite/qpi/actor.rb', line 191 def side style.side end |
#state ⇒ Symbol
Get the piece state
200 201 202 |
# File 'lib/sashite/qpi/actor.rb', line 200 def state piece.state end |
#to_pin ⇒ String
Convert the actor to its PIN representation (piece component only)
153 154 155 |
# File 'lib/sashite/qpi/actor.rb', line 153 def to_pin piece.to_s end |
#to_s ⇒ String
Convert the actor to its GAN string representation
142 143 144 |
# File 'lib/sashite/qpi/actor.rb', line 142 def to_s "#{style}#{SEPARATOR}#{piece}" end |
#to_snn ⇒ String
Convert the actor to its SNN representation (style component only)
164 165 166 |
# File 'lib/sashite/qpi/actor.rb', line 164 def to_snn style.to_s end |
#type ⇒ Symbol
Get the piece type
182 183 184 |
# File 'lib/sashite/qpi/actor.rb', line 182 def type piece.type end |
#with_name(new_name) ⇒ Actor
Create a new actor with a different style name (keeping same type, side, and state)
256 257 258 259 260 261 |
# File 'lib/sashite/qpi/actor.rb', line 256 def with_name(new_name) self.class.validate_name(new_name) return self if name == new_name self.class.new(new_name, type, side, state) end |
#with_side(new_side) ⇒ Actor
Create a new actor with a different side (keeping same name, type, and state)
282 283 284 285 286 287 |
# File 'lib/sashite/qpi/actor.rb', line 282 def with_side(new_side) self.class.validate_side(new_side) return self if side == new_side self.class.new(name, type, new_side, state) end |
#with_state(new_state) ⇒ Actor
Create a new actor with a different piece state (keeping same name, type, and side)
295 296 297 298 299 300 |
# File 'lib/sashite/qpi/actor.rb', line 295 def with_state(new_state) self.class.validate_state(new_state) return self if state == new_state self.class.new(name, type, side, new_state) end |
#with_type(new_type) ⇒ Actor
Create a new actor with a different piece type (keeping same name, side, and state)
269 270 271 272 273 274 |
# File 'lib/sashite/qpi/actor.rb', line 269 def with_type(new_type) self.class.validate_type(new_type) return self if type == new_type self.class.new(name, new_type, side, state) end |