Skip to content

strvdr/first-move-robotics

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

47 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Kirin Autonomous Chess System

      ,  ,
      \\ \\                 
      ) \\ \\    _p_        
       )^\))\))  /  *\      KIRIN CHESS ENGINE v0.3
        \_|| || / /^`-'     
 __       -\ \\--/ /          Author:      Strydr Silverberg
<'  \\___/   ___. )'                        Colorado School of Mines, Class of 2027
      `====\ )___/\\            
           //     `"            Hardware:    Colin Dake
           \\    /  \                          Colorado School of Mines, Class of 2027

2026 Colorado School of Mines ProtoFund Recipient
Team: Strydr Silverberg · Colin Dake


Overview

<<<<<<< HEAD Kirin is an autonomous chess system that delivers a complete single-player experience by pairing a custom chess engine with a physically-actuated board. A two-axis gantry equipped with an electromagnet moves pieces across the board independently, eliminating the need for a human opponent while preserving the tactile satisfaction of over-the-board play. This README is intended to serve as a general overview, but comprehensive documentation for all software can be found in this repository under /docs.

Kirin is an autonomous chess system that delivers a complete single-player experience by pairing a custom chess engine with a physically-actuated board. A two-axis gantry equipped with an electromagnet moves pieces across the board independently, while hall effect sensors detect the human player's moves — eliminating the need for a human opponent while preserving the tactile satisfaction of over-the-board play.

b79318b6a99573981b8e596bc012288f14f80021

The system is built around four tightly-coupled layers:

  • Chess Engine — Bitboard-based move generation, Zobrist hashing, and alpha-beta search with iterative deepening
  • Board Scanner — 96 hall effect sensors read via multiplexed GPIO to detect piece presence on the board and in storage zones
  • Board Interpreter — Translates engine moves into physical paths, handling blocker detection, piece parking, and A* pathfinding
  • Gantry Controller — Generates and dispatches G-code to a GRBL-based motion controller via serial

Hardware

         22" rail travel
|<------------------------------->|
|                                 |
|  +-+ +---------------------+ +-+|
|  |B| |                     | |W||
|  |L| |     16" board       | |H||
|  |A| |                     | |I||
|  |C| |      8x8 grid       | |T||
|  |K| |                     | |E||
|  +-+ +---------------------+ +-+|
|  3.0"        16"           3.0" |
|<--->|<------------------->|<--->|
   ^                            ^
   |                            |
Black storage               White storage
X = 1.5" (center)          X = 20.5" (center)
Y = 4" to 18"              Y = 4" to 18"
Component Specification
Rail travel 22 inches
Board size 16 × 16 inches
Square size 2 × 2 inches
Storage zones 3-inch margins, left (black) and right (white)
Motion controller GRBL 1.1
Serial connection 115200 baud, 8N1
Piece actuation Electromagnet (M3/M5 spindle commands)
Piece detection 96× A3144 hall effect sensors (active-low, 10K pull-up)
Sensor muxing 6× CD74HC4067 16:1 multiplexers, 4 shared select lines
Sensor controller Raspberry Pi GPIO (libgpiod)

Sensor Layout

The 96 hall effect sensors are organized across six multiplexers sharing four select lines (S0–S3):

Mux Coverage Sensors
0 Board ranks 8–7 (a8–h7) 16
1 Board ranks 6–5 (a6–h5) 16
2 Board ranks 4–3 (a4–h3) 16
3 Board ranks 2–1 (a2–h1) 16
4 Black storage zone 16
5 White storage zone 16

Each storage mux has 16 channels: channels 0–7 for back-rank piece slots (rook, knight, bishop, queen, king, bishop, knight, rook) and channels 8–15 for pawn slots (files a–h).


Software Architecture

main.cpp
  ├── UCI Mode           — Standard UCI protocol for chess GUIs
  ├── Physical Mode      — Live game with gantry + sensor hardware
  ├── Simulation Mode    — Interactive, no hardware (manual UCI moves)
  └── Dry Run Mode       — Engine-vs-engine, full G-code audit, no hardware

