0.0
No release in over 3 years
Low commit activity in last 3 years
A Ruby implementation of GAN (General Actor Notation) v1.0.0 specification for identifying game actors in abstract strategy board games. GAN combines Style Name Notation (SNN) and Piece Identifier Notation (PIN) with a colon separator to provide complete, unambiguous piece identification. Represents all four fundamental piece attributes: Type, Side, State, and Style. Enables cross-style gaming, immutable transformations, and component extraction with to_pin/to_snn methods. Built on sashite-snn and sashite-pin gems.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Runtime

~> 2.0.2
~> 1.1.1
 Project Readme

Qpi.rb

Version Yard documentation Ruby License

QPI (Qualified Piece Identifier) implementation for the Ruby language.

What is QPI?

QPI (Qualified Piece Identifier) provides a rule-agnostic format for identifying game pieces in abstract strategy board games by combining Style Identifier Notation (SIN) and Piece Identifier Notation (PIN) primitives with a colon separator.

This gem implements the QPI Specification v1.0.0 exactly, providing complete piece identification with all four fundamental attributes: Family, Type, Side, and State.

Installation

# In your Gemfile
gem "sashite-qpi"

Or install manually:

gem install sashite-qpi

Dependencies

QPI builds upon two foundational primitive specifications:

gem "sashite-sin"  # Style Identifier Notation
gem "sashite-pin"  # Piece Identifier Notation

Usage

Basic Operations

require "sashite/qpi"

# Parse QPI strings
identifier = Sashite::Qpi.parse("C:K")         # Chess king, first player
identifier.to_s                                # => "C:K"

# Create identifiers from parameters (strict validation)
identifier = Sashite::Qpi.identifier(:C, :K, :first, :normal)
identifier = Sashite::Qpi::Identifier.new(:S, :R, :first, :enhanced)

# Validate QPI strings
Sashite::Qpi.valid?("C:K")                    # => true
Sashite::Qpi.valid?("s:+p")                   # => true
Sashite::Qpi.valid?("C:k")                    # => false (semantic mismatch)

Strict Parameter Validation

Important: QPI enforces the same strict validation as its underlying SIN and PIN primitives:

# ✓ Valid - uppercase symbols only for family and type parameters
Sashite::Qpi.identifier(:C, :K, :first, :normal)   # => "C:K"
Sashite::Qpi.identifier(:C, :K, :second, :normal)  # => "c:k"

# ✗ Invalid - lowercase symbols rejected with ArgumentError
Sashite::Qpi.identifier(:c, :K, :first, :normal)   # => ArgumentError
Sashite::Qpi.identifier(:C, :k, :first, :normal)   # => ArgumentError

Key principle: Input parameters must use uppercase symbols (:A to :Z). The side parameter determines the display case, not the input case.

Attribute Access

identifier = Sashite::Qpi.parse("S:+R")

# Four fundamental piece attributes
identifier.family                             # => :S
identifier.type                               # => :R
identifier.side                               # => :first
identifier.state                              # => :enhanced

# Component extraction
identifier.to_sin                             # => "S"
identifier.to_pin                             # => "+R"
identifier.sin_component                      # => #<Sashite::Sin::Identifier>
identifier.pin_component                      # => #<Sashite::Pin::Identifier>

Transformations

# All transformations return new immutable instances
identifier = Sashite::Qpi.parse("C:K")

# State transformations
enhanced = identifier.enhance                       # => "C:+K"
diminished = identifier.diminish                    # => "C:-K"
normalized = identifier.normalize                   # => "C:K"

# Attribute transformations
different_type = identifier.with_type(:Q)           # => "C:Q"
different_side = identifier.with_side(:second)      # => "c:k"
different_state = identifier.with_state(:enhanced)  # => "C:+K"
different_family = identifier.with_family(:S)       # => "S:K"

# Player assignment flip
flipped = identifier.flip # => "c:k"

# Chain transformations
result = identifier.flip.enhance.with_type(:Q) # => "c:+q"

State and Comparison Queries

identifier = Sashite::Qpi.parse("S:+P")

# State queries
identifier.normal?                             # => false
identifier.enhanced?                           # => true
identifier.diminished?                         # => false
identifier.first_player?                       # => true
identifier.second_player?                      # => false

# Comparison methods
other = Sashite::Qpi.parse("C:+P")
identifier.same_family?(other)                # => false (S vs C)
identifier.same_type?(other)                  # => true (both P)
identifier.same_side?(other)                  # => true (both first player)
identifier.same_state?(other)                 # => true (both enhanced)
identifier.cross_family?(other)               # => true (different families)

API Reference

Main Module Methods

  • Sashite::Qpi.parse(qpi_string) - Parse QPI string into Identifier object
  • Sashite::Qpi.identifier(family, type, side, state = :normal) - Create identifier from parameters (strict validation)
  • Sashite::Qpi.valid?(qpi_string) - Check if string is valid QPI notation

