#include "bot.hpp" #include "board.hpp" #include "moves.hpp" #include #include #include int PAWN_VALUE = 100; int KNIGHT_VALUE = 200; int BISHOP_VALUE = 300; int ROOK_VALUE = 400; int QUEEN_VALUE = 900; int CHECK = 20; int MATE = 10000; 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}; const int SEARCH_DEPTH = 3; #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); Game TestBoard = *b; PlayMove(move, &TestBoard); float alpha = -INFINITY; float beta = INFINITY; float eval = minimax(SEARCH_DEPTH - 1, &TestBoard, alpha, beta); 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); } auto moves = GetLegalMoves(b); if (moves.size() == 0) { return EvaluateBoardForWhite(b); } if (b->turn) { float bestEval = -INFINITY; for (Move move : moves) { Game next = *b; PlayMove(move, &next); float eval = minimax(depth - 1, &next, alpha, beta); bestEval = std::max(bestEval, eval); alpha = std::max(alpha, bestEval); if (alpha >= beta) { break; } } return bestEval; } else { float BestEval = INFINITY; for (Move move : moves) { Game next = *b; PlayMove(move, &next); float eval = minimax(depth - 1, &next, alpha, beta); 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) { float score = 0; bool isStaleMate = false; GetLegalMoves(b); if (b->state == WHITE_WON) { return MATE; } if (b->state == BLACK_WON) { return -MATE; } 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; break; case KNIGHT: value = KNIGHT_VALUE; value += KNIGHT_TABLE[PSTIndex(i, piece.color)]; break; case BISHOP: value = BISHOP_VALUE; break; case ROOK: value = ROOK_VALUE; 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; }