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’)
- 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)
- 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
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:
objectRepresents 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 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" ... })
- 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.
- 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
- 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_legal_moves(row: int, col: int) List[Tuple[int, int]][source]
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
- is_legal_move(from_row: int, from_col: int, to_row: int, to_col: int) bool[source]
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
- 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:
objectFEN (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:
- owner
The player who owns the piece (‘NORTH’ or ‘SOUTH’)
- Type:
- 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)
- 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
- 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
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
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.
- setoption(name: str, value: str) None[source]
Set engine option.
- Parameters:
name – Option name.
value – Option value string.
- Raises:
ValueError – If option is unknown.
- 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.
- send_info(info: InfoParameters) None[source]
Send info via callback if set.
- Parameters:
info – InfoParameters object containing search information.
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.
- class pykrieg.protocol.parser.ProtocolParser[source]
Parse UCI-like protocol commands.
- 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:
- 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.
- 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.
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.
- 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.
- 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.