#include "bot.hpp" #include "board.hpp" #include "moves.hpp" #include #include #include int PAWN_VALUE = 100; int KNIGHT_VALUE = 320; int BISHOP_VALUE = 330; int ROOK_VALUE = 500; int QUEEN_VALUE = 900; int CHECK = 10; int MATE = 10000; const 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, 30, 30, 20, 10, 10, // 5, 5, 10, 25, 25, 10, 5, 5, // 0, 0, 0, 20, 20, 0, 0, 0, // 5, -5, -10, 0, 0, -10, -5, 5, // 5, 10, 10, -20, -20, 10, 10, 5, // 0, 0, 0, 0, 0, 0, 0, 0 // }; 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, // -30, 5, 15, 20, 20, 15, 5, -30, // -30, 0, 15, 20, 20, 15, 0, -30, // -30, 5, 10, 15, 15, 10, 5, -30, // -40, -20, 0, 5, 5, 0, -20, -40, // -50, -40, -30, -30, -30, -30, -40, -50, // }; 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, // -10, 0, 10, 15, 15, 10, 0, -10, // -10, 5, 5, 10, 10, 5, 5, -10, // -10, 0, 5, 10, 10, 5, 0, -10, // -10, 0, 0, 0, 0, 0, 0, -10, // -20, -10, -10, -10, -10, -10, -10, -20, // }; 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, // -5, 0, 0, 5, 5, 0, 0, -5, // -5, 0, 0, 5, 5, 0, 0, -5, // -5, 0, 0, 0, 0, 0, 0, -5, // 5, 10, 10, 10, 10, 10, 10, 5, // 0, 0, 5, 10, 10, 5, 0, 0, // }; const int SEARCH_DEPTH = 5; #include void LogMove(const Move &move) { std::ofstream file("moves.txt", std::ios::app); if (!file.is_open()) return; file << static_cast('a' + move.From.file) << static_cast('8' - move.From.rank) << static_cast('a' + move.To.file) << static_cast('8' - move.To.rank); file << '\n'; } Move EngineGetBestMove(Game *b) { auto moves = GetLegalMoves(b); if (moves.size() == 0) { assert(false && "Unhanled error zero legal moves for bot"); } Move bestMove = moves[0]; float BestEval = (b->turn ? -INFINITY : INFINITY); for (Move move : moves) { LogMove(move); UndoMove undo = MakeMove(move, b); float alpha = -INFINITY; float beta = INFINITY; float eval = minimax(SEARCH_DEPTH - 1, b, alpha, beta); UnMakeMove(undo, b); if (b->turn) { if (eval > BestEval) { BestEval = eval; bestMove = move; } } else { if (eval < BestEval) { BestEval = eval; bestMove = move; } } } return bestMove; } float minimax(int depth, Game *b, float alpha, float beta) { if (depth == 0) { return EvaluateBoardForWhite(b, depth); } auto moves = GetLegalMoves(b); if (moves.size() == 0) { return EvaluateBoardForWhite(b, depth); } if (b->turn) { float bestEval = -INFINITY; for (Move move : moves) { UndoMove undo = MakeMove(move, b); float eval = minimax(depth - 1, b, alpha, beta); UnMakeMove(undo, b); bestEval = std::max(bestEval, eval); alpha = std::max(alpha, bestEval); if (alpha >= beta) { break; } } return bestEval; } else { float BestEval = INFINITY; for (Move move : moves) { UndoMove undo = MakeMove(move, b); float eval = minimax(depth - 1, b, alpha, beta); UnMakeMove(undo, b); BestEval = std::min(BestEval, eval); beta = std::min(beta, BestEval); if (alpha >= beta) { break; // *snips* } } return BestEval; } } int PSTIndex(int square, bool white) { return white ? square : (56 ^ square); } float EvaluateBoardForWhite(Game *b, int depth) { float score = 0; bool isStaleMate = false; GetLegalMoves(b); if (b->state == WHITE_WON) { return MATE + depth; } if (b->state == BLACK_WON) { return -MATE - depth; } if (b->state == STALEMATE || b->state == DRAW) { isStaleMate = true; } for (int i = 0; i < 64; i++) { Piece piece = b->pieces[i]; if (piece.type == NONE) continue; int value = 0; switch (piece.type) { case PAWN: value = PAWN_VALUE; value += PAWN_TABLE[PSTIndex(i, piece.color)]; break; case KNIGHT: value = KNIGHT_VALUE; value += KNIGHT_TABLE[PSTIndex(i, piece.color)]; break; case BISHOP: value = BISHOP_VALUE; value += BISHOP_TABLE[PSTIndex(i, piece.color)]; break; case ROOK: value = ROOK_VALUE; value += ROOK_TABLE[PSTIndex(i, piece.color)]; break; case QUEEN: value = QUEEN_VALUE; break; default: break; } if (piece.color) score += value; else score -= value; } if (IsPieceTypeAttacked(b, KING, true)) score -= CHECK; // white king attacked if (IsPieceTypeAttacked(b, KING, false)) score += CHECK; // black king attacked if (isStaleMate) { return 0; } return score; }