# Sashite.Epin
[](https://hex.pm/packages/sashite_epin)
[](https://hexdocs.pm/sashite_epin)
[](https://github.com/sashite/epin.ex/blob/main/LICENSE.md)
> **EPIN** (Extended Piece Identifier Notation) implementation for Elixir.
## What is EPIN?
EPIN (Extended Piece Identifier Notation) extends [PIN](https://sashite.dev/specs/pin/1.0.0/) by adding a **derivation marker** to track piece style in cross-style games.
**EPIN is simply: PIN + optional style derivation marker (`'`)**
This library implements the [EPIN Specification v1.0.0](https://sashite.dev/specs/epin/1.0.0/) with a minimal compositional API.
## Core Concept
```elixir
# EPIN is just PIN + derived flag
pin = Sashite.Pin.parse!("K^")
epin = Sashite.Epin.new(pin)
Sashite.Epin.to_string(epin) # => "K^" (native)
epin.pin # => %Sashite.Pin{} instance
epin.derived # => false
# Mark as derived
derived_epin = Sashite.Epin.mark_derived(epin)
Sashite.Epin.to_string(derived_epin) # => "K^'" (derived from opposite side's style)
```
**That's it.** All piece attributes come from the PIN component.
## Installation
```elixir
def deps do
[
{:sashite_epin, "~> 1.0"}
]
end
```
This will also install `sashite_pin` as a transitive dependency.
## Quick Start
```elixir
# Parse an EPIN string
{:ok, epin} = Sashite.Epin.parse("K^'")
Sashite.Epin.to_string(epin) # => "K^'"
# Access five fundamental attributes through PIN component + derived flag
epin.pin.type # => :K (Piece Name)
epin.pin.side # => :first (Piece Side)
epin.pin.state # => :normal (Piece State)
epin.pin.terminal # => true (Terminal Status)
epin.derived # => true (Piece Style: derived vs native)
# PIN component is a full %Sashite.Pin{} struct
Sashite.Pin.enhanced?(epin.pin) # => false
Sashite.Pin.letter(epin.pin) # => "K"
```
## Basic Usage
### Creating Identifiers
```elixir
# Parse from string
{:ok, epin} = Sashite.Epin.parse("K^") # Native
{:ok, epin} = Sashite.Epin.parse("K^'") # Derived
# Bang version
epin = Sashite.Epin.parse!("K^'")
# Create from PIN component
pin = Sashite.Pin.parse!("K^")
epin = Sashite.Epin.new(pin) # Native (default)
epin = Sashite.Epin.new(pin, derived: true) # Derived
# Validate
Sashite.Epin.valid?("K^") # => true
Sashite.Epin.valid?("K^'") # => true
Sashite.Epin.valid?("K^''") # => false (multiple markers)
Sashite.Epin.valid?("K'^") # => false (wrong order)
```
### Accessing Components
```elixir
epin = Sashite.Epin.parse!("+R^'")
# Get PIN component
epin.pin # => %Sashite.Pin{}
Sashite.Pin.to_string(epin.pin) # => "+R^"
# Check derivation
epin.derived # => true
Sashite.Epin.derived?(epin) # => true
# Serialize
Sashite.Epin.to_string(epin) # => "+R^'"
```
### Five Fundamental Attributes
All attributes accessible via PIN component + derived flag:
```elixir
epin = Sashite.Epin.parse!("+R^'")
# From PIN component (4 attributes)
epin.pin.type # => :R (Piece Name)
epin.pin.side # => :first (Piece Side)
epin.pin.state # => :enhanced (Piece State)
epin.pin.terminal # => true (Terminal Status)
# From EPIN (5th attribute)
epin.derived # => true (Piece Style: native vs derived)
```
## Transformations
All transformations return new immutable structs.
### Change Derivation Status
```elixir
epin = Sashite.Epin.parse!("K^")
# Mark as derived
derived = Sashite.Epin.mark_derived(epin)
Sashite.Epin.to_string(derived) # => "K^'"
# Mark as native
native = Sashite.Epin.unmark_derived(derived)
Sashite.Epin.to_string(native) # => "K^"
# Set explicitly
toggled = Sashite.Epin.with_derived(epin, true)
Sashite.Epin.to_string(toggled) # => "K^'"
```
### Transform via PIN Component
```elixir
epin = Sashite.Epin.parse!("K^'")
# Replace PIN component
new_pin = Sashite.Pin.with_type(epin.pin, :Q)
Sashite.Epin.to_string(Sashite.Epin.with_pin(epin, new_pin)) # => "Q^'"
# Change state
new_pin = Sashite.Pin.with_state(epin.pin, :enhanced)
Sashite.Epin.to_string(Sashite.Epin.with_pin(epin, new_pin)) # => "+K^'"
# Remove terminal marker
new_pin = Sashite.Pin.with_terminal(epin.pin, false)
Sashite.Epin.to_string(Sashite.Epin.with_pin(epin, new_pin)) # => "K'"
# Change side
new_pin = Sashite.Pin.flip(epin.pin)
Sashite.Epin.to_string(Sashite.Epin.with_pin(epin, new_pin)) # => "k^'"
```
### Multiple Transformations
```elixir
epin = Sashite.Epin.parse!("K^")
# Transform PIN and derivation
new_pin = epin.pin
|> Sashite.Pin.with_type(:Q)
|> Sashite.Pin.with_state(:enhanced)
transformed = epin
|> Sashite.Epin.with_pin(new_pin)
|> Sashite.Epin.mark_derived()
Sashite.Epin.to_string(transformed) # => "+Q^'"
```
## Component Queries
Use the PIN module API directly:
```elixir
epin = Sashite.Epin.parse!("+P^'")
# PIN queries (name, side, state, terminal)
epin.pin.type # => :P
epin.pin.side # => :first
epin.pin.state # => :enhanced
epin.pin.terminal # => true
Sashite.Pin.first_player?(epin.pin) # => true
Sashite.Pin.enhanced?(epin.pin) # => true
Sashite.Pin.letter(epin.pin) # => "P"
Sashite.Pin.prefix(epin.pin) # => "+"
Sashite.Pin.suffix(epin.pin) # => "^"
# EPIN queries (style)
Sashite.Epin.derived?(epin) # => true
Sashite.Epin.native?(epin) # => false
# Compare EPINs
other = Sashite.Epin.parse!("+P^")
Sashite.Pin.same_type?(epin.pin, other.pin) # => true (both P)
Sashite.Pin.same_state?(epin.pin, other.pin) # => true (both enhanced)
Sashite.Epin.same_derived?(epin, other) # => false (different derivation)
```
## API Reference
### Main Module
```elixir
# Parse EPIN string
Sashite.Epin.parse(epin_string) # => {:ok, %Sashite.Epin{}} | {:error, reason}
Sashite.Epin.parse!(epin_string) # => %Sashite.Epin{} | raises ArgumentError
# Create from PIN component
Sashite.Epin.new(pin) # => %Sashite.Epin{} (native)
Sashite.Epin.new(pin, derived: true) # => %Sashite.Epin{} (derived)
# Validate string
Sashite.Epin.valid?(epin_string) # => boolean
```
### Core Methods (6 total)
```elixir
# Creation
Sashite.Epin.new(pin, opts \\ []) # Create from PIN + derivation flag
# Component access
epin.pin # => %Sashite.Pin{}
epin.derived # => boolean
# Serialization
Sashite.Epin.to_string(epin) # => "K^'" or "K^"
# PIN replacement
Sashite.Epin.with_pin(epin, new_pin) # New EPIN with different PIN
# Derivation transformation
Sashite.Epin.mark_derived(epin) # Mark as derived (add ')
Sashite.Epin.unmark_derived(epin) # Mark as native (remove ')
Sashite.Epin.with_derived(epin, bool) # Set derivation explicitly
```
### Convenience Queries
```elixir
Sashite.Epin.derived?(epin) # epin.derived == true
Sashite.Epin.native?(epin) # epin.derived == false
Sashite.Epin.same_derived?(e1, e2) # Compare derivation status
```
**That's the entire API.** Everything else uses the PIN module API directly.
## Data Structure
```elixir
%Sashite.Epin{
pin: %Sashite.Pin{}, # Underlying PIN struct
derived: boolean() # Derivation status (default: false)
}
```
## Format Specification
### Structure
```
<pin>[']
```
Where:
- `<pin>` is any valid PIN token
- `'` is the optional derivation marker
### Grammar (EBNF)
```ebnf
epin ::= pin | pin "'"
pin ::= ["+" | "-"] letter ["^"]
letter ::= "A" | ... | "Z" | "a" | ... | "z"
```
### Regular Expression
```elixir
~r/\A[-+]?[A-Za-z]\^?'?\z/
```
## Cross-Style Game Example
In a chess-vs-makruk cross-style match where:
- First side native style = chess
- Second side native style = makruk
```elixir
# First player pieces
chess_king = Sashite.Epin.parse!("K^") # Native Chess king
makruk_pawn = Sashite.Epin.parse!("P'") # Derived Makruk pawn (foreign)
Sashite.Epin.native?(chess_king) # => true (uses own style)
Sashite.Epin.derived?(makruk_pawn) # => true (uses opponent's style)
# Second player pieces
makruk_king = Sashite.Epin.parse!("k^") # Native Makruk king
chess_pawn = Sashite.Epin.parse!("p'") # Derived Chess pawn (foreign)
Sashite.Epin.native?(makruk_king) # => true
Sashite.Epin.derived?(chess_pawn) # => true
```
## Design Principles
### 1. Pure Composition
EPIN doesn't reimplement PIN features — it extends PIN minimally:
```elixir
defstruct [:pin, :derived]
def new(%Sashite.Pin{} = pin, opts \\ []) do
%__MODULE__{
pin: pin,
derived: Keyword.get(opts, :derived, false)
}
end
```
### 2. Minimal API
**6 core methods only:**
1. `new/2` — create from PIN
2. `pin` field — get PIN component
3. `derived` field / `derived?/1` — check derivation
4. `to_string/1` — serialize
5. `with_pin/2` — replace PIN
6. `with_derived/2` / `mark_derived/1` / `unmark_derived/1` — change derivation
Everything else uses the PIN module API directly.
### 3. Component Transparency
Access PIN directly — no wrappers:
```elixir
# Use PIN API directly
epin.pin.type
Sashite.Pin.with_type(epin.pin, :Q)
Sashite.Pin.enhanced?(epin.pin)
Sashite.Pin.flip(epin.pin)
# No need for wrapper functions like:
# Sashite.Epin.type(epin)
# Sashite.Epin.with_type(epin, :Q)
# Sashite.Epin.enhanced?(epin)
# Sashite.Epin.flip(epin)
```
### 4. Backward Compatibility
Every valid PIN is a valid EPIN (without derivation marker):
```elixir
# All PIN identifiers work as EPIN
~w(K +R -p K^ +R^)
|> Enum.each(fn token ->
{:ok, epin} = Sashite.Epin.parse(token)
true = Sashite.Epin.native?(epin)
^token = Sashite.Epin.to_string(epin)
end)
```
## Comparison with PIN
### What EPIN Adds
```elixir
# PIN: 4 attributes
pin = Sashite.Pin.parse!("K^")
pin.type # Piece Name
pin.side # Piece Side
pin.state # Piece State
pin.terminal # Terminal Status
# EPIN: 5 attributes (PIN + style)
epin = Sashite.Epin.parse!("K^'")
epin.pin.type # Piece Name
epin.pin.side # Piece Side
epin.pin.state # Piece State
epin.pin.terminal # Terminal Status
epin.derived # Piece Style (5th attribute)
```
### When to Use EPIN vs PIN
**Use PIN when:**
- Single-style games (both players use same style)
- Style information not needed
- Maximum compatibility required
**Use EPIN when:**
- Cross-style games (different styles per player)
- Pieces can change style (promotion to foreign piece)
- Need to track native vs derived pieces
## Attribute Mapping
EPIN exposes all five fundamental attributes from the Sashité Game Protocol:
| Protocol Attribute | EPIN Access | Example |
|-------------------|-------------|---------|
| **Piece Name** | `epin.pin.type` | `:K` (King), `:R` (Rook) |
| **Piece Side** | `epin.pin.side` | `:first`, `:second` |
| **Piece State** | `epin.pin.state` | `:normal`, `:enhanced`, `:diminished` |
| **Terminal Status** | `epin.pin.terminal` | `true`, `false` |
| **Piece Style** | `epin.derived` | `false` (native), `true` (derived) |
## Related Specifications
- [EPIN Specification v1.0.0](https://sashite.dev/specs/epin/1.0.0/) — Technical specification
- [EPIN Examples](https://sashite.dev/specs/epin/1.0.0/examples/) — Usage examples
- [PIN Specification v1.0.0](https://sashite.dev/specs/pin/1.0.0/) — Base component
- [Sashité Game Protocol](https://sashite.dev/game-protocol/) — Foundation
## License
Available as open source under the [MIT License](https://opensource.org/licenses/MIT).
## About
Maintained by [Sashité](https://sashite.com/) — promoting chess variants and sharing the beauty of board game cultures.