MadChess 2.2 Released

I have released version 2.2 of my chess engine. Source code and Windows binaries are available on the Downloads page. MadChess 2.2 is about 50 Elo stronger than version 2.1. It includes the following improvements.

  • Added best move estimation. In principal variations where the cached position does not specify a best move, MadChess will search at reduced depth to obtain a best move. Then it completes the full depth search, searching the best move first. This is called Internal Iterative Deepening (IID) in other engines, though I find the name misleading. It’s recursive but not iterative.
  • Added endgame piece location constants to differentiate the value of pieces in the middlegame and endgame. Evaluation tuning found a significant difference only for rooks.
  • Modified the GetExchangeScore method to use standard 100, 300, 500, 900 piece material values rather than the tuned material values. This forces the engine to consider knights and bishops equal for the purpose of Static Exchange Evaluation (SEE).
  • Tuned evaluation parameters using the technique described by Peter Österlund.
  • Fixed a bug that prevented the evaluation from recognizing drawn endgames.
  • Added a help command that lists custom (non-standard UCI) commands.
  • Added a staticscore custom command that displays evaluation score details by color (white, black) and phase (middlegame, endgame). The scores include differences and totals to clearly indicate strengths and weaknesses of a position for each side.

As I write this post, MadChess is competing in a long time control gauntlet. Presently it’s rated 2574 +/- 69 Elo after 126 games. I’ve seen this show before. I expect its rating to decline as more games are played and the engine “regresses to the mean.” Nonetheless, I expect MadChess 2.2 to be rated > 2500 Elo in rapid chess. So now I can rightfully claim MadChess plays Grandmaster quality chess.

Update 2017 Jul 10: MadChess 2.2 completed the gauntlet tournament. It’s rated 2511 in rapid chess.

MadChess is written in a managed1 programming language, C#, using a mailbox2 board representation. Managed programming languages, such as C# or Java, feature high-level abstractions and trade performance for code clarity. Similarly, array board representations are easier to understand than bitboards- again trading speed for clarity. Despite these trade-offs, it’s possible to write a strong chess engine using a managed programming language and mailbox board representation, as I’ve demonstrated with MadChess.

How strong? I was curious how MadChess compares to other engines of similar technical design. I reviewed the engine list on Ron Murawski’s Computer Chess Wiki, which includes the programming language of each engine. I believe MadChess is the strongest managed, mailbox chess engine. Stronger chess engines have been written in managed languages, but they use bitboards. Lozza is a close 2nd, written- impressively- in Javascript.

Engine Programming Language Board Representation CCRL Rating
Carballo Java Bitboard 2727
NoraGrace C# Bitboard 2619
Cuckoo Chess Java Bitboard 2590
MadChess C# Mailbox 2511
Lozza Javascript Mailbox 2442
Absolute Zero C# Bitboard 2320
Mediocre Java Mailbox 2314
Fischerle Java Bitboard 2297
Dolphin C# Mailbox 2232
Cupcake Java Bitboard 2006
Chess4J Java Bitboard 1965
Deepov Java Bitboard 1844
Ziggy Java Mailbox 1709
SharpChess C# Mailbox 1674
Pulse Java Mailbox 1571
DarkFusch C# Bitboard 1172
jChecs Java Mailbox 1128

Of course this prompts a couple questions.

  • Should I rewrite MadChess using bitboards?
  • Should I rewrite MadChess using a more performance-capable programming language, such as C++?

I will ponder these questions. Regarding changing programming languages, I’m intrigued by the .NET Native and Core RT projects, which aim to compile C# to IL code, statically link .NET Framework IL code, cross-compile the IL code to C++, then use an optimizing C++ compiler to produce a single executable. If this technique can deliver the simplicity of C# with the performance of C++, I’ll have no reason to switch programming languages. I’m cautiously optimistic.


  1. So named because memory is automatically managed by the runtime and its garbage collector, not manually managed by the programmer.
  2. An array with each index representing a square, index values indicating an empty square or occupying piece. So named because the array representation resembles a mailbox commonly found in apartments.

Banks 64th Amateur Series Division 6

MadChess 2.1 participated in Graham Banks’ 64th amateur tournament in division 6.

                           1    2    3    4    5    6    7    8    9    0    1    2    
1   chess22k 1.3 64-bit    **** 11½1 1000 1½11 1100 ½111 11½0 0011 111½ 0101 011½ 0111  29.0/44
2   Gogobello 1.2 64-bit   00½0 **** 01½1 110½ 01½½ 01½½ ½½11 111½ 0111 111½ 1½½½ 1½11  28.5/44
3   Maverick 1.5 64-bit    0111 10½0 **** 0½10 ½1½1 011½ 0½0½ 1011 111½ ½111 1½01 001½  26.5/44
4   Dorky 4.5 64-bit       0½00 001½ 1½01 **** 1110 0½½½ 1110 ½½11 1½11 1½01 11½½ 0½½½  25.5/44
5   Betsabe II 1.66        0011 10½½ ½0½0 0001 **** 0½01 1101 110½ ½101 ½11½ 11½0 ½1½½  23.5/44
6   MadChess 2.1 64-bit    ½000 10½½ 100½ 1½½½ 1½10 **** 001½ 01½0 0010 01½½ ½½1½ 1½10  20.0/44  434.50
7   ECE-X2                 00½1 ½½00 1½1½ 0001 0010 110½ **** 1011 ½½00 0½½½ 1100 1½10  20.0/44  431.75
8   Nemeton 1.5            1100 000½ 0100 ½½00 001½ 10½1 0100 **** 0111 ½½10 1½1½ ½0½½  19.0/44  401.50
9   Lozza 1.17 64-bit      000½ 1000 000½ 0½00 ½010 1101 ½½11 1000 **** 0½10 1011 1½11  19.0/44  383.00
10  Schooner 1.7.0 64-bit  1010 000½ ½000 0½10 ½00½ 10½½ 1½½½ ½½01 1½01 **** ½010 110½  18.5/44
11  TJchess 1.3 64-bit     100½ 0½½½ 0½10 00½½ 00½1 ½½0½ 0011 0½0½ 0100 ½101 **** ½011  17.5/44
12  Bagatur 1.5c 64-bit    1000 0½00 110½ 1½½½ ½0½½ 0½01 0½01 ½1½½ 0½00 001½ ½100 ****  17.0/44

