API Reference

Pieces Module

Unit Type System

class pykrieg.pieces.Unit(owner: str)[source]

Base class for all unit types.

This class provides the foundation for all unit types in the game. Each unit type has specific combat statistics (attack, defense, movement, range) and belongs to a player (NORTH or SOUTH).

__init__(owner: str)[source]

Initialize a unit with an owner.

Parameters:

owner – The player who owns this unit (‘NORTH’ or ‘SOUTH’)

property owner: str

Return unit owner (NORTH or SOUTH).

property unit_type: str

Return unit type string.

property attack: int

Return attack value.

property defense: int

Return defense value.

property movement: int

Return movement range in squares.

property range: int | None

Return attack range in squares, or None for structures.

is_combat_unit() bool[source]

Check if this is a combat unit (can attack).

Returns:

True if the unit has an attack value > 0

is_structure() bool[source]

Check if this is a structure (cannot move).

Returns:

True if the unit has movement = 0

get_effective_attack(board: Board) int[source]

Get effective attack value, considering online/offline status.

Parameters:

board – The Board object to check online status

Returns:

Effective attack value (0 if offline and not a relay)

get_effective_defense(board: Board) int[source]

Get effective defense value, considering online/offline status.

Parameters:

board – The Board object to check online status

Returns:

Effective defense value (0 if offline and not a relay)

get_effective_range(board: Board) int | None[source]

Get effective range value, considering online/offline status.

Parameters:

board – The Board object to check online status

Returns:

Effective range value (0 if offline and not a relay, None for structures)

get_effective_movement(board: Board) int[source]

Get effective movement value, considering online/offline status.

Parameters:

board – The Board object to check online status

Returns:

Effective movement value (0 if offline and not a relay)

class pykrieg.pieces.Infantry(owner: str)[source]

Infantry unit: attack=4, defense=6, movement=1, range=2

unit_type = 'INFANTRY'
attack = 4
defense = 6
movement = 1
range = 2
class pykrieg.pieces.Cavalry(owner: str)[source]

Cavalry unit: attack=4, defense=5, movement=2, range=2

unit_type = 'CAVALRY'
attack = 4
defense = 5
movement = 2
range = 2
class pykrieg.pieces.Cannon(owner: str)[source]

Cannon unit: attack=5, defense=8, movement=1, range=3

unit_type = 'CANNON'
attack = 5
defense = 8
movement = 1
range = 3
class pykrieg.pieces.Relay(owner: str)[source]

Relay unit: attack=0, defense=1, movement=1, range=0

unit_type = 'RELAY'
attack = 0
defense = 1
movement = 1
range = 0
class pykrieg.pieces.SwiftCannon(owner: str)[source]

Swift Cannon unit: attack=5, defense=8, movement=2, range=3

unit_type = 'SWIFT_CANNON'
attack = 5
defense = 8
movement = 2
range = 3
class pykrieg.pieces.SwiftRelay(owner: str)[source]

Swift Relay unit: attack=0, defense=1, movement=2, range=0

unit_type = 'SWIFT_RELAY'
attack = 0
defense = 1
movement = 2
range = 0

Factory Function

pykrieg.pieces.create_piece(unit_type: str, owner: str) Unit[source]

Factory function to create unit instances from type strings.

This is the recommended way to create units, as it validates unit_type and owner parameters.

Parameters:
  • unit_type – String representing unit type

  • owner – ‘NORTH’ or ‘SOUTH’

Returns:

Unit subclass instance

Raises:

ValueError – If unit_type or owner is invalid

Note

Arsenal is no longer a unit type. Arsenals are now terrain structures. Use Board.set_arsenal() to place an arsenal.

Examples

>>> unit = create_piece("INFANTRY", "NORTH")
>>> unit.unit_type
'INFANTRY'
>>> unit.owner
'NORTH'

Board Module

Board class for Pykrieg - representing 20x25 game board.

This module implements Board class with territory divisions, coordinate validation, piece management, and Lines of Communication (LOC) network system.

class pykrieg.board.Board(enable_adjacency_relay_propagation: bool = True)[source]

Bases: object

Represents 20x25 game board with territory divisions.

The board has: - 20 rows (0-19) - 25 columns (0-24) - Total 500 squares - Territory division at row 10 (North: 0-9, South: 10-19)

TERRITORY_BOUNDARY = 10
__init__(enable_adjacency_relay_propagation: bool = True) None[source]

Initialize empty board with territory boundaries.

Parameters:

enable_adjacency_relay_propagation – When True (default), relays activated by proximity to an online unit propagate their LOC rays. When False, relays only propagate via arsenal rays and other relay rays (original Debord behavior).

property rows: int

Return number of rows.

property cols: int

Return number of columns.

property undo_redo_manager: UndoRedoManager

Return the undo/redo manager.

get_kfen_metadata() Dict[str, object] | None[source]

Get KFEN metadata for this board.

Returns:

Dictionary with KFEN metadata or None if not set

Note

This is used when saving games to include metadata like game name, player names, event, etc.

set_kfen_metadata(metadata: Dict[str, object]) None[source]

Set KFEN metadata for this board.

Parameters:

metadata – Dictionary with KFEN metadata fields

Example

>>> board.set_kfen_metadata({
...     "game_name": "Tournament Final",
...     "players": {"north": "Alice", "south": "Bob"},
...     "event": "World Championship 2026"
... })
property turn: str

Return current player.

turn_side() str[source]

Return current turn side (NORTH/SOUTH).

property territory_boundary: int

Return row number that separates territories.

is_valid_square(row: int, col: int) bool[source]

Check if coordinates are within board bounds.

get_piece(row: int, col: int) object | None[source]

Get piece at given coordinates.

Deprecated since version 0.1.2: Use get_unit() instead. Will be removed in version 0.3.0.

set_piece(row: int, col: int, piece: object) None[source]

Set piece at given coordinates.

Deprecated since version 0.1.2: Use place_unit() instead. Will be removed in version 0.3.0.

clear_square(row: int, col: int) None[source]

Remove piece from square.

place_unit(row: int, col: int, unit: object) None[source]

