UCI Protocol¶
Overview¶
The engine communicates via the Universal Chess Interface (UCI) protocol.
Supported Commands¶
Standard UCI Commands¶
IN uci
Initialize UCI mode and request engine identification.
IN isready
Check if engine is ready. Engine responds with readyok.
IN ucinewgame
Reset engine state for a new game.
IN position [fen | startpos] moves ...
Set up a position.
IN go [options]
Start calculating. Options include:
depth n– search to depth nnodes n– search a fixed number of nodes (reproducible; see Ch. Measuring Strength)movetime ms– search for ms millisecondswtime/btime ms– time remaining for white/blackwinc/binc ms– increment per movemovestogo n– moves until next time controlinfinite– search until stoppedperft n– run perft test to depth n
IN stop
Stop calculating.
IN quit
Exit the engine.
Engine Responses¶
id name Kirin
id author Strydr Silverberg
uciok
info score cp 35 depth 8 nodes 234567 time 1250 pv e2e4 e7e5 g1f3
bestmove e2e4
The option surface¶
Kirin advertises 26 options in response to uci. They fall into four groups:
Search features — one
checkoption per pruning or ordering technique (§The pruning and reduction surface).Evaluation —
Use NNUE,EvalFileand the five NNUE policy knobs (Ch. NNUE Evaluation).Time management —
Move Overhead,Slow Mover,Max Managed Move Time(Ch. Time Management).General —
Skill LevelandHash.
The full table, with every default and range, is Appendix UCI Option Reference.
This is a larger option surface than a chess engine strictly needs, and that is deliberate: it is the mechanism that makes A/B testing possible without rebuilding. A candidate and a baseline in a gauntlet are the same binary differing by one setoption line, which removes the compiler, the flags and the machine as confounders (Ch. Measuring Strength). Every toggle in the search group exists to be measured, and several are off today precisely because that measurement has not happened yet.
Skill Level¶
Kirin exposes a three-tier difficulty setting that adjusts both search depth and move quality, making the engine accessible to players of all levels while preserving full strength for competitive play.
Difficulty Tiers¶
Level |
Name |
Max Search Depth |
Move Noise |
|---|---|---|---|
0 |
Easy |
3 plies |
$\pm$ |
1 |
Medium |
5 plies |
$\pm$ |
2 |
Hard (default) |
Caller-supplied depth |
None |
Skill level configuration
Two independent mechanisms are combined to simulate human-like play at lower difficulty settings:
Depth capping. Inside
searchPosition, the caller-supplied depth is silently clamped to a tier-specific ceiling before iterative deepening begins. At skill 0 the engine never searches deeper than 3 plies; at skill 1 the cap is 5 plies; at skill 2 the cap is set to 64 (effectively unlimited for practical time controls).Root-move noise. After the search completes, the engine’s best move is not always played. At skill levels below 2, each legal root move is assigned the engine’s score perturbed by a random offset drawn uniformly from $[-\textit{range},, +\textit{range}]$, where range is 150 cp at skill 0 and 50 cp at skill 1. The move with the highest noisy score is selected. This causes the engine to occasionally prefer a suboptimal move in a way that resembles human error rather than purely random play.
UCI Configuration¶
The skill level is exposed as a standard UCI spin option and can be set from any compatible GUI or from the command line:
setoption name Skill Level value 0 % Easy
setoption name Skill Level value 1 % Medium
setoption name Skill Level value 2 % Hard (default)
The engine announces the option during UCI initialisation:
option name Skill Level type spin default 2 min 0 max 2
Values outside the range [0, 2] are clamped silently. The engine acknowledges each change with an info string:
info string Skill Level set to 1
Implementation¶
The global variable skillLevel in search.cpp stores the current setting. It is read in two places:
// Depth cap applied at the start of searchPosition()
static const int skillDepthCap[3] = { 3, 5, 64 };
int depthCap = skillDepthCap[skillLevel];
if (depth > depthCap) depth = depthCap;
// Noise injection applied after the best move is found
if (skillLevel < 2) {
static const int noiseRange[2] = { 150, 50 };
int range = noiseRange[skillLevel];
// ... re-score root moves with random perturbation
}
Note
Skill level does not affect the correctness of move generation or position evaluation — illegal moves are never produced regardless of the difficulty setting. The engine test suite (engine_test.cpp) verifies this invariant across all three levels and multiple board positions.