hardware/game_controller.h¶
Integration layer between engine and physical board.
This module bridges the chess engine’s internal representation with the physical board interpreter and gantry controller. It handles:
Converting engine move encoding to physical moves
Converting engine piece types to physical piece types
Orchestrating move execution on the physical board
Special move handling (castling, en passant, promotion)
Detecting human moves via hall effect sensor scanning
IMPORTANT: This header is designed to be included AFTER board_interpreter.h but BEFORE any engine headers to avoid macro conflicts.
Functions
-
PieceType engineToPhysicalPiece(int enginePiece)¶
Convert engine piece enum to physical PieceType
Engine pieces: P=0, N=1, B=2, R=3, Q=4, K=5, p=6, n=7, b=8, r=9, q=10, k=11 Physical pieces: PAWN=0, KNIGHT=1, BISHOP=2, ROOK=3, QUEEN=4, KING=5, UNKNOWN=6
- Parameters:
enginePiece – The engine’s piece enum value (0-11)
- Returns:
The corresponding PieceType for path generation
-
int physicalToEnginePiece(PieceType pt, bool isWhite)¶
Convert physical PieceType back to engine piece enum
- Parameters:
pt – The physical piece type
isWhite – True if the piece is white
- Returns:
The engine’s piece enum value (0-11)
-
inline bool isWhitePiece(int enginePiece)¶
Check if an engine piece is white
- Parameters:
enginePiece – The engine’s piece enum value (0-11)
- Returns:
True if the piece is white (0-5), false if black (6-11)
-
PhysicalMove engineToPhysicalMove(int engineMove)¶
Convert engine move encoding to PhysicalMove struct
Extracts source/target squares, piece type, and capture flag from the engine’s compact move encoding.
- Parameters:
engineMove – The engine’s encoded move (see movegen.h for format)
- Returns:
PhysicalMove struct ready for path planning
-
inline BoardCoord squareToBoardCoord(int square)¶
Convert a square index to BoardCoord
Engine squares: a8=0, b8=1, …, h1=63 BoardCoord: row 0 = rank 8, col 0 = file a
- Parameters:
square – The engine’s square index (0-63)
- Returns:
BoardCoord for the physical board
-
inline int boardCoordToSquare(const BoardCoord &coord)¶
Convert BoardCoord to engine square index
- Parameters:
coord – The board coordinate
- Returns:
Engine square index (0-63)
-
int parseBoardMove(const char *moveStr)¶
Parse a move string (e.g., “e2e4”) and find the matching move in the move list
- Parameters:
moveStr – The move string to parse
- Returns:
The encoded move if found, 0 otherwise
-
void printPhysicalBoard(const PhysicalBoard &board)¶
Print the current physical board state (for debugging)
-
class GameController¶
- #include <game_controller.h>
Public Functions
-
GameController()¶
-
~GameController()¶
-
bool connectHardware(const char *port)¶
Connect to the gantry controller hardware
- Parameters:
port – Serial port path (e.g., “/dev/ttyUSB0”)
- Returns:
True if connection successful
-
bool initScanner()¶
Initialize the board scanner hardware
- Returns:
True if scanner initialization succeeded
-
bool homeGantry()¶
Home the gantry (move to reference position)
- Returns:
True if homing successful
-
bool setupNewGame()¶
Set up a new game - move all pieces from capture zones to starting positions Also resets the physical board state tracking
- Returns:
True if setup successful
-
void syncWithEngine()¶
Synchronize physical board state with engine state without changing per-piece identity tracking.
This updates occupancy and scanner baselines only. PieceTracker must be initialized or updated separately so move-history-dependent identity information is preserved across normal gameplay.
-
void initTrackerForStartingPosition()¶
Reset piece identity tracking for a standard new game starting position. This should only be used when the engine position is the normal start.
-
void invalidateTracker()¶
Mark tracker identity as unavailable for slot-based move disambiguation. Use this after loading arbitrary positions or making manual identity- destroying edits.
-
void initTrackerFromSnapshot(const BoardIdentity &snapshot)¶
Seed piece identity from a known past snapshot (review-docs §6.5), so a review or a “play from here” game starts with exact identity instead of the standard start. Marks the tracker Exact.
-
bool executeSetupPlan(const SetupPlan &plan, const BoardIdentity &target)¶
Physically realize a setup plan (review-docs §6.3): drive the gantry through the plan’s ordered Move / Store / Retrieve steps, then adopt
targetas the exact identity. Path collisions per step are handled by planMove() as usual; the plan only guarantees ordering. Returns false on an invalid plan or a hardware error.
-
bool setUpPosition(const BoardIdentity &target)¶
Physically drive the pieces from wherever they stand now to
target.Plans from the tracked identity, so it is safe from any known board state: an already-correct board plans to an empty plan and executes as a no-op, which is what makes it usable when the caller cannot know whether the pieces are already in place (see HardwareBackend::prepareNewGameFromSnapshot — a review followed on the board has already built the position; one reviewed silently on screen has not).
Requires exact identity — where a piece must go is its slot, which the type-only engine state cannot tell us. Returns false if identity is Unknown (a cold board the machine has nothing to plan from) or on a hardware error.
-
bool resetToStartingPosition()¶
Physically restore the standard starting position from wherever the pieces are now — the end of a game, with most pieces still on the board and the captured ones in storage. A setUpPosition() to the standard start.
This is NOT setupNewGame(). That routine blindly retrieves all 32 pieces from their storage slots to their starting squares, which is only correct for a cold board with every piece parked in the rack; run it on a finished game and it flies to empty slots and drops pieces onto occupied squares. Here we plan from the tracked identity instead, so the ordering is safe.
Requires exact identity — see setUpPosition(). Returns false if identity is Unknown (caller should fall back to a hand reset) or on a hardware error.
-
bool executeEngineMove(int engineMove)¶
Execute a move from the engine on the physical board Handles normal moves, captures, and special moves (castling, en passant, promotion)
- Parameters:
engineMove – The engine’s encoded move
- Returns:
True if move executed successfully
-
bool undoMove(int engineMove, int capturedSlot)¶
Physically reverse a previously-executed move (physical review, §6.2). Backward stepping in a ReviewSession is one gantry operation, not a replay: the review session knows exactly what the move did, so it supplies the captured piece’s storage slot (SLOT_NONE for a non-capture) to retrieve it. Updates the physical board and the piece tracker in step, so the tracker stays exact as the reviewer scrubs backward and forward.
- Parameters:
engineMove – The engine move to undo (the one that produced the current position)
capturedSlot – The StartingSlot the captured piece is standing in, or SLOT_NONE for a non-capture
- Returns:
True if the undo executed successfully
-
bool executeHumanMove(const char *moveStr)¶
Execute a human move given in algebraic notation (typed input) Parses the move string and converts to engine format
- Parameters:
moveStr – Move in format like “e2e4”, “e7e8q” (promotion)
- Returns:
True if move executed successfully
-
bool waitForHumanMove(int &engineMove, int timeoutMs = 0, const std::atomic<bool> *cancelFlag = nullptr)¶
Wait for a human player to make a move on the physical board. Uses the hall effect sensors to detect piece movement, then validates the detected move against the engine’s legal move list.
This is the primary interface for sensor-based human input. It handles all move types (normal, capture, castling, en passant, promotion) by waiting until the board state corresponds to exactly one legal move.
The human can take as long as they want — there is no timeout that auto-commits a move. If the board sits in an invalid state for too long (e.g., the player knocked a piece over), the system will alert them via a callback but continue waiting patiently.
- Parameters:
engineMove – [out] Set to the matched engine move on success
timeoutMs – Maximum wait time (0 = wait forever)
- Returns:
True if a legal move was detected, false on timeout
-
inline void setScannerObserver(const ScannerObserver &observer)¶
Install the LED subsystem’s scanner observer (PLAN §6). Passed to the scanner on the next waitForHumanMove so a physical piece lift lights its legal moves. Copied by value; the observer’s context must outlive the controller (the LED subsystem does).
-
inline void setPlanObserver(const PlanObserver &observer)¶
-
bool executeCastling(int engineMove)¶
Execute a castling move Moves both king and rook in the correct order
- Parameters:
engineMove – The castling move from the engine
- Returns:
True if successful
-
bool executeEnPassant(int engineMove)¶
Execute an en passant capture Handles the capture from the correct square (not destination)
- Parameters:
engineMove – The en passant move from the engine
- Returns:
True if successful
-
bool executePromotion(int engineMove)¶
Execute a pawn promotion For now, just moves the pawn (simplified approach)
- Parameters:
engineMove – The promotion move from the engine
- Returns:
True if successful
-
bool verifyBoardState(Bitboard &expectedOcc, Bitboard &actualOcc)¶
Scan the physical board and verify it matches the engine’s expected state. Call this after gantry moves to catch dropped pieces, missed steps, or failed magnet engagement.
- Parameters:
expectedOcc – [out] Set to the engine’s expected occupancy
actualOcc – [out] Set to the sensor scan result
- Returns:
True if the physical board matches the engine’s expected state
-
bool verifyBoardState()¶
Convenience overload: verify and print diagnostics on mismatch.
- Returns:
True if board matches engine state
-
bool isGameOver() const¶
Check if the game is over (checkmate, stalemate, etc.) Queries the engine for game state
- Returns:
True if game has ended
-
inline const PhysicalBoard &getPhysicalBoard() const¶
Get the physical board state (for debugging)
-
inline Gantry::GrblController &getGantry()¶
Get the gantry controller (for manual operations)
-
inline BoardScanner &getScanner()¶
Get the board scanner (for diagnostics and direct access)
-
inline const PieceTracker &getPieceTracker() const¶
Get the piece tracker (for diagnostics and direct access)
-
inline PieceTracker &getPieceTracker()¶
-
inline TrackerValidity getTrackerValidity() const¶
Query whether slot-based piece identity is still exact.
-
inline bool hasExactTracker() const¶
-
void updateTracker(int engineMove)¶
Update the piece tracker after an engine move has been applied. Call this after makeMove() succeeds so the tracker stays in sync.
- Parameters:
engineMove – The engine move that was just applied
-
inline bool isConnected() const¶
Check if hardware is connected
-
inline bool isScannerReady() const¶
Check if the scanner is ready
-
inline void setEnginePlaysWhite(bool playsWhite)¶
Set which side the engine plays
-
bool isEngineTurn() const¶
Check if it’s the engine’s turn
-
inline bool isHumanTurn() const¶
Check if it’s the human’s turn
-
void startGame(bool enginePlaysWhite)¶
Start a new game with the engine playing the specified side
-
inline void stopGame()¶
Stop the current game
-
inline bool isGameInProgress() const¶
Check if a game is in progress
Public Static Functions
Private Functions
-
bool executeCastlingInternal(int engineMove)¶
-
bool executeEnPassantInternal(int engineMove)¶
-
bool executePromotionInternal(int engineMove)¶
-
bool executeNormalMove(int engineMove)¶
-
int getCapturedSlotForMove(int engineMove) const¶
-
bool undoNormalMove(int engineMove, int capturedSlot)¶
-
bool undoCastlingInternal(int engineMove)¶
-
bool undoEnPassantInternal(int engineMove, int capturedSlot)¶
-
bool retrieveFromStorage(const BoardCoord &square, bool isWhite, int slot)¶
Private Members
-
Gantry::GrblController gantry¶
-
BoardScanner scanner¶
-
PhysicalBoard physicalBoard¶
-
PieceTracker pieceTracker¶
-
bool gameInProgress¶
-
bool waitingForHuman¶
-
bool enginePlaysWhite¶
-
TrackerValidity trackerValidity¶
-
ScannerObserver scannerObserver¶
-
bool hasScannerObserver = false¶
-
PlanObserver planObserver¶
-
bool hasPlanObserver = false¶
-
GameController()¶
-
namespace EngineMove¶
Functions
-
inline int getSource(int move)¶
-
inline int getTarget(int move)¶
-
inline int getPiece(int move)¶
-
inline int getPromoted(int move)¶
-
inline bool getCapture(int move)¶
-
inline bool getDpp(int move)¶
-
inline bool getEnpassant(int move)¶
-
inline bool getCastle(int move)¶
-
inline int getCapturedPiece(int move)¶
-
inline int getSource(int move)¶