Place a Unit object on the board.

Parameters:
  • row – Row coordinate (0-19)

  • col – Column coordinate (0-24)

  • unit – Unit object to place

Raises:

ValueError – If coordinates are invalid

create_and_place_unit(row: int, col: int, unit_type: str, owner: str) object[source]

Create and place a unit on board in one step.

Parameters:
  • row – Row coordinate (0-19)

  • col – Column coordinate (0-24)

  • unit_type – Unit type string

  • owner – ‘NORTH’ or ‘SOUTH’

Returns:

The created Unit object

Raises:

ValueError – If coordinates, unit_type, or owner are invalid

get_unit(row: int, col: int) object | None[source]

Get Unit object at given coordinates.

Parameters:
  • row – Row coordinate (0-19)

  • col – Column coordinate (0-24)

Returns:

Unit object or None if square is empty

Raises:

ValueError – If coordinates are invalid

get_unit_type(row: int, col: int) str | None[source]

Get unit type string at given coordinates.

Returns unit type string or None if square is empty.

get_unit_owner(row: int, col: int) str | None[source]

Get unit owner at given coordinates.

Returns ‘NORTH’, ‘SOUTH’, or None if square is empty.

count_units(unit_type: str | None = None, owner: str | None = None) int[source]

Count units on the board with optional filters.

Parameters:
  • unit_type – Unit type to count, or None for all types

  • owner – Owner to count, or None for all owners

Returns:

Number of matching units

get_units_by_type(unit_type: str) List[Tuple[int, int]][source]

Get all coordinates containing a specific unit type.

Parameters:

unit_type – Unit type string

Returns:

List of (row, col) tuples containing unit type

get_units_by_owner(owner: str) List[Tuple[int, int]][source]

Get all coordinates containing units owned by a player.

Parameters:

owner – ‘NORTH’ or ‘SOUTH’

Returns:

List of (row, col) tuples containing player’s units

get_all_units() Dict[Tuple[int, int], object][source]

Get all units on the board.

Returns:

Dictionary mapping (row, col) tuples to Unit objects

is_valid_unit_type(unit_type: str) bool[source]

Check if unit type is valid.

Returns True if unit_type is in ALL_UNIT_TYPES.

is_valid_owner(owner: str) bool[source]

Check if owner is valid.

Returns True if owner is NORTH or SOUTH.

get_territory(row: int, col: int) str[source]

Determine which territory a square belongs to.

Returns:

‘NORTH’ if row < TERRITORY_BOUNDARY ‘SOUTH’ if row >= TERRITORY_BOUNDARY

is_north_territory(row: int, col: int) bool[source]

Check if square is in North territory.

is_south_territory(row: int, col: int) bool[source]

Check if square is in South territory.

get_territory_squares(territory: str) List[Tuple[int, int]][source]

Get all squares belonging to a territory.

Parameters:

territory – ‘NORTH’ or ‘SOUTH’

Returns:

List of (row, col) tuples

static spreadsheet_to_tuple(coord: str) Tuple[int, int][source]

Convert spreadsheet-style coordinate to internal (row, col) tuple.

Parameters:

coord – String in spreadsheet format (e.g., “1A”, “10AA”, “25Y”)

Returns:

  • row: 0-based from top

  • col: 0-based from left

Return type:

Tuple (row, col) where

Note: Coordinate origin is top-left (1A = top-left corner)

Format: NUMBERS (column) + LETTERS (row) Debord’s original convention: columns as numbers, rows as letters

Example

“1A” -> (0, 0) (top-left corner) “25T” -> (19, 24) (bottom-right on 25-col board) “10AA” -> (26, 9) (27th column, 10th row) “27A” -> (0, 26) (top row, 28th column)

static tuple_to_spreadsheet(row: int, col: int) str[source]

Convert internal (row, col) tuple to spreadsheet-style coordinate.

Parameters:
  • row – Row number (0-19, 0-based from top)

  • col – Column number (0-24, 0-based from left)

Returns:

String in spreadsheet format (e.g., “1A”, “10AA”, “25Y”)

Note: Coordinate origin is top-left (1A = top-left corner)

Format: NUMBERS (column) + LETTERS (row) Debord’s original convention: columns as numbers, rows as letters

Example

(0, 0) -> “1A” (top-left corner) (19, 24) -> “25T” (bottom-right on 25-col board) (9, 26) -> “27J” (10th row, 27th column) (0, 27) -> “28A” (top row, 28th column)

static tuple_to_index(row: int, col: int, board_cols: int = 25) int[source]

Convert row, col to square index (row-major order).

Parameters:
  • row – Row number (0-19)

  • col – Column number (0-24)

  • board_cols – Number of columns (default 25)

Returns:

Integer index (0-499)

Example

(0, 0) -> 0 (0, 1) -> 1 (1, 0) -> 25

static index_to_tuple(index: int, board_cols: int = 25, board_rows: int = 20) Tuple[int, int][source]

Convert square index to row, col tuple.

Parameters:
  • index – Integer index (0-499)

  • board_cols – Number of columns (default 25)

  • board_rows – Number of rows (default 20)

Returns:

Tuple (row, col)

Example

0 -> (0, 0) 1 -> (0, 1) 25 -> (1, 0)

Get all legal moves for unit at given position.

Convenience method that wraps movement.generate_moves.

Parameters:
  • row – Row of unit (0-19)

  • col – Column of unit (0-24)

Returns:

List of (to_row, to_col) tuples

Raises:

ValueError – If no unit at position

Check if a move is legal.

Convenience method that wraps movement.is_valid_move.

Parameters:
  • from_row – Source row (0-19)

  • from_col – Source column (0-24)

  • to_row – Target row (0-19)

  • to_col – Target column (0-24)

Returns:

True if move is legal, False otherwise

make_move(from_row: int, from_col: int, to_row: int, to_col: int) object[source]

Make a move on the board.

Convenience method that wraps movement.execute_move.

Parameters:
  • from_row – Source row (0-19)

  • from_col – Source column (0-24)

  • to_row – Target row (0-19)

  • to_col – Target column (0-24)

Returns:

The Unit object that was moved

