class Sashite::Snn::Style

This follows the SNN Specification v1.0.0 with Letter and Side attributes.
All instances are immutable - transformation methods return new instances.
- Lowercase letter: second player (a, b, c, …, z)
- Uppercase letter: first player (A, B, C, …, Z)
A style consists of a single ASCII letter with case-based side encoding:
Represents a style in SNN (Style Name Notation) format.

def self.parse(snn_string)

Other tags:
    Example: Parse SNN strings with case-based side inference -

Raises:
  • (ArgumentError) - if the SNN string is invalid

Returns:
  • (Style) - parsed style object with letter and inferred side

Parameters:
  • snn_string (String) -- SNN notation string (single ASCII letter)
def self.parse(snn_string)
  string_value = String(snn_string)
  validate_snn_string(string_value)
  # Determine side from case
  style_side = string_value == string_value.upcase ? FIRST_PLAYER : SECOND_PLAYER
  # Use the letter directly as symbol
  style_letter = string_value.to_sym
  new(style_letter, style_side)
end

def self.valid?(snn_string)

Other tags:
    Example: Validate SNN strings -

Returns:
  • (Boolean) - true if valid SNN, false otherwise

Parameters:
  • snn_string (String) -- the string to validate
def self.valid?(snn_string)
  return false unless snn_string.is_a?(::String)
  snn_string.match?(SNN_PATTERN)
end

def self.valid_letter?(letter)

Returns:
  • (Boolean) - true if valid

Parameters:
  • letter (Object) -- the letter to check
def self.valid_letter?(letter)
  return false unless letter.is_a?(::Symbol)
  letter_string = letter.to_s
  return false if letter_string.empty?
  # Must be exactly one ASCII letter
  letter_string.match?(SNN_PATTERN)
end

def self.validate_letter(letter)

Raises:
  • (ArgumentError) - if invalid

Parameters:
  • letter (Symbol) -- the letter to validate
def self.validate_letter(letter)
  return if valid_letter?(letter)
  raise ::ArgumentError, format(ERROR_INVALID_LETTER, letter.inspect)
end

def self.validate_side(side)

Raises:
  • (ArgumentError) - if invalid

Parameters:
  • side (Symbol) -- the side to validate
def self.validate_side(side)
  return if VALID_SIDES.include?(side)
  raise ::ArgumentError, format(ERROR_INVALID_SIDE, side.inspect)
end

def self.validate_snn_string(string)

Raises:
  • (ArgumentError) - if string doesn't match SNN pattern

Parameters:
  • string (String) -- string to validate
def self.validate_snn_string(string)
  return if string.match?(SNN_PATTERN)
  raise ::ArgumentError, format(ERROR_INVALID_SNN, string)
end

def ==(other)

Returns:
  • (Boolean) - true if both objects are styles with identical letter and side

Parameters:
  • other (Object) -- object to compare with
def ==(other)
  return false unless other.is_a?(self.class)
  letter == other.letter && side == other.side
end

def first_player?

Returns:
  • (Boolean) - true if first player
def first_player?
  side == FIRST_PLAYER
end

def flip

Other tags:
    Example: Flip player sides -

Returns:
  • (Style) - new immutable style instance with flipped side
def flip
  new_letter = first_player? ? letter.to_s.downcase.to_sym : letter.to_s.upcase.to_sym
  self.class.new(new_letter, opposite_side)
end

def hash

Returns:
  • (Integer) - hash value based on class, letter, and side
def hash
  [self.class, letter, side].hash
end

def initialize(letter, side)

Raises:
  • (ArgumentError) - if parameters are invalid

Parameters:
  • side (Symbol) -- player side (:first or :second)
  • letter (Symbol) -- style letter (single ASCII letter as symbol)
def initialize(letter, side)
  self.class.validate_letter(letter)
  self.class.validate_side(side)
  @letter = letter
  @side = side
  freeze
end

def opposite_side

Returns:
  • (Symbol) - the opposite side
def opposite_side
  first_player? ? SECOND_PLAYER : FIRST_PLAYER
end

def same_letter?(other)

Other tags:
    Example: Compare style letter families -

Returns:
  • (Boolean) - true if both styles use the same letter family (case-insensitive)

Parameters:
  • other (Style) -- style to compare with
def same_letter?(other)
  return false unless other.is_a?(self.class)
  letter.to_s.upcase == other.letter.to_s.upcase
end

def same_side?(other)

Returns:
  • (Boolean) - true if both styles belong to the same side

Parameters:
  • other (Style) -- style to compare with
def same_side?(other)
  return false unless other.is_a?(self.class)
  side == other.side
end

def second_player?

Returns:
  • (Boolean) - true if second player
def second_player?
  side == SECOND_PLAYER
end

def to_s

Other tags:
    Example: Display styles -

Returns:
  • (String) - SNN notation string (single ASCII letter)
def to_s
  letter.to_s
end

def with_letter(new_letter)

Other tags:
    Example: Change style letter -

Returns:
  • (Style) - new immutable style instance with different letter

Parameters:
  • new_letter (Symbol) -- new letter (single ASCII letter as symbol)
def with_letter(new_letter)
  self.class.validate_letter(new_letter)
  return self if letter == new_letter
  # Ensure the new letter has the correct case for the current side
  adjusted_letter = first_player? ? new_letter.to_s.upcase.to_sym : new_letter.to_s.downcase.to_sym
  self.class.new(adjusted_letter, side)
end

def with_side(new_side)

Other tags:
    Example: Change player side -

Returns:
  • (Style) - new immutable style instance with different side

Parameters:
  • new_side (Symbol) -- :first or :second
def with_side(new_side)
  self.class.validate_side(new_side)
  return self if side == new_side
  # Adjust letter case for the new side
  new_letter = new_side == FIRST_PLAYER ? letter.to_s.upcase.to_sym : letter.to_s.downcase.to_sym
  self.class.new(new_letter, new_side)
end