Module: Sashite::Stn

Defined in:
lib/sashite/stn.rb,
lib/sashite/stn/error.rb,
lib/sashite/stn/transition.rb

Defined Under Namespace

Classes: Error, Transition

Constant Summary collapse

EMPTY_TRANSITION =

Canonical, immutable transitions reused across calls.

Transition.new.freeze
PASS_TRANSITION =
Transition.new(toggle: true).freeze

Class Method Summary collapse

Class Method Details

.combine(*transitions) ⇒ Transition

Combine (compose) several transitions or Hash payloads left-to-right. Composition semantics follow STN rules:

- board: last value wins per cell
- hands: deltas are summed; entries summing to zero are removed
- toggle: XOR across the sequence

Examples:

Combine two moves into a cumulative delta

t1 = { "board" => { "e2" => nil, "e4" => "C:P" }, "toggle" => true }
t2 = { "board" => { "e7" => nil, "e5" => "c:p" }, "toggle" => true }
Sashite::Stn.combine(t1, t2).to_h
# => { :board=>{"e2"=>nil,"e4"=>"C:P","e7"=>nil,"e5"=>"c:p"} }

Mixed inputs (Hash and Transition)

t1 = Sashite::Stn.transition(board: { "e1" => nil, "g1" => "C:K", "h1" => nil, "f1" => "C:R" }, toggle: true)
t2 = { "hands" => { "c:b" => 1 }, "toggle" => true }
combo = Sashite::Stn.combine(t1, t2)
combo.toggle? # => false (true XOR true)

Parameters:

Returns:



147
148
149
150
# File 'lib/sashite/stn.rb', line 147

def combine(*transitions)
  parsed = transitions.flatten.compact.map { |t| parse(t) }
  parsed.reduce(EMPTY_TRANSITION) { |acc, t| acc.combine(t) }
end

.compose(*transitions) ⇒ Object

Friendly alias for combine.

See Also:

  • #combine


155
156
157
# File 'lib/sashite/stn.rb', line 155

def compose(*transitions)
  combine(*transitions)
end

.emptyTransition

Return the canonical empty transition: no board changes, no hand changes, no toggle. This instance is immutable and safe to reuse.

Examples:

Sashite::Stn.empty.empty?   # => true
Sashite::Stn.empty.toggle?  # => false

Returns:



111
112
113
# File 'lib/sashite/stn.rb', line 111

def empty
  EMPTY_TRANSITION
end

.parse(data) ⇒ Transition

Parse an STN payload (Hash) into a Transition, or return the same Transition if one is passed. Raises on invalid input.

Top-level keys may be symbols or strings and are normalized.

Examples:

Parse from Hash

tr = Sashite::Stn.parse({ "board" => { "e2" => nil, "e4" => "C:P" }, "toggle" => true })
tr.toggle?        # => true
tr.board_changes  # => { "e2" => nil, "e4" => "C:P" }

Passing a Transition returns it unchanged

tr = Sashite::Stn.transition(toggle: true)
Sashite::Stn.parse(tr).equal?(tr)  # => true

Parameters:

Returns:

Raises:



66
67
68
69
70
71
72
73
74
75
76
# File 'lib/sashite/stn.rb', line 66

def parse(data)
  case data
  when Transition
    data
  when ::Hash
    Transition.parse(_normalize_root(data))
  else
    raise Error::Validation,
          "STN must be provided as a Hash or a Transition, got: #{data.class}"
  end
end

.passTransition

Return the canonical pass transition: toggle only, no board/hands changes. This instance is immutable and safe to reuse.

Examples:

Sashite::Stn.pass.pass_move?  # => true
Sashite::Stn.pass.to_h        # => { :toggle=>true }

Returns:



123
124
125
# File 'lib/sashite/stn.rb', line 123

def pass
  PASS_TRANSITION
end

.transition(board: {}, hands: {}, toggle: false) ⇒ Transition

Construct a Transition directly from keyword arguments. Inputs are not mutated; keys are normalized to strings.

Examples:

Build a standard move (e2->e4) with toggle

tr = Sashite::Stn.transition(board: { "e2" => nil, "e4" => "C:P" }, toggle: true)
tr.to_h
# => { :board=>{"e2"=>nil,"e4"=>"C:P"}, :toggle=>true }

Drop from hand (hand-only + board change)

tr = Sashite::Stn.transition(
       board: { "e5" => "S:P" },
       hands: { "S:P" => -1 },
       toggle: true
     )

Parameters:

  • board (Hash{String,Symbol=>String,nil}) (defaults to: {})

    CELL -> (QPI or nil)

  • hands (Hash{String,Symbol=>Integer}) (defaults to: {})

    QPI -> delta (non-zero)

  • toggle (Boolean) (defaults to: false)

    switch active player if true

Returns:



97
98
99
100
101
# File 'lib/sashite/stn.rb', line 97

def transition(board: {}, hands: {}, toggle: false)
  board_norm = _stringify_map(board)
  hands_norm = _stringify_map(hands)
  Transition.new(board: board_norm, hands: hands_norm, toggle: !!toggle)
end

.valid?(data) ⇒ Boolean

Validate an STN payload (Hash) or a Transition instance.

The validation is strict and delegated to Sashite::Stn::Transition.parse. It accepts top-level keys as strings (“board”, “hands”, “toggle”) or symbols (:board, :hands, :toggle). Nested keys are normalized to strings.

Examples:

Hash – board-only change with turn toggle

Sashite::Stn.valid?({ "board" => { "e2" => nil, "e4" => "C:P" }, "toggle" => true })
# => true

Hash – invalid CELL coordinate

Sashite::Stn.valid?({ "board" => { "a0" => "C:P" } })
# => false

Transition – already parsed

tr = Sashite::Stn.transition(board: { "e2" => nil, "e4" => "C:P" }, toggle: true)
Sashite::Stn.valid?(tr)  # => true

Parameters:

Returns:

  • (Boolean)

    true if valid, false otherwise



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/sashite/stn.rb', line 33

def valid?(data)
  case data
  when Transition
    true
  when ::Hash
    begin
      Transition.parse(_normalize_root(data))
      true
    rescue Error
      false
    end
  else
    false
  end
end