Raises:

ValueError – If move is invalid

calculate_combat(target_row: int, target_col: int, attacker: str, defender: str) Dict[str, object][source]

Calculate complete combat scenario.

Convenience method that wraps combat.calculate_combat.

Parameters:
  • target_row – Target row (0-19)

  • target_col – Target column (0-24)

  • attacker – ‘NORTH’ or ‘SOUTH’

  • defender – ‘NORTH’ or ‘SOUTH’

Returns:

Dictionary with combat results

execute_capture(target_row: int, target_col: int) object[source]

Execute a capture (remove target unit from board).

Convenience method that wraps combat.execute_capture.

Parameters:
  • target_row – Target row (0-19)

  • target_col – Target column (0-24)

Returns:

The captured Unit object

add_pending_retreat(row: int, col: int) None[source]

Add unit to pending retreats.

This is called when combat resolves to RETREAT outcome. The actual retreat execution happens at the start of the defender’s next turn (enforced by turn management in 0.1.4).

Note: Retreats are tracked in-memory and not persisted in KFEN format. They are resolved each turn by turn management system.

Parameters:
  • row – Row of unit that must retreat

  • col – Column of unit that must retreat

Raises:

ValueError – If no unit at position

get_pending_retreats() List[Tuple[int, int]][source]

Get all pending retreats.

Returns:

List of (row, col) tuples for units that must retreat

clear_pending_retreats() None[source]

Clear all pending retreats after resolution.

This should be called after all retreats have been processed at the start of a turn.

has_pending_retreat(row: int, col: int) bool[source]

Check if unit at position must retreat.

Parameters:
  • row – Row of unit to check

  • col – Column of unit to check

Returns:

True if unit at (row, col) must retreat, False otherwise

get_units_must_retreat() List[Tuple[int, int]][source]

Get all units that must retreat this turn.

Returns:

List of (row, col) tuples for units that must retreat before making other moves

is_unit_in_retreat(row: int, col: int) bool[source]

Check if unit is currently in retreat mode.

Parameters:
  • row – Row of unit to check

  • col – Column of unit to check

Returns:

True if unit at (row, col) is in retreat mode, False otherwise

get_valid_retreat_positions(row: int, col: int) List[Tuple[int, int]][source]

Get valid retreat positions for a unit.

Parameters:
  • row – Row of unit to check

  • col – Column of unit to check

Returns:

List of (to_row, to_col) tuples representing valid retreat destinations

Note

This uses same movement rules as normal moves (terrain-independent in 0.1.4)

has_moved_this_turn(row: int, col: int) bool[source]

Check if a move originated from this position this turn.

Parameters:
  • row – Row of position to check

  • col – Column of position to check

Returns:

True if a unit moved FROM this position this turn, False otherwise

get_moves_this_turn() int[source]

Get number of units moved this turn.

Returns:

Number of units moved (0-5)

can_move_more() bool[source]

Check if player can move more units this turn.

Returns:

True if fewer than 5 units have been moved, False otherwise

get_attacks_this_turn() int[source]

Get number of attacks made this turn.

Returns:

Number of attacks made (0 or 1)

get_attack_target() Tuple[int, int] | None[source]

Get attack target square for current turn.

Returns:

Tuple (row, col) of target, or None if no attack/pass yet

can_attack_more() bool[source]

Check if player can attack more this turn.

Returns:

True if 0 attacks have been made, False otherwise

validate_move(from_row: int, from_col: int, to_row: int, to_col: int) bool[source]

Validate a move according to turn rules.

Checks: 1. Game is not over 2. It’s the movement phase 3. The unit belongs to the current player 4. The unit hasn’t moved yet this turn 5. The player hasn’t moved 5 units yet 6. If retreats are pending, only allow retreat moves 7. The move is legally valid (pseudo-legal check)

Parameters:
  • from_row – Source row (0-19)

  • from_col – Source column (0-24)

  • to_row – Target row (0-19)

  • to_col – Target column (0-24)

Returns:

True if move is valid according to turn rules, False otherwise

make_turn_move(from_row: int, from_col: int, to_row: int, to_col: int) Tuple[object, bool][source]

Make a move with turn validation and tracking.

Modified for 0.2.2: Check for enemy arsenal destruction.

This method: 1. Validates the move according to turn rules 2. Executes the move 3. Checks if destination is enemy arsenal and destroys it 4. Tracks that the unit has moved this turn 5. Clears the retreat flag if this was a retreat move

Parameters:
  • from_row – Source row (0-19)

  • from_col – Source column (0-24)

  • to_row – Target row (0-19)

  • to_col – Target column (0-24)

Returns:

Tuple of (unit that moved, arsenal_destroyed flag)

Raises:

ValueError – If move is invalid according to turn rules

validate_attack(target_row: int, target_col: int) bool[source]

Validate an attack according to turn rules.

Checks: 1. Game is not over 2. It’s the battle phase 3. The current player hasn’t attacked yet 4. There’s at least one attacking unit 5. No units must retreat (retreat enforcement)

Parameters:
  • target_row – Target row (0-19)

  • target_col – Target column (0-24)

Returns:

True if attack is valid according to turn rules, False otherwise

make_turn_attack(target_row: int, target_col: int) Dict[str, object][source]

Make an attack with turn validation and tracking.

This method: 1. Validates the attack according to turn rules 2. Calculates the combat result 3. Executes capture if applicable 4. Marks defender for retreat if applicable 5. Tracks that an attack has been made

Parameters:
  • target_row – Target row (0-19)

  • target_col – Target column (0-24)

Returns:

Dictionary with combat results

Raises:

ValueError – If attack is invalid according to turn rules

pass_attack() None[source]

Pass attack phase.

This method: 1. Validates it’s battle phase 2. Marks that an attack has been made (pass counts) 3. Records pass state for KFEN serialization (attack_target = None)

Raises:

ValueError – If not in battle phase or already attacked

switch_to_battle_phase() None[source]

Switch from movement phase to battle phase.

Raises:

ValueError – If not in movement phase or units must retreat

resolve_retreats() List[Tuple[int, int, object, str]][source]