GameController           — Orchestrates engine ↔ physical board
  ├── PhysicalBoard      — Bitboard-based physical state tracker
  ├── GrblController     — Serial comms, G-code dispatch, capture tracking
  ├── BoardScanner       — Hall effect sensor polling, move detection
  └── PieceTracker       — Maps individual piece identity to board squares

BoardInterpreter         — Path planning
  ├── Piece-specific paths (sliding, knight L-paths, etc.)
  ├── Blocker detection & temporary piece parking
  └── A* pathfinding for clear routes

Engine
  ├── Bitboard move generation (attacks.cpp, movegen.cpp)
  ├── Zobrist hashing (zobrist.cpp)
  ├── Alpha-beta search with iterative deepening (search.cpp)
  ├── Evaluation (evaluation.cpp)
  └── Difficulty system (skillLevel 0–2)

Key Data Flow

Engine → Gantry (engine moves a piece):

searchPosition(depth)
    → bestMove (encoded int)
        → engineToPhysicalMove()
            → PhysicalMove { from, to, pieceType, isCapture, ... }
                → planMove()
                    → Path (sequence of BoardCoords)
                        → generateMovePlanGcode()
                            → std::vector<std::string> G-code commands
                                → GrblController::sendCommands()

Sensors → Engine (human moves a piece):

BoardScanner::waitForLegalMove()
    → poll sensors until board/storage state changes
        → debounce, diff against baseline
            → matchLegalMove() (disambiguate via PieceTracker + storage slot)
                → matched engine move
                    → GameController advances engine state

Piece Identity Tracking

The engine only knows piece types on squares ("there's a black knight on d5"), not individual piece identity ("the b8-knight is on d5"). But the physical board has labeled storage slots, so when a capture occurs the system needs to know which piece was taken to route it to the correct slot. The PieceTracker maintains a 64-entry map from board squares to starting-slot identifiers, updated on every move.


Engine Difficulty

The engine exposes three difficulty levels via the skillLevel global (set at startup or through the UCI interface). Each level adjusts both search depth and move selection behaviour.

Level Name Max Depth Move Selection
0 Easy 3 Best scored move ± random noise up to 150cp
1 Medium 5 Best scored move ± random noise up to 50cp
2 Hard 64 (unlimited) Always the engine's top move — no noise

At levels 0 and 1, after the search completes, every legal root move is re-scored with a symmetric random perturbation. This causes the engine to occasionally prefer a suboptimal move, producing play that feels more natural for casual opponents rather than uniformly optimal. At level 2 the perturbation is zero and the engine always plays its principal variation.


Building

The project uses CMake (3.10+) with a C++17 toolchain. Serial hardware support is conditionally compiled via the HAS_SERIAL preprocessor flag (set automatically on Linux). GPIO sensor support requires libgpiod and the HAS_GPIOD flag.

kirin/
├── CMakeLists.txt
├── src/
│   ├── engine/         Chess engine core
│   ├── hardware/       Physical board control
│   └── main.cpp
└── tests/
    ├── captured_piece_test.cpp
    ├── board_interpreter_test.cpp
    ├── game_controller_test.cpp
    ├── engine_test.cpp
    ├── board_scanner_test.cpp
    ├── generate_test_report.py
    └── TEST_RESULTS.md
git clone https://github.com/strvdr/first-move-robotics.git
cd first-move-robotics/kirin
mkdir build && cd build

# Configure (Release by default)
cmake ..

# Configure Debug build
cmake .. -DCMAKE_BUILD_TYPE=Debug

# Enable GPIO sensor support (Raspberry Pi only)
cmake .. -DHAS_GPIOD=ON

# Build
cmake --build .

# Run tests
ctest

# Generate markdown test report (writes tests/TEST_RESULTS.md)
cmake --build . --target test_report

The CMake build produces three targets:

Target Description
kirin Main executable
kirin_engine Static library — chess engine core
kirin_hardware Static library — board interpreter, scanner, gantry controller (links engine)

