Piece Movement Part 4: Special Moves
- Sam Lench
- 2 days ago
- 3 min read
Now that we are using A Better Way to Represent Chess Moves, with 16-bits, lets use this to generate the special moves. Using that way of move encode is essential to the way I do things as we will use the MoveFlags to help us here.
Castling
Castling is a unique move because is means moving two pieces, the king and the rook, at the same time. To do this, we only need to store the kings MoveFlag as castling and then account for moving the rook aswell inside of the engines move function.
We are only using Pseudo-Legal moves right now, so we dont take into account the kings safety. Because of this, we only care about two things.
Does the player have the right to castle.
Are the squares betwene the king and rook empty.
Generating Castling Moves
To generate castling moves we need to check
//example for White King-Side Castling
if ((CastlingRights & Castling.WhiteKing) != Castling.None)
{
//bitboard mask for squares f1 and g1
ulong emptySquaresMask = (1UL << 5) | (1UL << 6);
//if no pieces are on those squares
if ((_allPiecesBB & emptySquaresMask) == 0)
{
//add the move to a moves list
moves.Add(new Move(4, 6, MoveFlag.CastleKingSide));
}
}En Passant
En passant is known to be a bit of a tricky special move to inplement. It is only possible on the next move of a double pawn push and the piece the taking pawn moves to isnt actually where the captured pawn is. So how do we deal with these problems?
Our 16-bit move encoding is great at this. When the engine processes a double pawn push, we assign the flag as MoveFlag.PawnDoublePush.
We need to create an EnPassantMask which marks the file of which en passant is possible. To store the EnPassantMask we use a byte (8-bit number) with each bit representing a file. We don't need to store the colour here as we can just see whose turn it currently is. We can do this since En Passant is only available for the move directly after a double pawn push.
When the move function checks for the EnPassantFlag, we set the EnPassantMask to the correct file and handle the En Passant move. On the next move we need to clear this to make sure it's not possible for multiple moves. When executing the move for En Passant, we need to make sure to take the pawn behind where the target square is. This can be done easily by bit shifting by 8. The following code can eaily handle to offset of which to shift by.
int offset = _isWhiteTurn ? -8 : 8;Pawn Promotion
Finally the last special move is pawn promotions. If you've read the A Better Way to Represent Chess Moves blog post you will know how we handle this here.
1. Find all pawns that can promote.
ulong emptySquares = ~allPieces;
ulong singlePush = (pawns << 8) & emptySquares;
ulong promotionPush = singlePush & rank8Mask;2. Foreach pawn in that mask, generate 4 moves, 1 for each promotion type.
3. Assign the correct MoveFlag when generating the move.
movesList.Add(new Move(fromIndex, toIndex, MoveFlag.PromoteQueen));
movesList.Add(new Move(fromIndex, toIndex, MoveFlag.PromoteRook));
movesList.Add(new Move(fromIndex, toIndex, MoveFlag.PromoteBishop));
movesList.Add(new Move(fromIndex, toIndex, MoveFlag.PromoteKnight));*note we need to check if the promotion is also a capture or not to add the correct flag.
