Leiserchess contains the following sub directories:
bin:
- A directory for your binaries. Helpful for competitive analysis and version control.
player:
- The code for the game engine that you will be improving.
- There is a README in the directory that orients you to the codebase and gives a high-level overview of what each of the components does.
- We have provided a basic Makefile. You can type 'make' to compile the program. Upon compilation, a binary 'leiserchess' will be produced.
- Warning: You are strongly advised NOT to reimplement the UCI interface in
leiserchess.c.
tester:
- An autotesting framework for Leiserchess developed by Don Dailey, although the framework supports any two-player game engine that supports the UCI (universal chess interface) protocol.
- You don't need to understand any of the codebase. You just need to use it.
- The autotester plays multiple player engines against each other according to the input configuration file given and produces a PGN file (*.pgn) that is a recording of the game plays.
- You will find detailed instructions later under EVALUATE PLAYERS.
tester/BayesElo:
- This is software developed by Rémi Coulom to estimate Elo ratings. (https://remi.coulom.free.fr/Bayesian-Elo/) (The Elo rating system is a method for calculating the relative skill levels of players in two-player games.)
- Again, for the purpose of this project, you only need to familiarize yourself with the use of this tool, but not actually understand any of the code base.
- BayesElo takes a PGN file (*.pgn), which is essentially a recording of game plays, and produces a rating for the players based on the PGN file. The PGN file will be generated by our autotester. You will find detailed instructions on how to use BayesElo and autotester later in this README, under EVALUATE PLAYERS.
tests:
- A directory for you to store your configuration files for the autotester.
- Note that when you run the autotester, the PGN file produced will go into this directory as well.
training:
- All the necessary scripts to train the NNUE from scratch.
lcgui:
- The codebase of a web GUI for playing Leiserchess, so you can play with your classmate or against the player bot you build. Or even better, have your parent play with the bot you built. :-)
- Again, this is a tool for you to use, and you don't need to understand the codebase to do well in this project. Of course, if you are so inclined, we welcome you to hack the web interface as well and contribute to the codebase.
This portion of the document is based on the Universal Chess Interface (UCI) document of April 2006 which was designed by Stefan Meyer-Kahlen, the author of the Shredder chess program.
In October 2011 it was adapted to Khet 2 by Ruben Perez and Don Dailey. In October 2012 it was adapted to Leiserchess.
-
The specification is independent of the operating system.
-
All communication is done via standard input and output with text commands.
-
The engine should boot and wait for input from the GUI. The engine should wait for the "isready" or "setoption" command to set up its internal parameters as the boot process should be as quick as possible.
-
All command strings the engine receives will end with '\n', also all commands the GUI receives should end with '\n'. Note: '\n' can be 0x0d or 0x0a0d or any combination depending on your OS. If you use Engine and GUI in the same OS this should be no problem if you communicate in text mode, but be aware of this when for example running a Linux engine in a Windows GUI.
-
Arbitrary white space between tokens is allowed.
-
The engine will always be in forced mode, which means it should never start calculating without receiving a "go" command first.
-
Before the engine is asked to search on a position, there will always be a position command to tell the engine about the current position.
-
By default the opening book is disabled. The "USE_OB" option (see below) allows the engine to use its own book.
-
If the engine receives a command which it is not supposed to come, for example "stop," when the engine is not calculating, it should also just ignore it.
The format for moves is the algebraic notation described in the Leiserchess_2026.pdf document.
These are all the commands the engine gets from the interface.
-
uciTell the engine to use the UCI (Universal Chess Interface). This will be sent once as a first command after the program boots to tell the engine to switch to UCI mode. After receiving the uci command, the engine must identify itself with the "id" command and send the "option" commands to tell the GUI which engine settings the engine supports if any. After that, the engine should send "uciok" to acknowledge the UCI mode. If no uciok is sent within a certain time period, the engine task will be killed by the GUI.
-
isreadyThis command is used to synchronize the engine with the GUI. When the GUI has sent a command or multiple commands that can take some time to complete, this command can be used to wait for the engine to be ready again or to ping the engine to find out if it is still alive. This command is also required once before the engine is asked to do any search to wait for the engine to finish initializing. This command must always be answered with "readyok" and can be sent also when the engine is calculating, in which case the engine should also immediately answer with "readyok" without stopping the search.
-
setoption name <id> [value <x>]This command is sent to the engine when the user wants to change the internal parameters of the engine. For the "button" type, no value is needed. One string will be sent for each parameter, and it will only be sent when the engine is waiting. The name and value of the option in should not be case sensitive and can include spaces. The substrings "value" and "name" should be avoided in and to allow unambiguous parsing. For example, do not use = "draw value".
Here are some examples:
- "setoption name lcoverage value 200\n"
- "setoption name draw value 0\n"
- "setoption name hash value 64\n"
-
position [fen <fenstring> | classic ] moves <move1> .... <movei>NOTE: fen is described in the Leiserchess_2023.pdf document.
Set up the position described in the fenstring on the internal board and play the moves on the internal chess board. If the game was played from the start position the string "startpos" will be sent. Note: no "new" command is needed. However, if this position is from a different game than the last position sent to the engine, the GUI should have sent a "ucinewgame" in between.
-
move <move>Make a move using the syntax described in the Leiserchess_2023.pdf documentation.
-
goStart calculating on the current position set up with the "position" command. There are a number of commands that can follow this command, all will be sent in the same string. If one command is not sent its value should be interpreted as it would not influence the search.
-
time <x>Current player has x milliseconds left on the clock -
inc <x>Current player increment per move in milliseconds if x > 0. -
depth <x>Search x plies only.
-
-
perft [<N>]Compute the number of positions per ply up to ply
<N>(default value = 4). Used for debugging the move generator. -
displayOutput an ASCII graphic of the board position. Used interactively.
-
eval [<move>]Output the components of the NNUE evaluator on the position (default) or on the position after has been played. Used for debugging.
-
quitQuit the program as soon as possible.
-
id-
name <x>This must be sent after receiving the "uci" command to identify the engine, e.g. "id name Leiserchess 1038\n" -
author <x>This must be sent after receiving the "uci" command to identify the engine, e.g. "Don Dailey, Charles E. Leiserson, and the staff of MIT 6.172 Fall 2012-2013"
-
-
uciokMust be sent after the id and optional options to tell the GUI that the engine has sent all infos and is ready in uci mode.
-
readyokThis must be sent when the engine has received an "isready" command and has processed all input and is ready to accept new commands now. It is usually sent after a command that can take some time to be able to wait for the engine, but it can be used anytime, even when the engine is searching, and must always be answered with "isready".
-
bestmove <move>The engine has stopped searching and found the move
<move>best in this position. -
infoThe engine wants to send information. This can be done whenever an info has changed. The engine can send only selected infos or multiple infos with one info command,
e.g. "info depth 12 nodes 123456 nps 100000".
Also, all infos belonging to the pv should be sent together,
e.g. "info depth 2 score cp 214 time 1242 nodes 2124 nps 34928 pv e4R j4f5 b3L"
Additional info:
-
depth <x>Search depth in plies.
-
time <x>The time searched in microseconds, this should be sent together with the pv.
-
nodes <x><x>nodes searched, the engine should send this info regularly. -
pv <move1> ... <movei>The best line found.
-
score cp <x>The score from the engine's point of view in centipawns.
-
string <str>Any string str which will be displayed by the engine, if there is a string command the rest of the line will be interpreted as .
-
-
optionThis command tells the GUI which parameters can be changed in the engine. This should be sent once at engine startup after the "uci" and the "id" commands if any parameter can be changed in the engine.
The GUI should parse this and build a dialogue for the user to change the settings.
If the user wants to change some settings, the GUI can send a "setoption" command to the engine. Note that the GUI need not send the setoption command when starting the engine for every option if it doesn't want to change the default value.
For all allowed combinations see the examples below, as some combinations of these tokens don't make sense. One string will be sent for each parameter.
-
name <id>The option has the name id. -
type <t>The option has type t. There are 5 different types of options the engine can send:
-
checka checkbox that can either be true or false -
spina spin wheel that can be an integer in a certain range -
comboa combo box that can have different predefined strings as a value -
buttona button that can be pressed to send a command to the engine -
stringa text field that has a string as a value, an empty string has the value ""
-
-
default <x>the default value of this parameter is x -
min <x>the minimum value of this parameter is x -
max <x>the maximum value of this parameter is x -
var <x>a predefined value of this parameter is x
Examples:
Here are 6 strings for each of the 5 possible types of options:
-
"option name use nullmove type check default true\n"
"option name selectivity R factor type spin default 2 min 0 max 4\n"
"option name Style type combo default Normal var Solid var Normal var Risky\n"
"option name Book Path type string default c:\\n"
"option name Clear Hash type button\n"
Note: This options interface is only partially obeyed by the current Leiserchess engine.
Here is an example where indented lines are messages from the engine and blank lines are just for clarity. I have added comments after a semicolon:
uci
id name Leiserchess 1038
id author Don Dailey, Charles E. Leiserson, and the staff of MIT 6.172 Fall 2012-2013
option name hattack type spin value 200 default 200 min 0 max 10000
option name attack type spin value 100 default 100 min 0 max 10000
option name pmirror type spin value 500 default 500 min 0 max 10000
...
uciok
isready ; like a ping command
readyok ; engine responds
position startpos moves h0g1 b3R ; no response required
go depth 5 ; begin a 5 ply search (but don't execute move on board)
info depth 1 move_no 1 time (microsec) 158 nodes 89 nps 563141
info score cp -30 pv b2b3
info depth 1 move_no 3 time (microsec) 252 nodes 179 nps 707742
info score cp 61 pv h1h2
info depth 1 move_no 8 time (microsec) 476 nodes 272 nps 571228
info score cp 167 pv a1b2
info depth 2 move_no 1 time (microsec) 6905 nodes 1187 nps 171903
info score cp 0 pv a1b2 h6g5
info depth 3 move_no 1 time (microsec) 38518 nodes 21429 nps 556333
info score cp 124 pv a1b2 h6g5 h1g0
info depth 4 move_no 1 time (microsec) 93260 nodes 50900 nps 545780
info score cp 45 pv a1b2 h6g5 h1R a5b6
info depth 5 move_no 1 time (microsec) 700082 nodes 243533 nps 347863
info score cp 31 pv a1b2 h6g5 h1g0 a6R c3b3
bestmove a1b2
Once you start modifying the player code, you will want to check and make sure that your modification is indeed an improvement, and this is where the autotester comes into play. We recommend that you set up your Makefile so that you can easily compile different versions of the players easily (with different binary names). Once you have your player binaries, you can evaluate your changes by playing the newer player binary against the older one using the autotester framework. Be sure that you are not running anything else computationally intensive when running the autotester for accurate results. The autotester also acts as a referee, ensuring that both engines are making valid moves.
The autotester takes in a configuration file, specifying the parameter for running the autotester. Detailed instructions for writing a configuration file are in tester/README. We have provided a basic configuration file for you, in tests/basic.txt.
When you run the leiserchess binary directly without the web GUI, leiserchess launches into an "infinite" loop, accepting commands based on the UCI specification. You can type 'help' to see possible commands supported by the leiserchess UCI. The UCI interface is purely text based, so you can debug your player program easily using a terminal. Anything you print in the player code base gets printed out to the terminal window in which you run leiserchess.
NOTE: You should rename the player from 'leiserchess' to something else (both the binary and what's printed by the 'uci' command) once you start modifying the player to help you keep track of different versions of your bot.