Release builds are compiled with -O3 -march=native -flto. Debug builds use -g -O0 -DDEBUG.


Testing

The project has a comprehensive test suite covering all layers. Tests are run via CTest and results are automatically published to tests/TEST_RESULTS.md on every push to main via GitHub Actions.

Suite Coverage Assertions
captured_piece_test Move encoding — captured-piece bit-field, flag isolation, bit-position overlap 11
board_interpreter_test Path planning, blocker relocation/restoration, knight routing, A* pathfinding, nested blockers, edge cases 43
game_controller_test Coordinate conversion, piece-type mapping, PhysicalBoard sync, parseBoardMove, isGameOver, special moves, PieceTracker integration 114
engine_test Perft node counts (4 positions × up to depth 4), evaluation sanity, tactical position structure, skill-level globals, repetition detection 46
board_scanner_test Sensor move detection, capture disambiguation via storage slots, castling/en passant detection, PieceTracker state tracking, simulation mode 88

Test design notes

The engine test suite avoids calling searchPosition() directly. In a CTest subprocess stdin is connected to /dev/null, which the engine's communicate() function interprets as a stop signal, setting stopped = 1 and corrupting all subsequent search results. Instead, engine correctness is verified through:

  • Perft — recursive legal-node counts matched against published ground truth (chessprogramming.org) for four standard positions including Kiwipete
  • evaluate() — direct calls to check sign, symmetry, and material ordering
  • generateMoves() / makeMove() — used to verify tactical positions structurally: checkmate and stalemate are confirmed by perft(1) == 0 combined with isAttacked(), specific moves are confirmed present or absent in the legal move list

The board scanner tests run entirely in simulation mode and do not require GPIO hardware.


Usage

Usage: kirin [OPTIONS]

Options:
  (no args)                  Run in UCI mode (for chess GUIs)
  --physical PORT            Connect to gantry on serial PORT
                               e.g.  kirin --physical /dev/ttyUSB0
  --simulate                 Interactive simulation (no hardware)
  --dryrun [MOVES [DEPTH]]   Engine-vs-engine dry run, prints all G-code
                               MOVES  = max moves per side (default 40)
                               DEPTH  = search depth     (default 4)
                               e.g.  kirin --dryrun 20 5
  --help, -h                 Show this help message

UCI Mode

Exposes the engine over standard UCI for use with chess GUIs (Arena, Cute Chess, etc.) or for automated testing via the go / position / ucinewgame command set.

./kirin

Physical Mode

Connects to the gantry over serial, initializes the board scanner, homes the axes, and enters an interactive command loop. Supports two play styles:

  • play [white|black] — Sensor-based game. The human moves pieces physically on the board; the system detects moves via hall effect sensors and responds automatically via the gantry. This is the primary play experience.
  • newgame [white|black] — Manual-entry fallback. The human types moves in UCI notation. Useful when sensors are unavailable or for debugging.

Additional commands include scan (display raw sensor readings), diag (full sensor diagnostic), board, fen, home, and test.

./kirin --physical /dev/ttyUSB0

Simulation Mode

Interactive move-by-move walkthrough. Enter moves in UCI format and inspect the generated move plans and G-code — no hardware required.

./kirin --simulate

Dry Run Mode

Plays a complete engine-vs-engine game automatically and prints every G-code command that would be sent to the gantry, with inline annotations. Use this to audit the full command sequence before powering the hardware for the first time.

./kirin --dryrun          # 40 moves per side, depth 4
./kirin --dryrun 20 5     # 20 moves per side, depth 5

Example dry-run output:

══════════════════════════════════════════════════════════════
  Move 1.  e2e4
══════════════════════════════════════════════════════════════
  $H                              ; HOME (seek limit switches)
  G1 X4.00 Y14.00                 ; MOVE
  M3                              ; MAGNET ON
  G4 P0.5                         ; DWELL (settle)
  G1 X4.00 Y10.00                 ; MOVE
  M5                              ; MAGNET OFF
  ...

Module Reference

