I'm doing this little write up for two reasons. One, it would have been useful to me to have an idea of what it takes to go from Standard to FRC/Chess960, so perhaps this may have some value to others Two, I generated what I think is a reasonable (and large) set of PERFT tests which others may use in the future.
As for Ethereal, as I write this I am doing the finishing touches on verifying PERFT, as well as playing games against master (non FRC) in order to measure the slow down. Currently the elo loss is around 1 to 2, played at 10+.1s, which I think is quite respectable for the overhead that FRC involves. I'm always scared to push things like this, since something may break no matter how careful I am, but I plan to push this before Version 11.50. Hopefully if there are any bugs, they appear to me before the proper release of V11.50. If you would like to play around with the tentative FRC support, I can always provide binaries.
#1 Move Encoding
In this process I think I came across the answer to the question "Why does Stockfish encode castle moves as King captures Rook". I chose a different route, but I'll write a bit about both.
The benefit, or the reason, that Stockfish encodes using KxR is because at many points in the engine you will need to be able to determine if a given move is a king side or queen side castle. That may sound trivial -- in fact until I was nearly done implementing it I thought I could simply take the starting square of the king and compare it to the ending square. But then you run into the caveat for FRC which is that the King might not even move during the castle. By encoding as King captures Rook, figuring out which side you are castling to is trivial.
I decided instead to set one of the extra bits in the uint16_t that represents moves in Ethereal. That bit says whether or not the castle is king side or queen side. Technically, the bits were open to use, but this was a bit of a hack, and to anyone starting from the ground I would recommend the Stockfish paradigm. One setback to my method is that additional getlsb() & getmsb() calls are needed to locate the rook you are castling to. But as I said above, the elo loss (read: speed loss) is quite minimal, so ... c'est la vie
#2 Apply and Reverting Moves
Assuming you have a working engine for Standard chess, I only have one idea to add here which tripped me up. Namely, the king and rook might not be moved during a given castle move. Additionally, the king may move to the rook's square, or the rook may move to the king's square. This threw out a few assumptions I had in Ethereal. Namely:
For reverting a move, I would do something like this:
Code: Select all
board->squares[from] = board->squares[to]; board->squares[to] = EMPTY
Code: Select all
board->pieces[KING] ^= (1ull << from) ^ (1ull << to)
Thankfully, this issue is easy to spot. If pieces disappear, especially the King, something is going to crash ASAP
#3 FEN parsing
This one is fairly simple, but Lucas gave me a really good starting point for this. I can see how someone could come up with a very contrived way of doing this. Below is my code for parsing the Castle Rights portion of the FEN. For Ethereal, board->castleRooks is an uint64_t, with a bit set for each rook that has the potential to be castled with. The use of getmsb and getlsb, to play around with king vs queen side appears in many places for me after adding FRC support. This parser handles FEN, X-FEN, and SHREDDER-FEN representations. Also, in Ethereal, bit 0 is A1, and bit 7 is H1, hence the corresponding getlsb() & getmsb() calls.
Code: Select all
while ((ch = *token++)) {
if (ch == 'K') setBit(&board->castleRooks, getmsb(white & rooks & RANK_1));
if (ch == 'Q') setBit(&board->castleRooks, getlsb(white & rooks & RANK_1));
if (ch == 'k') setBit(&board->castleRooks, getmsb(black & rooks & RANK_8));
if (ch == 'q') setBit(&board->castleRooks, getlsb(black & rooks & RANK_8));
if ('A' <= ch && ch <= 'H') setBit(&board->castleRooks, square(0, ch - 'A'));
if ('a' <= ch && ch <= 'h') setBit(&board->castleRooks, square(7, ch - 'a'));
}
If you already have a working engine, the nice starting point is to rewrite your move generation and application functions to be generalized. It should not be too hard to get this done and still have the same game-play from your engine in Standard mode. Once you have that framework, you can start playing with FRC positions which will slowly introduce all the corner cases to deal with.
For PERFT testing, I took all 960 of the positions, piped them into Stockfish and had Stockfish play 8 moves at random. This gives a good spread of opening positions, with castle moves possible without too great a depth. Then I used Stockfish to generate a depth 6 PERFT value. The final test suite can be found here ( If this link dies, then the suite will still exist but just on the master branch of Ethereal )
Additionally I wrote the following in order to automate running those positions. If your engine is xboard, or you have a more proper (read: pedantic) implement ion of UCI then tweaks are needed. ( Again, if this link dies, then this file will still exist but just on the master branch of Ethereal )