Games

Missed Opportunity in the Endgame

I’ve been playing chess against my engine recently, using a beautiful 23″ wooden board by Drueke, with my notebook PC off to the side.  I set the time control to 25 min + 10 sec / move and give myself an extra 5 minutes per game (more time than MadChess) to make the moves on the board and the PC.  I figure that’s fair- it averages to 5 seconds per move over 60 moves.

Since MadChess plays chess as well as an international master (slightly below a grand master), and I am nowhere near that strong, I handicapped it to play at 1300 Elo.  This is made possible by the UCI_LimitStrength algorithm I implemented in the engine.

25 + 10 is an enjoyable pace. It’s slow enough to think through each move, yet fast enough to complete a game in an hour.  After 48 moves (and numerous missed opportunities as post-game analysis revealed), I reach the following position.  Playing the black pieces, thinking I needed to move my king up the board to support my passed pawn, I played Kc5, a blunder.  Can you find the correct move for black?

Of course! I overlooked the move, thinking it lost material. It does, but only temporarily. The pawn will queen.

I am a better chess programmer than player.  I need to play more games and write less code.  I must say though, what a strange feeling is produced by playing a game against an artificial intelligence I have created, purposefully handicapping it to get an even game, then unleashing its full potential after the game is concluded so it may identify all the moments I erred.  A bit magical and eerie all at once.

The full game, with MadChess’ post-game analysis (at 10 seconds per move), is available by clicking the share icon in the diagram above (the rightmost icon).

Banks 63rd Amateur Series Division 6

MadChess 2.1 participated in Graham Banks’ 63rd amateur tournament in division 6.

                                  1    2    3    4    5    6    7    8    9    0    1    2    
1   CyberPagno 3.0 64-bit         **** 111½ 0½½½ 000½ ½11½ ½010 0011 ½101 1½01 1011 1110 1111  27.0/44
2   Maverick 1.5 64-bit           000½ **** 0½10 ½1½1 1100 ½½½1 00½1 1110 ½11½ ½0½½ 11½1 ½111  25.5/44
3   Dorky 4.5 64-bit              1½½½ 1½01 **** ½110 ½½½½ 0½00 1110 00½1 11½½ 0½½1 1111 ½½½0  25.0/44  539.50
4   ECE-X2                        111½ ½0½0 ½001 **** 0101 011½ 101½ ½01½ 1½½0 11½1 0½10 1½½1  25.0/44  536.25
5   Bagatur 1.4d 64-bit           ½00½ 0011 ½½½½ 1010 **** 1011 010½ ½00½ 0½11 1111 101½ 1011  24.5/44
6   Lozza 1.17 64-bit             ½101 ½½½0 1½11 100½ 0100 **** 0½½½ ½10½ ½1½½ ½010 0111 01½1  23.0/44  498.00
7   Betsabe II 1.66               1100 11½0 0001 010½ 101½ 1½½½ **** ½1½0 10½0 011½ ½0½1 11½½  23.0/44  494.75
8   MadChess 2.1 64-bit           ½010 0001 11½0 ½10½ ½11½ ½01½ ½0½1 **** ½½½0 ½½10 01½1 1½00  21.5/44
9   Nemeton 1.5                   0½10 ½00½ 00½½ 0½½1 1½00 ½0½½ 01½1 ½½½1 **** 1½1½ 1010 11½0  21.0/44
10  TJchess 1.3 64-bit            0100 ½1½½ 1½½0 00½0 0000 ½101 100½ ½½01 0½0½ **** 0½0½ ½011  16.5/44
11  Bumblebee 1.0.36898e1 64-bit  0001 00½0 0000 1½01 010½ 1000 ½1½0 10½0 0101 1½1½ **** ½0½0  16.0/44  347.75
12  Shallow r694 64-bit           0000 ½000 ½½½1 0½½0 0100 10½0 00½½ 0½11 00½1 ½100 ½1½1 ****  16.0/44  340.25

Games

MadChess 2.1 Released

I have released version 2.1 of my chess engine. I built this version of MadChess using .NET Core, Microsoft’s new cross-OS development platform. I have provided source code and binaries for Windows on the Downloads page.

I do not have access to a Linux or Mac machine. If you are feeling adventurous and would like to build Linux or Mac binaries, please refer to Getting Started with .NET Core on Windows / Linux / MacOS for instructions on how to build .NET Core applications.

I have configured the MadChess project to build a self-contained application. All binaries required to run MadChess are included in the ZIP file. Unlike previous versions of MadChess, no prerequisite .NET framework must be installed prior to running the engine.

MadChess 2.1 is about 60 or 70 Elo stronger than version 2.0. It includes the following improvements, some impacting playing strength, some more focused on code quality.

  • Converted cached position from a class to a ulong primitive with bitwise operations.
  • Tuned evaluation using the technique described by Peter Österlund.
  • Include static exchange score when determining move futility.
  • Update history heuristic value of previously played quiet moves that failed to produce a cutoff.
  • Extract principal variation from a triangular array (rather than from hash table).
  • Simplified time management.