src/main.cpp — Entry point, run-mode dispatch, sensor-based game loop

src/engine/

File Responsibility
attacks.h/.cpp Attack table generation (magic bitboards)
bitboard.h/.cpp Bitboard primitives and utilities
movegen.h/.cpp Legal move generation
search.h/.cpp Alpha-beta search, iterative deepening, difficulty system
evaluation.h/.cpp Static position evaluation
position.h/.cpp Board state, FEN parsing
zobrist.h/.cpp Zobrist hashing for transposition table
uci.h/.cpp UCI protocol handler
types.h/.cpp Shared type definitions
utils.h/.cpp Time control, random numbers, I/O utilities
engine.h Umbrella include for engine internals

src/hardware/

File Responsibility
game_controller.h/.cpp Engine ↔ hardware integration, special move handling, game loop orchestration
gantry_controller.h/.cpp G-code generation, GRBL serial comms, capture zone tracking, coordinate translation
board_interpreter.h/.cpp Path planning, blocker detection, A* routing
board_scanner.h/.cpp Hall effect sensor polling via multiplexed GPIO, move detection, capture disambiguation
piece_tracker.h Per-square piece identity tracking for storage-slot-based capture matching

tests/

File Coverage
captured_piece_test.cpp Move encoding — captured-piece bit-field round-trips
board_interpreter_test.cpp Path planning, blocker handling, A* pathfinding
game_controller_test.cpp Move encoding/decoding, coordinate conversion, physical board state, special moves
engine_test.cpp Perft, evaluation sanity, tactical positions, skill level, repetition detection
board_scanner_test.cpp Sensor move detection, storage-slot capture disambiguation, PieceTracker integration
generate_test_report.py Runs all binaries and writes TEST_RESULTS.md

Coordinate Systems

The codebase uses two coordinate systems that are explicitly converted at the engine/hardware boundary.

Engine squares (0–63): a8 = 0, b8 = 1, ..., h1 = 63

BoardCoord (row, col): row 0 = rank 8, col 0 = file a — matches the physical board orientation as viewed from white's side.

Physical position (inches): origin at the gantry home position (bottom-left); X increases right, Y increases up.


Storage Zone Layout

Captured pieces are stored in two zones flanking the board. Each zone has two columns (back-rank pieces / pawns) with 2-inch vertical spacing matching the board squares. Each storage slot is monitored by a dedicated hall effect sensor, enabling the system to detect which specific slot a captured piece was placed in.

Zone X center Y range Contents
Black storage (left) 1.5" 4" – 18" White pieces taken by black
White storage (right) 20.5" 4" – 18" Black pieces taken by white

Within each zone, slots are assigned to specific starting pieces (rook-a through rook-h for back-rank pieces, pawn-a through pawn-h for pawns). The PieceTracker maps each piece's current board square to its starting slot, allowing the system to verify that captured pieces are placed in the correct storage position.


Development Status

Feature Status
Bitboard engine & UCI ✅ Complete
G-code generation pipeline ✅ Complete
Dry run mode (engine-vs-engine) ✅ Complete
Simulation mode ✅ Complete
Serial / GRBL integration ✅ Complete
Engine difficulty system (Easy / Medium / Hard) ✅ Complete
Hall effect sensor scanning (96 sensors, 6 muxes) ✅ Complete
Sensor-based human move detection ✅ Complete
Storage-slot capture disambiguation (PieceTracker) ✅ Complete
Comprehensive test suite (300+ assertions) ✅ Complete
CI test report via GitHub Actions ✅ Complete
Physical board testing 🔧 In progress
En passant path planning 🔧 In progress
Promotion handling 🔧 In progress

License

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License v3.0 as published by the Free Software Foundation. See LICENSE for details.


Acknowledgments

Funded by the 2026 Colorado School of Mines ProtoFund.
This prototype applies engineering principles and computer science fundamentals developed through coursework at the Colorado School of Mines.


Kirin v0.3 — Colorado School of Mines, 2026

About

Chess against AI, but on a real-world chess board.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors