epin.rb
EPIN (Extended Piece Identifier Notation) implementation for Ruby.
Overview
This library implements the EPIN Specification v1.0.0.
EPIN extends PIN with an optional derivation marker (') that flags whether a piece uses a native or derived style.
Installation
# In your Gemfile
gem "sashite-epin"Or install manually:
gem install sashite-epinDependencies
gem "sashite-pin" # Piece Identifier NotationUsage
Parsing (String → Identifier)
Convert an EPIN string into an Identifier object.
require "sashite/epin"
# Standard parsing (raises on error)
epin = Sashite::Epin.parse("K^'")
epin.to_s # => "K^'"
# Access PIN attributes through the component
epin.pin.abbr # => :K
epin.pin.side # => :first
epin.pin.state # => :normal
epin.pin.terminal? # => true
# Access derivation status
epin.derived? # => true
epin.native? # => false
# PIN component is a full Sashite::Pin::Identifier instance
epin.pin.enhanced? # => false
epin.pin.first_player? # => true
# Invalid input raises ArgumentError
Sashite::Epin.parse("invalid") # => raises ArgumentErrorFormatting (Identifier → String)
Convert an Identifier back to an EPIN string.
# From PIN component
pin = Sashite::Pin.parse("K^")
epin = Sashite::Epin::Identifier.new(pin)
epin.to_s # => "K^"
# With derivation
epin = Sashite::Epin::Identifier.new(pin, derived: true)
epin.to_s # => "K^'"Validation
# Boolean check
Sashite::Epin.valid?("K") # => true
Sashite::Epin.valid?("+R^'") # => true
Sashite::Epin.valid?("invalid") # => false
Sashite::Epin.valid?("K''") # => false
Sashite::Epin.valid?("K'^") # => falseAccessing Components
epin = Sashite::Epin.parse("+R^'")
# Get PIN component
epin.pin # => #<Sashite::Pin::Identifier +R^>
epin.pin.to_s # => "+R^"
# Check derivation
epin.derived? # => true
epin.native? # => false
# Serialize
epin.to_s # => "+R^'"Transformations
All transformations return new immutable instances.
epin = Sashite::Epin.parse("K^")
# Derivation transformations
epin.derive.to_s # => "K^'"
epin.native.to_s # => "K^"
# Replace PIN component
new_pin = Sashite::Pin.parse("+Q^")
epin.with_pin(new_pin).to_s # => "+Q^"Transform via PIN Component
epin = Sashite::Epin.parse("K^'")
# Change abbr
epin.with_pin(epin.pin.with_abbr(:Q)).to_s # => "Q^'"
# Change state
epin.with_pin(epin.pin.enhance).to_s # => "+K^'"
# Change side
epin.with_pin(epin.pin.flip).to_s # => "k^'"
# Remove terminal
epin.with_pin(epin.pin.non_terminal).to_s # => "K'"Component Queries
Use the PIN API directly:
epin = Sashite::Epin.parse("+P^'")
# PIN queries
epin.pin.abbr # => :P
epin.pin.side # => :first
epin.pin.state # => :enhanced
epin.pin.terminal? # => true
epin.pin.first_player? # => true
epin.pin.enhanced? # => true
# EPIN queries
epin.derived? # => true
epin.native? # => false
# Compare EPINs
other = Sashite::Epin.parse("+P^")
epin.pin.same_abbr?(other.pin) # => true
epin.pin.same_state?(other.pin) # => true
epin.same_derived?(other) # => falseAPI Reference
Types
# Identifier represents a parsed EPIN combining PIN with derivation status.
class Sashite::Epin::Identifier
# Creates an Identifier from a PIN component.
# Raises ArgumentError if the PIN is invalid.
#
# @param pin [Sashite::Pin::Identifier] PIN component
# @param derived [Boolean] Derived status
# @return [Identifier]
def initialize(pin, derived: false)
# Returns the PIN component.
#
# @return [Sashite::Pin::Identifier]
def pin
# Returns true if derived style.
#
# @return [Boolean]
def derived?
# Returns true if native style.
#
# @return [Boolean]
def native?
# Returns the EPIN string representation.
#
# @return [String]
def to_s
endParsing
# Parses an EPIN string into an Identifier.
# Raises ArgumentError if the string is not valid.
#
# @param string [String] EPIN string
# @return [Identifier]
# @raise [ArgumentError] if invalid
def Sashite::Epin.parse(string)Validation
# Reports whether string is a valid EPIN.
#
# @param string [String] EPIN string
# @return [Boolean]
def Sashite::Epin.valid?(string)Transformations
# PIN replacement (returns new Identifier)
def with_pin(new_pin) # => Identifier with different PIN
# Derivation transformations
def derive # => Identifier with derived: true
def native # => Identifier with derived: falseErrors
All parsing and validation errors raise ArgumentError with descriptive messages:
| Message | Cause |
|---|---|
"invalid derivation marker" |
Derivation marker misplaced or duplicated |
"invalid PIN component: ..." |
PIN parsing failed |
PIN Compatibility
Every valid PIN is a valid EPIN (native by default):
%w[K +R -p K^ +R^].each do |pin_token|
epin = Sashite::Epin.parse(pin_token)
epin.native? # => true
epin.to_s # => pin_token
endDesign Principles
- Pure composition: EPIN composes PIN without reimplementing features
-
Minimal API: Core methods (
pin,derived?,native?,to_s) plus transformations - Component transparency: Access PIN directly, no wrapper methods
- Immutable identifiers: Frozen instances prevent mutation
-
Ruby idioms:
valid?predicate,to_sconversion,ArgumentErrorfor invalid input
Related Specifications
- Game Protocol — Conceptual foundation
- EPIN Specification — Official specification
- EPIN Examples — Usage examples
- PIN Specification — Base component
License
Available as open source under the Apache License 2.0.