#include "evaluate.hpp" #include "board/board.hpp" #include "moves.hpp" #include #include #include #include #include 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; static std::array PAWN_TABLE = { 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, // 5, 5, 10, 30, 30, 10, 5, 5, // 0, 0, 0, 25, 25, 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, // }; static std::array KNIGHT_TABLE = { -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, // }; static std::array BISHOP_TABLE = { -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, // }; static std::array ROOK_TABLE = { 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, // }; static std::array QUEEN_TABLE = { -20, -10, -10, -5, -5, -10, -10, -20, // -10, 0, 0, 0, 0, 0, 0, -10, // -10, 0, 5, 5, 5, 5, 0, -10, // -5, 0, 5, 5, 5, 5, 0, -5, // 0, 0, 5, 5, 5, 5, 0, -5, // -10, 5, 5, 5, 5, 5, 0, -10, // -10, 0, 5, 0, 0, 0, 0, -10, // -20, -10, -10, -5, -5, -10, -10, -20, // }; static std::array KING_TABLE_EARLY = { -30, -40, -40, -50, -50, -40, -40, -30, // -30, -40, -40, -50, -50, -40, -40, -30, // -30, -40, -40, -50, -50, -40, -40, -30, // -30, -40, -40, -50, -50, -40, -40, -30, // -20, -30, -30, -40, -40, -30, -30, -20, // -10, -20, -20, -20, -20, -20, -20, -10, // 20, 20, 0, 0, 0, 0, 20, 20, // 20, 30, 10, 0, 0, 10, 30, 20, // }; int EvaluateBoard(Game *g) { int score = EvaluateBoardForWhite(g); return g->turn ? score : score * -1; }; /* * i cound have come up with better name. * this function just takes square and if color is black rotate it */ static size_t RotateBoardForBlack(const size_t square, const bool color) { return !color ? square : (56 ^ square); } static int ForceKingToEdgeBonus(const Position enemyKing, const Position myKing) { int bonus = 0; // Push enemy king toward edge const int distToCenter = std::abs(enemyKing.file - 3) + std::abs(enemyKing.rank - 3); bonus += distToCenter * 10; // Bring own king closer const int kingDistance = std::abs(myKing.file - enemyKing.file) + std::abs(myKing.rank - enemyKing.rank); bonus += (14 - kingDistance) * 5; return bonus; } int CountBoardMaterial(Game *g) { int score = 0; uint64_t piece_bitboard = g->PieceBitboard; while (piece_bitboard != 0) { auto i = static_cast(__builtin_ctzll(piece_bitboard)); piece_bitboard &= piece_bitboard - 1; const Piece piece = g->pieces[i]; if (piece.type == NONEPIECE) { assert(false && "in piece bitboard found none piece"); std::cout << "Internal error\n"; exit(1); } int value = 0; switch (piece.type) { case PAWN: value = PAWN_VALUE; value += PAWN_TABLE[RotateBoardForBlack(i, piece.color)]; break; case KNIGHT: value = KNIGHT_VALUE; value += KNIGHT_TABLE[RotateBoardForBlack(i, piece.color)]; break; case BISHOP: value = BISHOP_VALUE; value += BISHOP_TABLE[RotateBoardForBlack(i, piece.color)]; break; case ROOK: value = ROOK_VALUE; value += ROOK_TABLE[RotateBoardForBlack(i, piece.color)]; break; case QUEEN: value = QUEEN_VALUE; value += QUEEN_TABLE[RotateBoardForBlack(i, piece.color)]; break; case KING: value = KING_TABLE_EARLY[RotateBoardForBlack(i, piece.color)]; break; default: break; } if (piece.color) { score += value; } else { score -= value; } } return score; }; bool IsEndgame(const Game *g) { int queens = 0; int minor = 0; for (const Piece &piece : g->pieces) { switch (piece.type) { case QUEEN: queens++; break; case BISHOP: case KNIGHT: case ROOK: minor++; break; default: break; } } return queens == 0 || (queens == 2 && minor <= 1); } int EvaluateBoardForWhite(Game *g) { g->state = GetNewGameState(g); switch (g->state) { case WHITE_WON: return MATE; case BLACK_WON: return -MATE; case DRAW: return 0; case TURN: break; } int score = 0; score += CountBoardMaterial(g); if (IsEndgame(g)) { Position whiteKing = FindKing(g, true); Position blackKing = FindKing(g, false); score += ForceKingToEdgeBonus(blackKing, whiteKing); score -= ForceKingToEdgeBonus(whiteKing, blackKing); } return score; }