Resolve pending retreats at the start of a turn.

This method checks for pending retreats and enforces retreat rules: 1. For each unit that must retreat, find valid retreat squares 2. If valid retreat exists, mark unit as “must retreat” (player must choose destination) 3. If no valid retreat exists, capture (destroy) the unit 4. Clear pending retreats after resolution

Returns:

  • row, col: Position where unit was captured

  • unit: The captured unit object

  • reason: String explaining why unit was captured (e.g., “no valid retreat”)

Return type:

List of tuples (row, col, unit, reason) for captured units

Note

  • This is called at the start of the defender’s turn

  • Retreating units must move before other units can move

  • Units in retreat are tracked in _units_must_retreat

end_turn() List[Tuple[int, int, object, str]][source]

End current turn and switch to next player.

This method: 1. Clears moved unit tracking 2. Clears attack counter 3. Switches to other player 4. Resets phase to movement 5. Increments turn number 6. Resolves any pending retreats for new player 7. Recalculates networks to ensure victory checks have current state 8. Checks victory conditions (new for 0.2.2)

Returns:

List of tuples (row, col, unit, reason) for captured units during retreat resolution

Note

  • Retreat resolution happens at the start of the new player’s turn

  • Turn number increments on each player switch

  • Retreat state (_units_must_retreat) is NOT cleared automatically

  • It persists until the NEXT end_turn() call

  • This allows retreat state to persist during a player’s turn

reset_turn_state() None[source]

Reset turn state without changing turn number or player.

This method is useful for: - Undo functionality - Testing scenarios - Loading from FEN

Resets: - Moved units tracking - Attack counter - Current phase (to movement)

Does NOT reset: - Turn number - Current player - Pending retreats - Board position

property turn_number: int

Return current turn number.

property current_phase: str

Return current turn phase.

increment_turn() None[source]

Increment turn number and switch player.

can_undo() bool[source]

Check if undo is available.

Returns:

True if undo is available, False otherwise

can_redo() bool[source]

Check if redo is available.

Returns:

True if redo is available, False otherwise

undo(count: int = 1) None[source]

Undo one or more actions.

Parameters:

count – Number of actions to undo (default: 1)

Raises:

ValueError – If count exceeds available actions

redo(count: int = 1) None[source]

Redo one or more actions.

Parameters:

count – Number of actions to redo (default: 1)

Raises:

ValueError – If count exceeds available actions

set_max_undo_history(max_size: int) None[source]

Set maximum undo history size.

Parameters:

max_size – Maximum number of actions to keep. Set to 0 for unlimited.

set_terrain(row: int, col: int, terrain: str | None) None[source]

Set terrain type for a square.

Parameters:
  • row – Row coordinate (0-19)

  • col – Column coordinate (0-24)

  • terrain – Terrain type (None, ‘MOUNTAIN’, ‘MOUNTAIN_PASS’, ‘FORTRESS’, ‘ARSENAL’)

Raises:

ValueError – If coordinates are invalid or terrain type is invalid

get_terrain(row: int, col: int) str | None[source]

Get terrain type for a square.

Parameters:
  • row – Row coordinate (0-19)

  • col – Column coordinate (0-24)

Returns:

Terrain type or None if no terrain

Raises:

ValueError – If coordinates are invalid

set_arsenal(row: int, col: int, owner: str) None[source]

Set arsenal terrain with owner.

Parameters:
  • row – Row coordinate (0-19)

  • col – Column coordinate (0-24)

  • owner – ‘NORTH’ or ‘SOUTH’

Raises:

ValueError – If coordinates are invalid or owner is invalid

get_arsenal_owner(row: int, col: int) str | None[source]

Get owner of arsenal at given coordinates.

Parameters:
  • row – Row coordinate (0-19)

  • col – Column coordinate (0-24)

Returns:

‘NORTH’, ‘SOUTH’, or None if no arsenal

Raises:

ValueError – If coordinates are invalid

remove_arsenal(row: int, col: int) None[source]

Remove arsenal from square.

Parameters:
  • row – Row coordinate (0-19)

  • col – Column coordinate (0-24)

Raises:

ValueError – If coordinates are invalid

calculate_network(player: str, enable_step4: bool = True) None[source]

Calculate lines of communication network for specified player.

This must be called after any board state change.

Parameters:
  • player – The player to calculate network for (‘NORTH’ or ‘SOUTH’)

  • enable_step4 – Whether to enable step 4 (ray propagation from proximity-activated relays)

set_adjacency_relay_propagation(enable: bool) None[source]

Enable/disable adjacency-based relay propagation (Step 4).

When enabled (default): Relays activated by proximity to an online unit will propagate their own LOC rays in all 8 directions.

When disabled: Relays only propagate LOC rays when activated via arsenal rays or other relay rays (original Debord behavior).

Parameters:

enable – True to enable, False to disable

get_adjacency_relay_propagation() bool[source]

Check if adjacency-based relay propagation is enabled.

Returns:

True if Step 4 is enabled, False otherwise

enable_networks() None[source]

Enable network rules for both players.

This method activates Lines of Communication system, after which units must be connected to arsenals through to network to function.

Once enabled, network system enforces: - Units not connected to network have 0 attack/defense/range (except relays) - Units not connected to network have 0 movement (except relays/swift relays)

Note: This should be called explicitly when network rules are desired. The default behavior is that networks are disabled (all units online).

The optional Step 4 (adjacency-based relay propagation) is controlled by Board constructor’s enable_adjacency_relay_propagation parameter.

Example

>>> board = Board()  # Step 4 enabled (default)
>>> board.create_and_place_unit(5, 10, 'ARSENAL', 'NORTH')
>>> board.create_and_place_unit(5, 12, 'INFANTRY', 'NORTH')
>>> # Enable network rules
>>> board.enable_networks()
>>> board = Board(enable_adjacency_relay_propagation=False)  # Step 4 disabled
is_unit_online(row: int, col: int, player: str) bool[source]

Check if a square is covered by network for a player.

This checks if the square is within network coverage, which includes both units and empty squares covered by rays.

