Well, rather fixing castling, I suppose. There were quite a few problems with castling in my JustChess engine. In fact, it was so bad, I probably should have removed the function altogether until I fixed it. However, to God be the glory, it is fixed now. You can download the latest and greatest version on F-Droid (hopefully soon) or here directly:
https://gitlab.com/alaskalinuxuser/app_JustChess/-/tree/master/app?ref_type=heads
Castling caused three main problems in the original code that I wrote.
- Castling would often replace a rook with an opponent’s king.
- Castling would magically move your king somewhere else, but make him invisible.
- Castling could happen even if the king moved, or allow the king to be captured by the enemy.
That’s actually a big problem! Most of these were discussed in these two issues on the issue tracker:
https://gitlab.com/alaskalinuxuser/app_JustChess/-/issues/12
https://gitlab.com/alaskalinuxuser/app_JustChess/-/issues/13
The first issue, replacing a rook with your opponents king was actually the hardest to figure out. I started by adding debugging lines into each function to see when the board replaced the rook with the king, and then had to play through numerous games. It was tedious and time consuming. What I found was that the problem was not in the kingmoves/bkingmoves function, nor in the allmoves function, nor in the engine at all.
Actually, the problem was in the main activity, where after making a move, I wanted to ask the engine to list all available moves to see if there was a checkmate or stalemate condition. For this I should have called the terminal command of:
moveOptions= terminal("availMoves,black");
But instead I called:
moveOptions= terminal("suggestMove,black");
The suggest move option is a rated move suggestion, singular, based on ply and various evaluation criteria. The available moves option is a simple request of all currently available moves for that color. Thus, rather than getting a list of moves to check for stalemate or checkmate, it was getting a string about a specific suggested move. Ultimately, this should still have worked, except the last move function would get confused (due to my poor programming skills) about which move was really the last move, leading to a pretty bad return which resulted in switching the rook at A1 (the first square of the board, on the left bottom, numbered 00 in the engine) with a king piece of the color of the last person to castle!
While it took me forever to figure this out, the fix took about 30 seconds. I just changed the command from suggestMove to availMoves.
The second issue, moving the king and making him invisible, well, let’s just say I was not doing a great job at checking the king’s safety. And I’m not sure it put him back where he was supposed to be. Essentially, king safety during castling was always returning true, till it wasn’t. So you could capture him, or he might disappear, or some other unexpected error. It’s a lot of code that you can check out in this commit:
https://gitlab.com/alaskalinuxuser/app_JustChess/-/commit/f85839de525297712c10941b0e98ea538344fafe
And finally, the third issue was that the king or rooks could move and then still castle, as long as they were back at their original square. This was really some sloppy work on my part. I put in code to check if the kings or rooks moved, but didn’t call it into question before castling. So the engine knew that they moved, but didn’t realize that was a criteria for castling. So I added some statements like this:
if (wKingNeverMove == 0) {
// and
if (theBoard[7] == 'R' && theBoard[5] == '*' && theBoard[6] == '*' && wKRNeverMove == 0)
// and
if (theBoard[0] == 'R' && theBoard[1] == '*' &&
theBoard[2] == '*' && theBoard[3] == '*' && wQRNeverMove == 0)
And so on for the black pieces as well. All the variable were there and in place, but the castling rules were not checking them before allowing/suggesting the move. Fortunately it now does check this, and the game play is much smoother.
Linux – keep it simple.