Module: Sashite::Qpi

Defined in:
lib/sashite/qpi.rb,
lib/sashite/qpi/identifier.rb

Overview

QPI (Qualified Piece Identifier) implementation for Ruby

Provides complete piece identification by combining two primitive notations:

  • SIN (Style Identifier Notation) — identifies the piece style

  • PIN (Piece Identifier Notation) — identifies the piece attributes

A QPI identifier is simply a pair of (SIN, PIN) with one constraint: both components must represent the same player.

## Core Concept

QPI is pure composition:

sin = Sashite::Sin.parse("C")
pin = Sashite::Pin.parse("K^")
qpi = Sashite::Qpi.new(sin, pin)
qpi.to_s  # => "C:K^"
qpi.sin   # => SIN::Identifier instance
qpi.pin   # => PIN::Identifier instance

All piece attributes come from the components.

## Five Fundamental Attributes

QPI exposes all five attributes from the Sashité Game Protocol:

  • **Piece Style** — via qpi.sin.family

  • **Piece Name** — via qpi.pin.type

  • **Piece Side** — via qpi.sin.side or qpi.pin.side

  • **Piece State** — via qpi.pin.state

  • **Terminal Status** — via qpi.pin.terminal?

## Format Specification

Structure: ‘<sin>:<pin>`

Grammar (BNF):

<qpi> ::= <uppercase-qpi> | <lowercase-qpi>
<uppercase-qpi> ::= <uppercase-letter> ":" <uppercase-pin>
<lowercase-qpi> ::= <lowercase-letter> ":" <lowercase-pin>
<uppercase-pin> ::= ["+" | "-"] <uppercase-letter> ["^"]
<lowercase-pin> ::= ["+" | "-"] <lowercase-letter> ["^"]

Regular Expression: ‘/A(:[-+]?[A-Z]^?|:[-+]?[a-z]^?)z/`

## Semantic Constraint

The SIN and PIN components must represent the same player:

  • Valid: “C:K” (both first player), “c:k” (both second player)

  • Invalid: “C:k” (side mismatch), “c:K” (side mismatch)

## Examples

# Parse QPI string
qpi = Sashite::Qpi.parse("C:K^")
qpi.sin.family        # => :C (Piece Style)
qpi.pin.type          # => :K (Piece Name)
qpi.sin.side          # => :first (Piece Side)
qpi.pin.state         # => :normal (Piece State)
qpi.pin.terminal?     # => true (Terminal Status)

# Create from components
sin = Sashite::Sin.parse("S")
pin = Sashite::Pin.parse("+R^")
qpi = Sashite::Qpi.new(sin, pin)
qpi.to_s              # => "S:+R^"

# Transform via components
qpi.with_sin(qpi.sin.with_family(:C))     # => "C:+R^"
qpi.with_pin(qpi.pin.with_type(:B))       # => "S:+B^"

# Flip both components (only convenience method)
qpi.flip              # => "s:+r^"

## Design Properties

  • Rule-agnostic: Independent of game mechanics

  • **Pure composition**: Zero feature duplication

  • **Minimal API**: Only 5 core methods

  • **Component transparency**: Direct primitive access

  • Immutable: Frozen instances

  • **Semantic validation**: Automatic side consistency

Defined Under Namespace

Classes: Identifier

Class Method Summary collapse

Class Method Details

.new(sin, pin) ⇒ Qpi::Identifier

Create a new identifier from SIN and PIN components

Examples:

sin = Sashite::Sin.parse("C")
pin = Sashite::Pin.parse("K^")
qpi = Sashite::Qpi.new(sin, pin)
qpi.to_s              # => "C:K^"

Raises:

  • (ArgumentError)

    if components have different sides



131
132
133
# File 'lib/sashite/qpi.rb', line 131

def self.new(sin, pin)
  Identifier.new(sin, pin)
end

.parse(qpi_string) ⇒ Qpi::Identifier

Parse a QPI string into an Identifier object

Examples:

qpi = Sashite::Qpi.parse("C:K^")
qpi.sin.family        # => :C
qpi.pin.type          # => :K
qpi.pin.terminal?     # => true

Raises:

  • (ArgumentError)

    if invalid or semantically inconsistent



115
116
117
# File 'lib/sashite/qpi.rb', line 115

def self.parse(qpi_string)
  Identifier.parse(qpi_string)
end

.valid?(qpi_string) ⇒ Boolean

Check if a string is a valid QPI notation

Examples:

Sashite::Qpi.valid?("C:K^")   # => true
Sashite::Qpi.valid?("C:k")    # => false (side mismatch)


100
101
102
# File 'lib/sashite/qpi.rb', line 100

def self.valid?(qpi_string)
  Identifier.valid?(qpi_string)
end