Parameters:
  • row – Row coordinate (0-19)

  • col – Column coordinate (0-24)

  • player – ‘NORTH’ or ‘SOUTH’

Returns:

True if square is covered by network, False otherwise

Note

If network has never been calculated (_network_calculated == False), returns True for all squares (optimistic default). This ensures backward compatibility and allows normal movement before network system is invoked.

is_relay_online(row: int, col: int) bool[source]

Check if a relay/swift relay is online.

Parameters:
  • row – Row coordinate (0-19)

  • col – Column coordinate (0-24)

Returns:

True if relay is online, False otherwise

Note

If network has never been calculated (_network_calculated == False), returns True for all relays (optimistic default). This ensures backward compatibility and allows normal movement before network system is invoked.

get_online_units(player: str) Set[Tuple[int, int]][source]

Get all online units for a player.

Parameters:

player – ‘NORTH’ or ‘SOUTH’

Returns:

Set of (row, col) tuples

Note

If network has never been calculated (_network_calculated == False), returns all units for the player (optimistic default). This ensures backward compatibility and allows normal movement before network system is invoked.

get_offline_units(player: str) Set[Tuple[int, int]][source]

Get all offline units for a player.

Parameters:

player – ‘NORTH’ or ‘SOUTH’

Returns:

Set of (row, col) tuples

Note

If network has never been calculated (_network_calculated == False), returns empty set (optimistic default - no units are offline).

get_network_active_relays(player: str) Set[Tuple[int, int]][source]

Get all relays/swift relays that are online and can propagate.

Parameters:

player – ‘NORTH’ or ‘SOUTH’

Returns:

Set of (row, col) tuples

is_ray_covered(row: int, col: int, player: str) bool[source]

Check if a square is covered by arsenal/relay rays only (for display).

This method is used by display code to determine which squares should be shown as green (online via rays) vs grey (online only via proximity).

Parameters:
  • row – Row coordinate (0-19)

  • col – Column coordinate (0-24)

  • player – ‘NORTH’ or ‘SOUTH’

Returns:

True if square is covered by arsenal/relay rays, False otherwise

Note

  • This is purely for visual representation purposes

  • Does not affect game mechanics (movement, combat, etc.)

  • Use is_unit_online() for game mechanic queries

  • Returns True for squares covered by ray propagation (steps 1 and 2)

  • Returns False for squares only covered by proximity propagation (step 3)

Example

>>> board = Board()
>>> board.set_arsenal(10, 10, 'NORTH')
>>> board.enable_networks()
>>> # Square covered by arsenal ray will be ray_covered
>>> board.is_ray_covered(10, 15, 'NORTH')  # True
>>> # Square covered only by proximity won't be ray_covered
>>> board.is_ray_covered(11, 14, 'NORTH')  # False
>>> # But it's still online for game mechanics
>>> board.is_unit_online(11, 14, 'NORTH')  # True
property game_state: str

Return current game state.

Returns:

‘ONGOING’, ‘NORTH_WINS’, ‘SOUTH_WINS’, or ‘DRAW’

property victory_result: Dict[str, object] | None

Return victory result if game has ended, None otherwise.

Returns:

Dictionary with victory details or None if game is ongoing. Keys: ‘game_state’, ‘winner’, ‘victory_condition’, ‘details’

is_game_over() bool[source]

Check if game has ended.

Returns:

True if game is over (winner determined or draw), False otherwise

check_victory() Dict[str, object][source]

Check victory conditions and update game state.

This method should be called after each turn to detect victory conditions.

Returns:

  • ‘game_state’: Current game state

  • ’winner’: Winning player or None

  • ’victory_condition’: Specific condition met or None

  • ’details’: Human-readable explanation

Return type:

Dictionary with victory details

handle_surrender(player: str) None[source]

Handle a player’s surrender.

This method sets the game state to indicate the surrendering player has lost.

Parameters:

player – ‘NORTH’ or ‘SOUTH’ - the player who is surrendering

Raises:

ValueError – If game is already over or player is invalid

destroy_arsenal(row: int, col: int) None[source]

Destroy an arsenal at the specified position.

This is called when a unit moves onto an enemy arsenal. The arsenal is removed from the board and the terrain becomes empty.

Parameters:
  • row – Row coordinate (0-19)

  • col – Column coordinate (0-24)

Raises:

ValueError – If position doesn’t contain an arsenal

FEN Module

FEN (Forsyth-Edwards Notation) for Pykrieg board serialization.

This module implements FEN serialization/deserialization for the 0.1.0 version of Pykrieg, supporting basic board state representation.

class pykrieg.fen.Fen[source]

Bases: object

FEN (Forsyth-Edwards Notation) for Pykrieg board serialization.

This is 0.1.0 basic implementation supporting: - Board serialization/deserialization - Basic piece representation - Turn tracking

FEN Format (0.1.4 - With Turn State): <board_data>/<turn>/<phase>/<actions>/<turn_number>/<retreats>

Where: - board_data: Row-by-row representation of pieces (20 rows separated by ‘/’) - turn: Current player (‘N’ or ‘S’) - phase: Turn phase (‘M’ for movement, ‘B’ for battle) - actions: Move pairs or attack target (empty list in 0.1.4) - turn_number: Current turn number (1, 2, 3, …) - retreats: List of retreat positions [row1,col1,row2,col2,…]

Note: Retreats are tracked in-memory and may be lost on FEN serialization.

Board Data Format: - Each row separated by ‘/’ - Pieces: unit_type (e.g., ‘I’, ‘C’, ‘K’, ‘A’, ‘R’, ‘W’, ‘X’) - Uppercase: North pieces - Lowercase: South pieces - Empty squares: ‘_’

Example FEN: ___________________________/…/_________________________/N/M/[]

PIECE_SYMBOLS = {'CANNON': 'K', 'CAVALRY': 'C', 'INFANTRY': 'I', 'RELAY': 'R', 'SWIFT_CANNON': 'W', 'SWIFT_RELAY': 'X'}
SYMBOL_TO_PIECE = {'C': 'CAVALRY', 'I': 'INFANTRY', 'K': 'CANNON', 'R': 'RELAY', 'W': 'SWIFT_CANNON', 'X': 'SWIFT_RELAY'}
static board_to_fen(board: Board, include_turn_state: bool = True) str[source]

