Case study

Kyndill

Rust game AI for the AI Fundamentals Challenge, built around a strict deadline

I built this Rust Ashton Tablut player for the University of Bologna AI Fundamentals Challenge. It reads a framed TCP protocol, generates legal moves locally and runs deterministic alpha-beta search over a compact board state. Before replying, a final guard checks the move and remaining time.

  • University challenge project
  • Completed
  • Rust
  • Game AI
  • Alpha-beta
  • Heuristics
  • Protocols

Overview

Project summary

What it is
Rust Ashton Tablut player for the University of Bologna AI Fundamentals Tablut Challenge.
My role
I built the protocol adapter, local rule engine, compact state model, deadline-aware search and final guard.
Core stack
Rust, framed JSON over TCP, u128 occupancy masks, packed moves, alpha-beta, transposition table.
Repository
The repository includes protocol handling, legal move generation, search, timeout control and tests.

Engine flow

I built Kyndill to play Ashton Tablut under a strict referee protocol and response deadline. Each turn arrives as JSON over TCP, framed by a 4-byte big-endian length prefix. The protocol adapter decodes that payload into typed local state, which the engine then searches.

I kept the Ashton Tablut rules in a local engine for the 9 x 9 board. It generates moves, checks captures and king escape conditions, and produces legal child states, so the search code never needs to construct protocol actions.

Engine flow

From referee state to legal action

01

Referee framed JSON

TCP payload with a 4-byte big-endian length prefix.

02

Protocol adapter

The adapter decodes the role, board, turn and deadline.

03

Typed state

It converts the external state into checked local structures.

04

Local rule engine

The local engine generates only legal Ashton Tablut moves.

05

Compact search state

The search uses occupancy masks, packed moves and hashing.

06

Iterative deepening

The engine completes each depth with alpha-beta pruning.

07

Candidate move

The engine keeps the best fully completed legal result.

08

Final guard

The final guard checks legality and the remaining time before sending.

09

Framed JSON action

The adapter returns one validated move to the referee.

State model

Compact board state

The search core uses a small checked state, so it does not need to interpret the referee payload again at every step.

Board
9 x 9 Ashton Tablut state
Masks
u128 occupancy bitsets
Moves
Packed move representation
Table
Transposition table entries
Deadline behavior

Fallback before timeout

  1. Response window

    Each turn starts with a fixed response window.

  2. Completed depth

    The engine stores only completed legal results.

  3. Deadline check

    The engine stops deeper work as the deadline approaches.

  4. Fallback

    The player sends the last completed result or a deterministic prevalidated fallback.

Search under a deadline

The search is deterministic alpha-beta with iterative deepening. After completing a depth, the engine saves its best legal move before starting the next one. If the deadline interrupts a deeper search, the previous result is still available.

The search path uses compact u128 occupancy masks, packed moves, incremental hashing, a bounded transposition table, and killer and history ordering.

Safety guard

Before writing a response, Kyndill checks both legality and the remaining time. If deeper search cannot finish safely, it sends either the last completed iterative-deepening result or a deterministic fallback that has already been checked.

Safety boundary

  1. Decode

    Read the framed JSON payload and convert it into typed local state.

    protocol
  2. Generate

    Build legal Ashton Tablut moves from the local rule engine.

    rules
  3. Search

    Run iterative-deepening alpha-beta search within the move budget.

    engine
  4. Guard

    Send a legal move before the deadline, using the prevalidated deterministic fallback if needed.

    deadline

Implementation notes

  • V4 is the default runtime profile.
  • White uses static installed weights tuned offline.
  • Black stays frozen as a manually tuned asymmetric path.
  • Runtime play uses installed static profiles only.