Class: Sashite::Ggn::Ruleset::Source::Destination::Engine

Inherits:
Object
  • Object
show all
Defined in:
lib/sashite/ggn/ruleset/source/destination/engine.rb

Overview

Movement possibility evaluator

Evaluates whether movements are possible based on environmental pre-conditions as defined in the GGN specification v1.0.0.

The Engine acts as the final stage in the GGN navigation chain, determining which movement possibilities from the GGN data structure are valid given the current board state.

Instance Method Summary collapse

Constructor Details

#initialize(*possibilities) ⇒ Engine

Note:

This constructor is typically called internally through the navigation chain: ruleset.select(piece).from(source).to(destination)

Create a new Engine with movement possibilities

Examples:

Structure of a possibility

{
  "must" => { "e3" => "empty", "e4" => "empty" },
  "deny" => { "f3" => "enemy" }
}

Parameters:

  • possibilities (Array<Hash>)

    Array of movement possibility objects from the GGN data structure. Each possibility must contain “must” and “deny” fields with LCN-formatted conditions.



36
37
38
39
40
# File 'lib/sashite/ggn/ruleset/source/destination/engine.rb', line 36

def initialize(*possibilities)
  @possibilities = validate_and_freeze(possibilities)

  freeze
end

Instance Method Details

#where(active_side, squares) ⇒ Array<Hash>

Evaluate which movement possibilities match the current position

Returns the subset of movement possibilities whose pre-conditions are satisfied by the current board state. This is the core evaluation method that determines if a movement is pseudo-legal.

Each possibility is evaluated independently with the following logic:

  • All “must” conditions must be satisfied (AND logic)

  • No “deny” conditions can be satisfied (NOR logic)

The “enemy” keyword in conditions is evaluated from the active player’s perspective, following the LCN specification’s standard interpretation.

Examples:

Chess pawn two-square advance

active_side = :first
squares = {
  "e2" => "C:P",  # White pawn on starting square
  "e3" => nil,    # Path must be clear
  "e4" => nil     # Destination must be empty
}
possibilities = engine.where(active_side, squares)
# => [{"must" => {"e3" => "empty", "e4" => "empty"}, "deny" => {}}]

Capture evaluation with enemy keyword

active_side = :first
squares = {
  "e4" => "C:P",  # White pawn
  "d5" => "c:p"   # Black pawn (enemy from white's perspective)
}
possibilities = engine.where(active_side, squares)
# => [{"must" => {"d5" => "enemy"}, "deny" => {}}]

No matching possibilities (blocked path)

squares = { "e2" => "C:P", "e3" => "c:p", "e4" => nil }
possibilities = engine.where(active_side, squares)
# => []

Parameters:

  • active_side (Symbol)

    Active player side (:first or :second). This determines which pieces are considered “enemy” when evaluating the “enemy” keyword in conditions.

  • squares (Hash{String => String, nil})

    Current board state mapping CELL coordinates to QPI piece identifiers. Use nil for empty squares. Only squares referenced in conditions need to be included.

Returns:

  • (Array<Hash>)

    Subset of movement possibilities that satisfy their pre-conditions. Each returned Hash is the original possibility from the GGN data, containing at minimum “must” and “deny” fields. Returns an empty array if no possibilities match.

Raises:

  • (ArgumentError)

    if active_side is not :first or :second



93
94
95
96
97
98
99
100
# File 'lib/sashite/ggn/ruleset/source/destination/engine.rb', line 93

def where(active_side, squares)
  validate_active_side!(active_side)
  validate_squares!(squares)

  @possibilities.select do |possibility|
    satisfies_conditions?(possibility, active_side, squares)
  end
end