Identifier Class

Creation and Parsing

  • Sashite::Qpi::Identifier.new(family, type, side, state = :normal) - Create from parameters (strict validation)
  • Sashite::Qpi::Identifier.parse(qpi_string) - Parse QPI string

Parameter Validation

Strict validation enforced:

  • family parameter: Must be symbol :A to :Z (uppercase only)
  • type parameter: Must be symbol :A to :Z (uppercase only)
  • side parameter: Must be :first or :second
  • state parameter: Must be :normal, :enhanced, or :diminished

Attribute Access

  • #family - Get style family (symbol :A to :Z)
  • #type - Get piece type (symbol :A to :Z)
  • #side - Get player side (:first or :second)
  • #state - Get piece state (:normal, :enhanced, or :diminished)
  • #to_s - Convert to QPI string representation

Component Access

  • #to_sin - Get SIN string representation
  • #to_pin - Get PIN string representation
  • #sin_component - Get SIN identifier object
  • #pin_component - Get PIN identifier object

State Queries

  • #normal? - Check if normal state
  • #enhanced? - Check if enhanced state
  • #diminished? - Check if diminished state
  • #first_player? - Check if first player
  • #second_player? - Check if second player

Transformations (immutable - return new instances)

  • #enhance - Create enhanced version
  • #diminish - Create diminished version
  • #normalize - Remove state modifiers
  • #with_type(new_type) - Change piece type
  • #with_side(new_side) - Change player side
  • #with_state(new_state) - Change piece state
  • #with_family(new_family) - Change style family
  • #flip - Switch player assignment for both components

Comparison Methods

  • #same_family?(other) - Check if same style family
  • #same_type?(other) - Check if same piece type
  • #same_side?(other) - Check if same player side
  • #same_state?(other) - Check if same piece state
  • #cross_family?(other) - Check if different style families
  • #==(other) - Full equality comparison

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]|[a-z]:[-+]?[a-z])\z/

Examples

  • C:K - Chess-style king, first player
  • c:k - Chess-style king, second player
  • S:+R - Shogi-style enhanced rook, first player
  • x:-s - Xiangqi-style diminished soldier, second player

Semantic Consistency

QPI enforces semantic consistency: the style and piece components must represent the same player. Both components use case to indicate player assignment, and these must align.

Valid combinations:

Sashite::Qpi.valid?("C:K")    # => true (both first player)
Sashite::Qpi.valid?("c:k")    # => true (both second player)

Invalid combinations:

Sashite::Qpi.valid?("C:k")    # => false (family=first, piece=second)
Sashite::Qpi.valid?("c:K")    # => false (family=second, piece=first)

Parameter Validation

Strict Validation Rules

QPI enforces strict parameter validation consistent with its underlying SIN and PIN primitives:

# ✓ Valid parameter examples
Sashite::Qpi.identifier(:C, :K, :first, :normal)     # All uppercase symbols
Sashite::Qpi.identifier(:S, :R, :second, :enhanced)  # Display case determined by side

# ✗ Invalid parameter examples (raise ArgumentError)
Sashite::Qpi.identifier(:c, :K, :first, :normal)     # Lowercase family rejected
Sashite::Qpi.identifier(:C, :k, :first, :normal)     # Lowercase type rejected
Sashite::Qpi.identifier("C", :K, :first, :normal)    # String family rejected
Sashite::Qpi.identifier(:C, "K", :first, :normal)    # String type rejected

Error Handling

QPI delegates validation to its underlying primitives, ensuring consistent error messages:

begin
  Sashite::Qpi.identifier(:c, :K, :first, :normal)
rescue ArgumentError => e
  # Same error message as Sashite::Sin::Identifier.new(:c, :first)
  puts e.message # => "Family must be a symbol from :A to :Z representing Style Family, got: :c"
end

begin
  Sashite::Qpi.identifier(:C, :k, :first, :normal)
rescue ArgumentError => e
  # Same error message as Sashite::Pin::Identifier.new(:k, :first, :normal)
  puts e.message # => "Type must be a symbol from :A to :Z, got: :k"
end

Design Properties

  • Rule-agnostic: Independent of specific game mechanics
  • Complete identification: All four piece attributes represented
  • Cross-style support: Enables multi-tradition gaming
  • Semantic validation: Ensures component consistency
  • Primitive foundation: Built from SIN and PIN specifications
  • Strict validation: Consistent parameter validation with underlying primitives
  • Immutable: All instances frozen, transformations return new objects
  • Functional: Pure functions with no side effects

Related Specifications

License

Available as open source under the MIT License.

About

Maintained by Sashité — promoting chess variants and sharing the beauty of board game cultures.