diff options
| author | Adam <adammegarules1@gmail.com> | 2026-07-30 16:40:17 +0200 |
|---|---|---|
| committer | Adam <adammegarules1@gmail.com> | 2026-07-30 16:40:17 +0200 |
| commit | 8df9e390a71eeb2835eab06ec4b474c8cad63ecd (patch) | |
| tree | 4788195fc373baa22bb349f1532e62d3db9dc99b /src/bot.cpp | |
| parent | 72646d9beb20e338569d578fee38a37be06faabe (diff) | |
improving bot
Diffstat (limited to 'src/bot.cpp')
| -rw-r--r-- | src/bot.cpp | 173 |
1 files changed, 112 insertions, 61 deletions
diff --git a/src/bot.cpp b/src/bot.cpp index 9a489dd..f05c303 100644 --- a/src/bot.cpp +++ b/src/bot.cpp @@ -10,16 +10,17 @@ #include <iostream> #include <vector> -const int DEFAULT_DEPTH = 4; +constexpr int DEFAULT_DEPTH = 4; +constexpr int Q_DEPTH_LIMIT = 6; -const int PAWN_VALUE = 100; -const int KNIGHT_VALUE = 320; -const int BISHOP_VALUE = 400; -const int ROOK_VALUE = 500; -const int QUEEN_VALUE = 900; -const int MATE = 10000; +constexpr int PAWN_VALUE = 100; +constexpr int KNIGHT_VALUE = 320; +constexpr int BISHOP_VALUE = 400; +constexpr int ROOK_VALUE = 500; +constexpr int QUEEN_VALUE = 900; +constexpr int MATE = 10000; -const int PAWN_TABLE[64] = { +static int PAWN_TABLE[64] = { 0, 0, 0, 0, 0, 0, 0, 0, // last rank promotes to a quuen 50, 50, 50, 50, 50, 50, 50, 50, // comments to stop formating to ruin it 10, 10, 20, 35, 35, 20, 10, 10, // @@ -30,7 +31,7 @@ const int PAWN_TABLE[64] = { 0, 0, 0, 0, 0, 0, 0, 0 // }; -int KNIGHT_TABLE[64] = { +static int KNIGHT_TABLE[64] = { -50, -40, -30, -30, -30, -30, -40, -50, // -40, -20, 0, 0, 0, 0, -20, -40, // -30, 0, 10, 15, 15, 10, 0, -30, // @@ -41,7 +42,7 @@ int KNIGHT_TABLE[64] = { -50, -40, -30, -30, -30, -30, -40, -50, // }; -int BISHOP_TABLE[64] = { +static int BISHOP_TABLE[64] = { -20, -10, -10, -10, -10, -10, -10, -20, // -10, 5, 0, 0, 0, 0, 5, -10, // -10, 10, 10, 10, 10, 10, 10, -10, // @@ -52,7 +53,7 @@ int BISHOP_TABLE[64] = { -20, -10, -10, -10, -10, -10, -10, -20, // }; -int ROOK_TABLE[64] = { +static int ROOK_TABLE[64] = { 0, 0, 5, 10, 10, 5, 0, 0, // 5, 10, 10, 10, 10, 10, 10, 5, // -5, 0, 0, 0, 0, 0, 0, -5, // @@ -63,7 +64,7 @@ int ROOK_TABLE[64] = { 0, 0, 5, 10, 10, 5, 0, 0, // }; -int QUEEN_TABLE[64] = { +static int QUEEN_TABLE[64] = { -20, -10, -10, -5, -5, -10, -10, -20, // -10, 0, 0, 0, 0, 0, 0, -10, // -10, 0, 5, 5, 5, 5, 0, -10, // @@ -74,7 +75,7 @@ int QUEEN_TABLE[64] = { -20, -10, -10, -5, -5, -10, -10, -20, // }; -int KING_TABLE_EARLY[64] = { +static int KING_TABLE_EARLY[64] = { -30, -40, -40, -50, -50, -40, -40, -30, // -30, -40, -40, -50, -50, -40, -40, -30, // -30, -40, -40, -50, -50, -40, -40, -30, // @@ -88,8 +89,8 @@ int KING_TABLE_EARLY[64] = { int ScoreMove(const Game *board, const Move &move) { int score = 0; - Piece moving = board->pieces[PositionToIndex(move.From)]; - Piece captured = board->pieces[PositionToIndex(move.To)]; + const Piece moving = board->pieces[PositionToIndex(move.From)]; + const Piece captured = board->pieces[PositionToIndex(move.To)]; // Captures (MVV-LVA) if (captured.type != NONEPIECE) { @@ -118,32 +119,33 @@ int ScoreMove(const Game *board, const Move &move) { std::vector<Move> GetSortedLegalMoves(Game *g) { auto moves = GetLegalMoves(g); - if (moves.size() == 0) { + if (moves.empty()) { return moves; } - std::sort(moves.begin(), moves.end(), [&](const Move &a, const Move &c) { + std::ranges::sort(moves, [&](const Move &a, const Move &c) { return ScoreMove(g, a) > ScoreMove(g, c); }); return moves; } -Move EngineGetBestMove(Game *b, int depth) { - int usedDepth = (depth != -1 ? depth : DEFAULT_DEPTH); - auto moves = GetSortedLegalMoves(b); - if (moves.size() == 0) { +Move EngineGetBestMove(Game *b, const int depth) { + const int usedDepth = (depth != -1 ? depth : DEFAULT_DEPTH); + const auto moves = GetSortedLegalMoves(b); + if (moves.empty()) { std::cout << "Expected a position with legal moves"; - assert(false && "Unhanled error zero legal moves for bot"); + assert(false && "Error zero legal moves for bot"); exit(1); } Move bestMove = moves[0]; - float BestEval = (b->turn ? -INFINITY : INFINITY); - for (Move move : moves) { - UndoMove undo = MakeMove(move, b); - float alpha = -INFINITY; - float beta = INFINITY; + int BestEval = b->turn ? std::numeric_limits<int>::lowest() + : std::numeric_limits<int>::max(); + for (const Move move : moves) { + const UndoMove undo = MakeMove(move, b); + const float alpha = -INFINITY; + const float beta = INFINITY; - float eval = minimax(usedDepth - 1, b, alpha, beta); + const int eval = minimax(usedDepth - 1, b, alpha, beta); UnMakeMove(undo, b); if (b->turn) { @@ -160,11 +162,52 @@ Move EngineGetBestMove(Game *b, int depth) { } return bestMove; } -float minimax(int depth, Game *b, float alpha, float beta) { +int quiescenceSearch(Game *b, int Qdepth) { + int standPat = EvaluateBoardForWhite(b); + if (Qdepth >= Q_DEPTH_LIMIT) { + return standPat; + } + std::vector<Move> attackMoves; + for (Move move : GetSortedLegalMoves(b)) { + if (b->pieces[PositionToIndex(move.To)].type != NONEPIECE) { + attackMoves.push_back(move); + } + }; + if (attackMoves.empty()) { + return standPat; + } + + int bestEval = b->turn ? std::numeric_limits<int>::lowest() + : std::numeric_limits<int>::max(); + + if (b->turn) { + for (Move move : attackMoves) { + UndoMove undo = MakeMove(move, b); + + int eval = quiescenceSearch(b, Qdepth + 1); + + UnMakeMove(undo, b); + bestEval = std::max(bestEval, eval); + } + } else { + for (Move move : attackMoves) { + UndoMove undo = MakeMove(move, b); + + int eval = quiescenceSearch(b, Qdepth + 1); + + UnMakeMove(undo, b); + bestEval = std::min(bestEval, eval); + } + } + + return bestEval; +} + +int minimax(int depth, Game *b, float alpha, float beta) { uint64_t gameHash = GenerateZobristKey(b); if (b->ThreeFoldMap[gameHash] >= 2) { - return b->turn ? -500 : 500; + return 0; } if (b->Transpositions->contains(gameHash)) { TranspositionsEntry data = b->Transpositions->at(gameHash); @@ -173,24 +216,29 @@ float minimax(int depth, Game *b, float alpha, float beta) { } } + if (depth <= 0) { + return quiescenceSearch(b, 0); + } auto moves = GetSortedLegalMoves(b); - if (depth == 0 || moves.size() == 0) { - return EvaluateBoardForWhite(b); + if (moves.empty()) { + return quiescenceSearch(b, 0); } bool shouldStore = true; - float bestEval = b->turn ? -INFINITY : INFINITY; + int bestEval = b->turn ? std::numeric_limits<int>::lowest() + : std::numeric_limits<int>::max(); + if (b->turn) { for (Move move : moves) { UndoMove undo = MakeMove(move, b); - float eval = minimax(depth - 1, b, alpha, beta); + int eval = minimax(depth - 1, b, alpha, beta); UnMakeMove(undo, b); bestEval = std::max(bestEval, eval); - alpha = std::max(alpha, bestEval); + alpha = std::max(alpha, static_cast<float>(bestEval)); if (alpha >= beta) { shouldStore = false; @@ -201,12 +249,12 @@ float minimax(int depth, Game *b, float alpha, float beta) { for (Move move : moves) { UndoMove undo = MakeMove(move, b); - float eval = minimax(depth - 1, b, alpha, beta); + int eval = minimax(depth - 1, b, alpha, beta); UnMakeMove(undo, b); bestEval = std::min(bestEval, eval); - beta = std::min(beta, bestEval); + beta = std::min(beta, static_cast<float>(bestEval)); if (alpha >= beta) { shouldStore = false; break; // *snips* @@ -215,24 +263,29 @@ float minimax(int depth, Game *b, float alpha, float beta) { } if (shouldStore) { - b->Transpositions->operator[](gameHash) = {depth, bestEval}; + b->Transpositions->operator[](gameHash) = {.depth = depth, + .Eval = bestEval}; } return bestEval; } -int PSTIndex(int square, bool white) { return white ? square : (56 ^ square); } -static int ForceKingToEdgeBonus(Position enemyKing, Position myKing) { +static int PSTIndex(const int square, const bool white) { + return white ? square : (56 ^ square); +} + +static int ForceKingToEdgeBonus(const Position enemyKing, + const Position myKing) { int bonus = 0; // Push enemy king toward edge - int distToCenter = + const int distToCenter = std::abs(enemyKing.file - 3) + std::abs(enemyKing.rank - 3); bonus += distToCenter * 10; // Bring own king closer - int kingDistance = std::abs(myKing.file - enemyKing.file) + - std::abs(myKing.rank - enemyKing.rank); + const int kingDistance = std::abs(myKing.file - enemyKing.file) + + std::abs(myKing.rank - enemyKing.rank); bonus += (14 - kingDistance) * 5; @@ -242,8 +295,8 @@ static bool IsEndgame(Game *g) { int queens = 0; int rooks = 0; - for (int i = 0; i < 64; i++) { - switch (g->pieces[i].type) { + for (const Piece &piece : g->pieces) { + switch (piece.type) { case QUEEN: queens++; break; @@ -258,11 +311,10 @@ static bool IsEndgame(Game *g) { return queens == 0 || (queens == 2 && rooks <= 1); } -float EvaluateBoardForWhite(Game *g) { - float score = 0; +int EvaluateBoardForWhite(Game *g) { + int score = 0; - GameState state = GetNewGameState(g); - switch (state) { + switch (GetNewGameState(g)) { case WHITE_WON: return MATE; break; @@ -271,13 +323,11 @@ float EvaluateBoardForWhite(Game *g) { break; case TURN: break; - case STALEMATE: - return 0; case DRAW: return 0; } for (int i = 0; i < 64; i++) { - Piece piece = g->pieces[i]; + const Piece piece = g->pieces[i]; if (piece.type == NONEPIECE) continue; @@ -316,15 +366,16 @@ float EvaluateBoardForWhite(Game *g) { } else { score -= value; } - Position whiteKing = FindKing(g, true); - Position blackKing = FindKing(g, false); - - if (IsEndgame(g)) { - score += - ForceKingToEdgeBonus(blackKing, whiteKing); // White attacking black - score -= - ForceKingToEdgeBonus(whiteKing, blackKing); // Black attacking white - } + } + + Position whiteKing = FindKing(g, true); + Position blackKing = FindKing(g, false); + + if (IsEndgame(g)) { + score += + ForceKingToEdgeBonus(blackKing, whiteKing); // White attacking black + score -= + ForceKingToEdgeBonus(whiteKing, blackKing); // Black attacking white } return score; } |