Convert Board object to FEN string (0.2.1 with terrain).

Parameters:
  • board – Board object

  • include_turn_state – If False, omit turn/phase/turn_number/retreats (for KFEN embedding)

Returns:

FEN string representation

Note

Uses bracket notation for terrain: (unit) on pass, [unit] in fortress Empty terrain: p (pass), f (fortress), m (mountain)

Example

Empty board: “_________________________/…/N/M/[]/1/[]” With terrain: “_____________________(I)______________/…/N/M/[]/1/[]”

static fen_to_board(fen_string: str) Board[source]

Convert FEN string to Board object (0.2.1 with terrain).

Supports backward compatibility: - 0.1.4: 25 parts (no terrain) - 0.2.1: 25 parts (terrain with bracket notation) - Board-only: 20 parts (used in KFEN board_info section)

Parameters:

fen_string – FEN string

Returns:

Board object

Example

“_________________________/…/N/M/[]/1/[]” -> Board “_____________________(I)______________/…/N/M/[]/1/[]” -> Board with terrain “_________________________/…/…” -> Board (20 parts, board data only)

Constants Module

Game constants for Pykrieg.

This module contains all the constants used throughout the Pykrieg game implementation, including board dimensions, players, unit types, and FEN symbols.

Types Module

Type definitions for Pykrieg.

This module contains type hints and TypedDict classes used throughout the Pykrieg game implementation for better code clarity and type safety.

class pykrieg.types.Piece[source]

Represents a piece on the board (for backward compatibility).

This is kept for backward compatibility with existing code. New code should use Unit objects directly.

type

The unit type (e.g., ‘INFANTRY’, ‘CAVALRY’)

Type:

str

owner

The player who owns the piece (‘NORTH’ or ‘SOUTH’)

Type:

str

type: str
owner: str

Turn Management

Turn management system for Pykrieg.

This module implements turn structure, phase management, and turn validation rules for the game.

Turn Structure: - Turn Start: Resolve pending retreats - Movement Phase: Up to 5 units can move - Battle Phase: 1 attack or pass - End Turn: Switch player, increment turn number

Scope for 0.1.4: - Turn validation (movement limit, attack limit) - Phase management - Retreat resolution - Turn state management - Online/offline status NOT enforced (added in 0.2.0)

exception pykrieg.turn.TurnValidationError[source]

Raised when a turn action violates turn rules.

class pykrieg.turn.TurnState(moved_units: Set[Tuple[int, int]], attacks_this_turn: int, current_phase: str, pending_retreats: List[Tuple[int, int]])[source]

Represents the state of a turn.

moved_units

Set of original (row, col) for units moved this turn

attacks_this_turn

Number of attacks made (0 or 1)

current_phase

‘M’ (Movement) or ‘B’ (Battle)

pending_retreats

List of (row, col) for units that must retreat

__init__(moved_units: Set[Tuple[int, int]], attacks_this_turn: int, current_phase: str, pending_retreats: List[Tuple[int, int]])[source]
to_dict() Dict[str, object][source]

Convert turn state to dictionary for serialization.

classmethod from_dict(data: Dict[str, object]) TurnState[source]

Create TurnState from dictionary.

pykrieg.turn.get_turn_state(board: Board) TurnState[source]

Get the current turn state from board.

Parameters:

board – The game board

Returns:

TurnState object with current turn information

pykrieg.turn.validate_turn_action(board: Board, action_type: str, **kwargs: Any) bool[source]

Validate a turn action.

Parameters:
  • board – The game board

  • action_type – Type of action (‘move’, ‘attack’, ‘pass’)

  • **kwargs – Action-specific parameters

Returns:

True if action is valid, False otherwise

pykrieg.turn.can_end_turn(board: Board) bool[source]

Check if turn can be ended.

Parameters:

board – The game board

Returns:

True if turn can be ended, False otherwise

Note

  • In movement phase, can end turn at any time (0-5 moves)

  • In battle phase, must attack or pass before ending turn

pykrieg.turn.get_turn_summary(board: Board) Dict[source]

Get a summary of the current turn state.

Parameters:

board – The game board

Returns:

Dictionary with turn summary information

Turn Methods on Board

The Board class provides turn management methods:

Movement Phase

Board.has_moved_this_turn(row: int, col: int) bool[source]

Check if a move originated from this position this turn.

Parameters:
  • row – Row of position to check

  • col – Column of position to check

Returns:

True if a unit moved FROM this position this turn, False otherwise

Board.get_moves_this_turn() int[source]

Get number of units moved this turn.

Returns:

Number of units moved (0-5)

Board.can_move_more() bool[source]

Check if player can move more units this turn.

Returns:

True if fewer than 5 units have been moved, False otherwise

Board.validate_move(from_row: int, from_col: int, to_row: int, to_col: int) bool[source]

Validate a move according to turn rules.

Checks: 1. Game is not over 2. It’s the movement phase 3. The unit belongs to the current player 4. The unit hasn’t moved yet this turn 5. The player hasn’t moved 5 units yet 6. If retreats are pending, only allow retreat moves 7. The move is legally valid (pseudo-legal check)

Parameters:
  • from_row – Source row (0-19)

  • from_col – Source column (0-24)

  • to_row – Target row (0-19)

  • to_col – Target column (0-24)

Returns:

True if move is valid according to turn rules, False otherwise

Board.make_turn_move(from_row: int, from_col: int, to_row: int, to_col: int) Tuple[object, bool][source]

Make a move with turn validation and tracking.

Modified for 0.2.2: Check for enemy arsenal destruction.

This method: 1. Validates the move according to turn rules 2. Executes the move 3. Checks if destination is enemy arsenal and destroys it 4. Tracks that the unit has moved this turn 5. Clears the retreat flag if this was a retreat move

Parameters:
  • from_row – Source row (0-19)

  • from_col – Source column (0-24)

  • to_row – Target row (0-19)

  • to_col – Target column (0-24)

Returns:

Tuple of (unit that moved, arsenal_destroyed flag)

Raises:

ValueError – If move is invalid according to turn rules

Battle Phase

Board.get_attacks_this_turn() int[source]

Get number of attacks made this turn.

Returns:

Number of attacks made (0 or 1)

Board.can_attack_more() bool[source]

Check if player can attack more this turn.

Returns:

True if 0 attacks have been made, False otherwise

Board.validate_attack(target_row: int, target_col: int) bool[source]

Validate an attack according to turn rules.

Checks: 1. Game is not over 2. It’s the battle phase 3. The current player hasn’t attacked yet 4. There’s at least one attacking unit 5. No units must retreat (retreat enforcement)

Parameters:
  • target_row – Target row (0-19)

  • target_col – Target column (0-24)

Returns:

True if attack is valid according to turn rules, False otherwise

Board.make_turn_attack(target_row: int, target_col: int) Dict[str, object][source]

Make an attack with turn validation and tracking.

This method: 1. Validates the attack according to turn rules 2. Calculates the combat result 3. Executes capture if applicable 4. Marks defender for retreat if applicable 5. Tracks that an attack has been made

Parameters:
  • target_row – Target row (0-19)

  • target_col – Target column (0-24)

Returns:

Dictionary with combat results

Raises:

ValueError – If attack is invalid according to turn rules

Board.pass_attack() None[source]

Pass attack phase.

This method: 1. Validates it’s battle phase 2. Marks that an attack has been made (pass counts) 3. Records pass state for KFEN serialization (attack_target = None)

Raises:

ValueError – If not in battle phase or already attacked

Phase Management

Board.switch_to_battle_phase() None[source]

Switch from movement phase to battle phase.

Raises:

ValueError – If not in movement phase or units must retreat

Retreat Management

Board.add_pending_retreat(row: int, col: int) None[source]

Add unit to pending retreats.

This is called when combat resolves to RETREAT outcome. The actual retreat execution happens at the start of the defender’s next turn (enforced by turn management in 0.1.4).

Note: Retreats are tracked in-memory and not persisted in KFEN format. They are resolved each turn by turn management system.

Parameters:
  • row – Row of unit that must retreat

  • col – Column of unit that must retreat

Raises:

ValueError – If no unit at position

Board.get_pending_retreats() List[Tuple[int, int]][source]

Get all pending retreats.

Returns:

List of (row, col) tuples for units that must retreat

Board.has_pending_retreat(row: int, col: int) bool[source]

Check if unit at position must retreat.

Parameters:
  • row – Row of unit to check

  • col – Column of unit to check

Returns:

True if unit at (row, col) must retreat, False otherwise

Board.clear_pending_retreats() None[source]

Clear all pending retreats after resolution.

This should be called after all retreats have been processed at the start of a turn.

Board.resolve_retreats() List[Tuple[int, int, object, str]][source]

Resolve pending retreats at the start of a turn.

This method checks for pending retreats and enforces retreat rules: 1. For each unit that must retreat, find valid retreat squares 2. If valid retreat exists, mark unit as “must retreat” (player must choose destination) 3. If no valid retreat exists, capture (destroy) the unit 4. Clear pending retreats after resolution

Returns:

  • row, col: Position where unit was captured

  • unit: The captured unit object

  • reason: String explaining why unit was captured (e.g., “no valid retreat”)

Return type:

List of tuples (row, col, unit, reason) for captured units

Note

  • This is called at the start of the defender’s turn

  • Retreating units must move before other units can move

  • Units in retreat are tracked in _units_must_retreat

Turn Control

Board.end_turn() List[Tuple[int, int, object, str]][source]

End current turn and switch to next player.

This method: 1. Clears moved unit tracking 2. Clears attack counter 3. Switches to other player 4. Resets phase to movement 5. Increments turn number 6. Resolves any pending retreats for new player 7. Recalculates networks to ensure victory checks have current state 8. Checks victory conditions (new for 0.2.2)

Returns:

List of tuples (row, col, unit, reason) for captured units during retreat resolution

Note

  • Retreat resolution happens at the start of the new player’s turn

  • Turn number increments on each player switch

  • Retreat state (_units_must_retreat) is NOT cleared automatically

  • It persists until the NEXT end_turn() call

  • This allows retreat state to persist during a player’s turn

Board.reset_turn_state() None[source]

Reset turn state without changing turn number or player.

This method is useful for: - Undo functionality - Testing scenarios - Loading from FEN

Resets: - Moved units tracking - Attack counter - Current phase (to movement)

Does NOT reset: - Turn number - Current player - Pending retreats - Board position

Board.increment_turn() None[source]

Increment turn number and switch player.

Turn State

Board.turn_number()

Return current turn number.

Board.current_phase()

Return current turn phase.

Protocol Module

The protocol module implements a UCI-like communication protocol for engine-frontend communication.

Protocol Engine

class pykrieg.protocol.engine.UCIEngine[source]

Base class for UCI-compatible engines.

__init__() None[source]
abstract get_name() str[source]

Return engine name.

Returns:

Engine name string.

abstract get_author() str[source]

Return engine author.

Returns:

Engine author string.

uci() None[source]

Initialize UCI mode.

isready() bool[source]

Check if engine is ready.

Returns:

True if engine is ready.

setoption(name: str, value: str) None[source]

Set engine option.

Parameters:
  • name – Option name.

  • value – Option value string.

Raises:

ValueError – If option is unknown.

ucinewgame() None[source]

Start a new game.

position(position_type: str, value: str | None, moves: List[str]) None[source]

Set board position.

Parameters:
  • position_type – Position type (“startpos” or “kfen”).

  • value – Position string (for KFEN), or None for startpos.

  • moves – List of move strings to apply.

Raises:

ValueError – If position setup fails.

abstract go(params: GoParameters) str[source]

Start search and return best move.

Parameters:

params – GoParameters object containing search parameters.

Returns:

Best move string, or empty string if no move available.

stop() None[source]

Stop current search.

send_info(info: InfoParameters) None[source]

Send info via callback if set.

Parameters:

info – InfoParameters object containing search information.

