#include #include #include #include #include #include #include #include "board.hpp" #include "moves.hpp" #include "zobrist.hpp" int PositionToIndex(Position i) { return (i.rank * 8) + i.file; } uint16_t CreateMove(uint8_t from, uint8_t to, PieceType promotion) { if (from >= 64 || to >= 64) { std::cout << "expected a valid chess piece position 0-63"; assert(false); } uint16_t move = 0; bool firstPromotionFlag = false; bool secondPromotionFlag = false; switch (promotion) { case NONEPIECE: case KNIGHT: firstPromotionFlag = false; secondPromotionFlag = false; break; case QUEEN: firstPromotionFlag = true; secondPromotionFlag = false; break; case ROOK: firstPromotionFlag = false; secondPromotionFlag = true; break; case BISHOP: firstPromotionFlag = true; secondPromotionFlag = true; break; default: std::cout << "unexpected promotion type"; assert(false); exit(1); } if (firstPromotionFlag) { move |= (1 << 1); } if (secondPromotionFlag) { move |= (1 << 0); } move |= static_cast(from << 2); move |= static_cast(to << 8); return move; }; PieceType getPromotionTypeFromMove(uint16_t move) { bool firstFlag = (move & (1 << 1)) != 0; bool secondFlag = (move & (1 << 0)) != 0; /* * 00 for knight * 10 for queen * 01 for rook * 11 for bishop */ if (!firstFlag && !secondFlag) { // 00 return KNIGHT; } if (firstFlag && !secondFlag) { // 10 return QUEEN; } if (!firstFlag && secondFlag) { // 01 return ROOK; } if (firstFlag && secondFlag) { // 11 return BISHOP; } std::cout << "should have never happend"; assert(false); return ROOK; }; uint8_t getFromValueFromMove(uint16_t move) { return (move >> 2) & 63; }; uint8_t getToValueFromMove(uint16_t move) { return (move >> 8) & 63; }; Position FindKing(Game *g, bool color) { return IndexToPosition(__builtin_ctzll(g->PieceBitboards[color][KING])); } bool isRepetionDraw(uint64_t key, Game *g) { int size = static_cast(g->history.size()); if (size < 4) { return false; // draw is imposible if less that 4 moves were played } int start = size - g->halfMoveClock; if (start < 0) { return false; } // start can only be smaller than zero in position from fen where // the history is not recorded int repetions = 0; for (int i = start; i < size; i++) { uint64_t move = g->history[static_cast(i)]; if (move == key) { repetions++; } } return repetions >= 3; }; Undo MakeMove(uint16_t move, Game *g) { uint8_t fromSquare = getFromValueFromMove(move); uint8_t toSquare = getToValueFromMove(move); PieceType promotion = getPromotionTypeFromMove(move); Undo undo = {}; undo.from = fromSquare; undo.to = toSquare; undo.movedPiece = g->pieces[fromSquare]; undo.capturedPiece = g->pieces[toSquare]; undo.oldTurn = g->turn; undo.oldCanEnpassant = g->canEnpassant; undo.oldEnPassant = g->enPassant; undo.oldHalfMoveClock = g->halfMoveClock; undo.oldMoveClock = g->MoveClock; undo.oldState = g->state; undo.OldWhiteCastleKing = g->whiteCastleKing; undo.OldWhiteCastleQueen = g->whiteCastleQueen; undo.OldBlackCastleKing = g->blackCastleKing; undo.OldBlackCastleQueen = g->blackCastleQueen; // play move Piece piece = g->pieces[fromSquare]; Piece piece2 = g->pieces[toSquare]; if (piece2.type != NONEPIECE) { if (piece2.color == g->turn) { assert(false && "capturing friendly piece error"); } } // clock g->MoveClock++; if (piece.type == PAWN || piece2.type != NONEPIECE) { g->halfMoveClock = 0; } else { g->halfMoveClock++; } if (g->halfMoveClock >= 100) { g->state = DRAW; } // Playing enpasstant if (piece.type == PAWN && g->canEnpassant && IndexToPosition(toSquare) == g->enPassant) { Position capturedPawn = IndexToPosition(toSquare); capturedPawn.rank += piece.color ? -1 : 1; undo.wasEnPassantCapture = true; undo.enPassantCapturedSquare = capturedPawn; undo.enPassantCapturedPiece = g->pieces[PositionToIndex(capturedPawn)]; bool captuaredColor = !piece.color; uint64_t square = 1ULL << PositionToIndex(capturedPawn); g->PieceBitboards[captuaredColor][PAWN] ^= square; if (captuaredColor) g->WhitePieceBitboard ^= square; else g->BlackPieceBitboard ^= square; g->PieceBitboard = g->WhitePieceBitboard | g->BlackPieceBitboard; g->pieces[PositionToIndex(capturedPawn)] = {false, NONEPIECE}; } g->canEnpassant = false; // Adding enpassant if (piece.type == PAWN) { int to = toSquare; // check if a pawn has moved two squares to -= piece.color ? 2 * 8 : -2 * 8; if (to == fromSquare) { g->canEnpassant = true; int target = fromSquare; target += piece.color ? 8 : -8; g->enPassant = IndexToPosition(target); }; } // promotion is always set and should only be aplied when reached final rank // (default: knight) if (piece.type == PAWN && IndexToPosition(toSquare).rank == (piece.color ? 7 : 0)) { piece.type = promotion; } // caslte if (piece.type == KING && IndexToPosition(fromSquare).rank == (piece.color ? 0 : 7) && std::abs(toSquare - fromSquare) == 2) { undo.wasCastle = true; undo.CastledSide = toSquare > fromSquare; if (undo.CastledSide) { Position rookFrom = IndexToPosition(fromSquare); rookFrom.file = 7; Position rookTo = IndexToPosition(toSquare); rookTo.file = 5; uint64_t from = 1ULL << PositionToIndex(rookFrom); uint64_t to = 1ULL << PositionToIndex(rookTo); g->PieceBitboards[piece.color][ROOK] ^= from; g->PieceBitboards[piece.color][ROOK] ^= to; if (piece.color) g->WhitePieceBitboard ^= from | to; else g->BlackPieceBitboard ^= from | to; g->PieceBitboard = g->WhitePieceBitboard | g->BlackPieceBitboard; g->pieces[PositionToIndex(rookTo)] = g->pieces[PositionToIndex(rookFrom)]; g->pieces[PositionToIndex(rookFrom)] = {false, NONEPIECE}; } else { // Queenside: a -> d Position rookFrom = IndexToPosition(fromSquare); rookFrom.file = 0; Position rookTo = IndexToPosition(toSquare); rookTo.file = 3; uint64_t from = 1ULL << PositionToIndex(rookFrom); uint64_t to = 1ULL << PositionToIndex(rookTo); g->PieceBitboards[piece.color][ROOK] ^= from; g->PieceBitboards[piece.color][ROOK] ^= to; if (piece.color) g->WhitePieceBitboard ^= from | to; else g->BlackPieceBitboard ^= from | to; g->PieceBitboard = g->WhitePieceBitboard | g->BlackPieceBitboard; g->pieces[PositionToIndex(rookTo)] = g->pieces[PositionToIndex(rookFrom)]; g->pieces[PositionToIndex(rookFrom)] = {false, NONEPIECE}; } } /* * remove castling right if king has moved * but only for color of the king */ if (piece.type == KING) { if (piece.color) { g->whiteCastleKing = false; g->whiteCastleQueen = false; } else { g->blackCastleKing = false; g->blackCastleQueen = false; } } // if rook has moved if (piece.type == ROOK) { if (piece.color) { // White if (fromSquare == 0) { // a1 g->whiteCastleQueen = false; } if (fromSquare == 7) { // h1 g->whiteCastleKing = false; } } else { // Black if (fromSquare == 7 * 8) { // a8 g->blackCastleQueen = false; } if (fromSquare == 7 * 9) { // h8 g->blackCastleKing = false; } } } // this part is written with ai if (piece2.type == ROOK) { if (piece2.color) { // White rook captured if (toSquare == 0) { // a1 g->whiteCastleQueen = false; } if (toSquare == 7) { // h1 g->whiteCastleKing = false; } } else { // Black rook captured if (toSquare == 7 * 8) { // a8 g->blackCastleQueen = false; } if (toSquare == 7 * 9) { // h8 g->blackCastleKing = false; } } } // playing the moves uint64_t from = 1ULL << fromSquare; uint64_t to = 1ULL << toSquare; // remove moving piece from source g->PieceBitboards[piece.color][undo.movedPiece.type] ^= from; // remove captured piece if (piece2.type != NONEPIECE) { g->PieceBitboards[piece2.color][piece2.type] ^= to; if (piece2.color) g->WhitePieceBitboard ^= to; else g->BlackPieceBitboard ^= to; } // add moving piece to destination g->PieceBitboards[piece.color][piece.type] ^= to; if (piece.color) { g->WhitePieceBitboard ^= from | to; } else { g->BlackPieceBitboard ^= from | to; } g->PieceBitboard = g->WhitePieceBitboard | g->BlackPieceBitboard; g->pieces[toSquare] = piece; g->pieces[fromSquare] = {.color = false, .type = NONEPIECE}; // changing who turn it is g->turn = !g->turn; // 3 fold check uint64_t key = GenerateZobristKey(g); undo.ZobristKey = key; g->history.push_back(key); return undo; }; void UndoMove(Undo undo, Game *g) { g->pieces[undo.from] = undo.movedPiece; g->pieces[undo.to] = undo.capturedPiece; if (undo.wasEnPassantCapture) { g->pieces[PositionToIndex(undo.enPassantCapturedSquare)] = undo.enPassantCapturedPiece; } g->turn = undo.oldTurn; g->canEnpassant = undo.oldCanEnpassant; g->enPassant = undo.oldEnPassant; g->halfMoveClock = undo.oldHalfMoveClock; g->MoveClock = undo.oldMoveClock; g->state = undo.oldState; g->whiteCastleKing = undo.OldWhiteCastleKing; g->whiteCastleQueen = undo.OldWhiteCastleQueen; g->blackCastleKing = undo.OldBlackCastleKing; g->blackCastleQueen = undo.OldBlackCastleQueen; if (undo.wasCastle) { if (undo.CastledSide) { // f -> h Position rookFrom = IndexToPosition(undo.from); rookFrom.file = 5; Position rookTo = IndexToPosition(undo.from); rookTo.file = 7; g->pieces[PositionToIndex(rookTo)] = g->pieces[PositionToIndex(rookFrom)]; g->pieces[PositionToIndex(rookFrom)] = { .color = false, .type = NONEPIECE, }; } else { // d -> a Position rookFrom = IndexToPosition(undo.from); rookFrom.file = 3; Position rookTo = IndexToPosition(undo.from); rookTo.file = 0; g->pieces[PositionToIndex(rookTo)] = g->pieces[PositionToIndex(rookFrom)]; g->pieces[PositionToIndex(rookFrom)] = { .color = false, .type = NONEPIECE, }; } } g->history.pop_back(); UpdateHelpers(g); }; void UpdateHelpers(Game *g) { g->PieceBitboard = 0; g->WhitePieceBitboard = 0; g->BlackPieceBitboard = 0; std::memset(g->PieceBitboards, 0, sizeof(g->PieceBitboards)); for (uint8_t i = 0; i < 64; i++) { Piece piece = g->pieces[i]; if (piece.type == NONEPIECE) { continue; } g->PieceBitboards[piece.color][piece.type] |= (1ULL << i); if (piece.color) { g->WhitePieceBitboard |= (1ULL << i); } else { g->BlackPieceBitboard |= (1ULL << i); } } g->PieceBitboard = g->WhitePieceBitboard | g->BlackPieceBitboard; }