lithander wrote: ↑Thu Feb 17, 2022 12:40 pm
eligolf wrote: ↑Thu Feb 17, 2022 12:07 pm
Should I try to make the move generation and make/unmake move functions faster than this before continuing with next phases of incorporating the engine itself? Or is this good enough in C# to continue?
My "I don't really care about the speed, I just want to learn alorithms of chess programming" engine does ~4M nps on perft with a mailbox board representation. (array with 64 slots of type piece)
My bitboard engine where I really do care about speed does 60-70M nps in perft with a pseudo-legal, bitboard based move generator.
Both engines are in C#. So I'd say try and see if you can't squeeze some extra performance out of it or at least try and find out why your perft is currently so much slower than it could be.
That is great, 4 M nps sounds amazing with that approach.
I am using pseudo legal moves and in the end of the MakeMove function I check if the move left king in check, and if it did I UnMake the move. Like this:
Code: Select all
public bool MakeMove(ulong move)
{
// Extract variables from move
int fromSquare = (int)Move.GetFromSquare(move);
int toSquare = (int)Move.GetToSquare(move);
int pieceMoved = (int)Move.GetPieceMoved(move);
int moveFlag = (int)Move.GetMoveFlag(move);
int pieceType = PieceTable[fromSquare];
int enemyColor = Color.Invert(ColorToMove);
// Increase counters
if (ColorToMove == Color.White) MovesCount++;
// Add variables to track lists
Castlings.Add(Castling);
Hashes.Add(Hash);
PawnHashes.Add(PawnHash);
EnPassants.Add(EnPassant);
IrreversibleMovesCounts.Add(IrreversibleMovesCount);
// Reset irreversible moves count if it is such a move
if (pieceType == Piece.Pawn || Move.IsCapture(moveFlag) || Move.IsCastling(moveFlag))
{
IrreversibleMovesCount = 0;
}
else IrreversibleMovesCount++;
// Check if enpassant is possible
if (EnPassant != GameConstants.EmptyBitboard)
{
int enPassantRank = BitOperations.BitScan(EnPassant) % 8; // -1 ??? ;
EnPassant = GameConstants.EmptyBitboard;
}
// ----- Check for different move types ------
// Quiet moves that are not double pushes
if (Move.IsSinglePush(moveFlag))
{
MovePiece(ColorToMove, pieceType, fromSquare, toSquare);
}
// Double pawn pushes
else if (Move.IsDoublePush(moveFlag))
{
MovePiece(ColorToMove, pieceType, fromSquare, toSquare);
// Enable enpassant
ulong epSquare = ColorToMove == Color.White ? 1ul << toSquare + 8 : 1ul << fromSquare + 8;
EnPassant |= epSquare;
}
// Enpassant moves
else if (Move.IsEnPassant(moveFlag))
{
// Find enemy pawn (which is not on the toSquare)
int enemyPieceSquare = ColorToMove == Color.White ? (byte)(toSquare + 8) : (byte)(toSquare - 8);
int capturedPiece = PieceTable[enemyPieceSquare];
CapturedPieces.Add(capturedPiece);
// Move own pawn and remove enemy pawn
MovePiece(ColorToMove, pieceType, fromSquare, toSquare);
RemovePiece(enemyColor, capturedPiece, enemyPieceSquare);
}
// Capture moves
else if (Move.IsCapture(moveFlag))
{
// Find the captured piece and remove it from the board
int capturedPiece = PieceTable[toSquare];
RemovePiece(enemyColor, capturedPiece, toSquare);
// Check if captured piece is a rook, if so change castling rights accordingly
if (capturedPiece == Piece.Rook)
{
// Black queen side castling
if (toSquare == 0)
{
Castling &= ~Castling.BlackQueenSide;
}
// Black king side castling
else if (toSquare == 7)
{
Castling &= ~Castling.BlackKingSide;
}
// White queen side castling
else if (toSquare == 56)
{
Castling &= ~Castling.WhiteQueenSide;
}
// White king side castling
else if (toSquare == 63)
{
Castling &= ~Castling.WhiteKingSide;
}
}
// Check for promotion captures
if (Move.IsPromotion(moveFlag))
{
// Find the piece that we should promote to
int promotionPiece = GetPromotionPiece(moveFlag);
// Remove pawn from square and add the promoted piece on
RemovePiece(ColorToMove, pieceType, fromSquare);
AddPiece(ColorToMove, promotionPiece, toSquare);
// Add to list
PromotedPieces.Add(promotionPiece);
}
// If not it is a normal capture
else
{
// Move the piece as normal
MovePiece(ColorToMove, pieceType, fromSquare, toSquare);
}
// Add captured piece to list of captured pieces
CapturedPieces.Add(capturedPiece);
}
// Castling moves
else if (Move.IsCastling(moveFlag))
{
// White to move
if (ColorToMove == Color.White)
{
// Remove castling rights
Castling &= ~Castling.WhiteCastling;
// King side castling
if (Move.IsKingCastling(moveFlag))
{
// Move rook and king to the correct places
MovePiece(Color.White, Piece.King, 60, 62);
MovePiece(Color.White, Piece.Rook, 63, 61);
}
// Queen side castling
else
{
MovePiece(Color.White, Piece.King, 60, 58);
MovePiece(Color.White, Piece.Rook, 56, 59);
}
}
// Black to move
else
{
// Remove castling rights
Castling &= ~Castling.BlackCastling;
// King side castling
if (Move.IsKingCastling(moveFlag))
{
// Move rook and king to the correct places
MovePiece(Color.Black, Piece.King, 4, 6);
MovePiece(Color.Black, Piece.Rook, 7, 5);
}
// Queen side castling
else
{
MovePiece(Color.Black, Piece.King, 4, 2);
MovePiece(Color.Black, Piece.Rook, 0, 3);
}
}
// Set castling is done for the color who moved
CastlingDone[ColorToMove] = true;
}
// Normal promotion moves that are not captures
else if (Move.IsPromotion(moveFlag))
{
// Get what piece was promoted
int promotionPiece = GetPromotionPiece(moveFlag);
// Remove pawn from square and add promoted piece on toSquare
RemovePiece(ColorToMove, pieceType, fromSquare);
AddPiece(ColorToMove, promotionPiece, toSquare);
// Add piece to list
PromotedPieces.Add(promotionPiece);
}
// Check if we moved our king without castling, if so remove castling rights for corresponding color
if (pieceType == Piece.King && !Move.IsCastling(moveFlag))
{
if (ColorToMove == Color.White) Castling &= ~Castling.WhiteCastling;
else Castling &= ~Castling.BlackCastling;
}
// Check if we moved rook and we have castling rights left
else if (pieceType == Piece.Rook && Castling != 0)
{
if (fromSquare == 63)
{
Castling &= ~Castling.WhiteKingSide;
}
else if (fromSquare == 56)
{
Castling &= ~Castling.WhiteQueenSide;
}
else if (fromSquare == 7)
{
Castling &= ~Castling.BlackKingSide;
}
else if (fromSquare == 0)
{
Castling &= ~Castling.BlackQueenSide;
}
}
// Change turns
ColorToMove = enemyColor;
int oldColorToMove = Color.Invert(ColorToMove);
// Check if move is legal (king is not under attack after move is done)
int ownKingSquare = BitOperations.GetLSBIndex(Pieces[oldColorToMove][Piece.King]);
if (!IsSquareAttacked(oldColorToMove, ownKingSquare))
{
return true;
}
// If not, then unmake the move
else
{
UnmakeMove(move);
return false;
}
}
The structure itself is taken with great inspiration from Cosette. I know the castling checks can be faster but not sure how much difference that would make.
Is it reasonable to create legal moves instead with bitboard approach or is it standard to make the move and check for legality afterwards? Since that is such rare case and most moves will be pruned anyways.