cleanup() None[source]

Cleanup resources.

Protocol Parser

class pykrieg.protocol.parser.ParsedCommand(command: Literal['uci', 'debug', 'isready', 'setoption', 'ucinewgame', 'position', 'go', 'stop', 'quit', 'status', 'network', 'victory', 'phase', 'retreats'], parameters: Dict[str, Any])[source]

Parsed command with its parameters.

command: Literal['uci', 'debug', 'isready', 'setoption', 'ucinewgame', 'position', 'go', 'stop', 'quit', 'status', 'network', 'victory', 'phase', 'retreats']
parameters: Dict[str, Any]
__init__(command: Literal['uci', 'debug', 'isready', 'setoption', 'ucinewgame', 'position', 'go', 'stop', 'quit', 'status', 'network', 'victory', 'phase', 'retreats'], parameters: Dict[str, Any]) None
class pykrieg.protocol.parser.ProtocolParser[source]

Parse UCI-like protocol commands.

__init__() None[source]
parse(command_string: str) ParsedCommand[source]

Parse a command string into a structured command.

Parameters:

command_string – The raw command string to parse.

Returns:

The parsed command with its parameters.

Return type:

ParsedCommand

Raises:
  • ParseError – If the command string is empty or invalid.

  • UnknownCommandError – If the command is not recognized.

Protocol Response

class pykrieg.protocol.response.ResponseGenerator(engine_name: str = 'Pykrieg Engine', engine_author: str = 'Pykrieg Team', protocol_version: str = '1.0')[source]

Generate UCI protocol responses.

__init__(engine_name: str = 'Pykrieg Engine', engine_author: str = 'Pykrieg Team', protocol_version: str = '1.0') None[source]
uci_identification() str[source]

Generate UCI identification response.

Returns:

Multi-line string with id name, id author, and uciok.

ready_ok() str[source]

Generate ready response.

Returns:

String ‘readyok’.

best_move(move: str | None, ponder_move: str | None = None) str[source]

Generate bestmove response.

Parameters:
  • move – The best move found, or None if no move available.

  • ponder_move – The ponder move for bestmove ponder, or None.

Returns:

Formatted bestmove response string.

info(info_params: InfoParameters) str[source]

Generate info response.

Parameters:

info_params – InfoParameters object containing search information.

Returns:

Formatted info response string.

option(option: EngineOption) str[source]

Generate option definition response.

Parameters:

option – EngineOption object to format.

Returns:

Formatted option response string.

status(turn: str, phase: str, turn_number: int) str[source]

Generate game status response.

Parameters:
  • turn – Current turn (“NORTH” or “SOUTH”).

  • phase – Current phase (“movement” or “battle”).

  • turn_number – Current turn number.

Returns:

Formatted status response string.

network(north_online: int, north_offline: int, south_online: int, south_offline: int) str[source]

Generate network status response.

Parameters:
  • north_online – Number of online units for NORTH.

  • north_offline – Number of offline units for NORTH.

  • south_online – Number of online units for SOUTH.

  • south_offline – Number of offline units for SOUTH.

Returns:

Formatted network response string.

victory(winner: str | None, condition: str | None) str[source]

Generate victory status response.

Parameters:
  • winner – Winning player (“NORTH” or “SOUTH”), or None if ongoing.

  • condition – Victory condition met, or None if ongoing.

Returns:

Formatted victory response string.

phase(current_phase: Literal['movement', 'battle']) str[source]

Generate phase response.

Parameters:

current_phase – Current phase (“movement” or “battle”).

Returns:

Formatted phase response string.

retreats(retreat_positions: List[str]) str[source]

Generate retreats response.

Parameters:

retreat_positions – List of positions requiring retreat.

Returns:

Formatted retreats response string.

error(message: str) str[source]

Generate error response.

Parameters:

message – Error message to send.

Returns:

Formatted error response string.

Protocol Types

class pykrieg.protocol.uci.GoParameters(depth: int | None = None, nodes: int | None = None, movetime: int | None = None, infinite: bool = False, ponder: bool = False)[source]

Parameters for the ‘go’ command.

depth: int | None = None
nodes: int | None = None
movetime: int | None = None
infinite: bool = False
ponder: bool = False
__init__(depth: int | None = None, nodes: int | None = None, movetime: int | None = None, infinite: bool = False, ponder: bool = False) None
class pykrieg.protocol.uci.Score(cp: int | None = None, mate: int | None = None, lowerbound: bool = False, upperbound: bool = False)[source]

Score information for info messages.

cp: int | None = None
mate: int | None = None
lowerbound: bool = False
upperbound: bool = False
__init__(cp: int | None = None, mate: int | None = None, lowerbound: bool = False, upperbound: bool = False) None
class pykrieg.protocol.uci.InfoParameters(depth: int | None = None, seldepth: int | None = None, time: int | None = None, nodes: int | None = None, score: Score | None = None, currmove: str | None = None, currmovenumber: int | None = None, hashfull: int | None = None, nps: int | None = None)[source]

Parameters for the ‘info’ response.

depth: int | None = None
seldepth: int | None = None
time: int | None = None
nodes: int | None = None
score: Score | None = None
currmove: str | None = None
currmovenumber: int | None = None
hashfull: int | None = None
nps: int | None = None
__init__(depth: int | None = None, seldepth: int | None = None, time: int | None = None, nodes: int | None = None, score: Score | None = None, currmove: str | None = None, currmovenumber: int | None = None, hashfull: int | None = None, nps: int | None = None) None
class pykrieg.protocol.uci.EngineOption(name: str, type: Literal['check', 'spin', 'combo', 'button', 'string'], default: str | int | bool | None, min: int | None = None, max: int | None = None, var: List[str] | None = None)[source]

Engine option definition.

name: str
type: Literal['check', 'spin', 'combo', 'button', 'string']
default: str | int | bool | None
min: int | None = None
max: int | None = None
var: List[str] | None = None
__init__(name: str, type: Literal['check', 'spin', 'combo', 'button', 'string'], default: str | int | bool | None, min: int | None = None, max: int | None = None, var: List[str] | None = None) None