From 21de797d664577b741646ca5239f2e861364ee84 Mon Sep 17 00:00:00 2001 From: MrGeorgen Date: Sun, 24 Mar 2024 16:53:44 +0100 Subject: [PATCH] evaluation --- include/chess/move.h | 2 +- include/chess/print.h | 1 + src/chess/evaluate.c | 100 ++++++++++++++++++++++++++++++++++++++++++ src/chess/evaluate.h | 8 ++++ src/chess/main.c | 4 +- src/chess/move.c | 35 +++++++++++---- src/common/print.c | 5 +++ 7 files changed, 144 insertions(+), 11 deletions(-) create mode 100644 src/chess/evaluate.c create mode 100644 src/chess/evaluate.h diff --git a/include/chess/move.h b/include/chess/move.h index a251430..f212c26 100644 --- a/include/chess/move.h +++ b/include/chess/move.h @@ -26,7 +26,7 @@ uint_least8_t pieceValidMoves(struct gameState_t gameState, struct piece_t piece void undoMove(uint_least64_t *board, struct move_t move, bool color); struct gameState_t makeMove(struct gameState_t gameState, struct move_t move); -struct gameState_t computerMove(struct gameState_t gameState); void initMagicTable(); +bool kingInCheck(const uint_least64_t *board, const bool color); #endif diff --git a/include/chess/print.h b/include/chess/print.h index ce72321..4063fa6 100644 --- a/include/chess/print.h +++ b/include/chess/print.h @@ -28,3 +28,4 @@ void printerull(FILE *file, unsigned long long num); void printPieceMask(uint_least64_t mask); void fieldToString(uint_least8_t field, char *output); void printMove(const struct move_t move); +void printMoveln(const struct move_t move); diff --git a/src/chess/evaluate.c b/src/chess/evaluate.c new file mode 100644 index 0000000..41cc3f0 --- /dev/null +++ b/src/chess/evaluate.c @@ -0,0 +1,100 @@ +#include "chess/move.h" +#include "chess/types.h" +#include +#include +#include +#include + +// These min and max values will not overflow, when negated +#define INT_16_SAFE_MIN (INT_LEAST16_MIN + 1) +#define INT_16_SAFE_MAX (INT_LEAST16_MAX - 1) + +#ifdef __GNUC__ // Check if using GCC or compatible compiler +static int_least16_t countOnes(uint_least64_t num) { + return __builtin_popcountll(num); // Use GCC built-in function if available +} +#elif defined(_MSC_VER) // Check if using Microsoft Visual C++ +#include +static int_least16_t countOnes(uint_least64_t num) { + return __popcnt64(num); // Use MSVC intrinsic if available +} +#else +static int_least16_t countOnes(uint_least64_t num) { + int_least16_t count = 0; + while(num) { + count += num & 1; + num >>= 1; + } + return count; +} +#endif + +static int_least16_t evaluate(const struct gameState_t gameState) { + int_least16_t value = 0; + const uint_least64_t *board = gameState.board; + int_least16_t pieceValues[PIECES_LENGTH]; + pieceValues[PAWN] = 100; + pieceValues[KNIGHT] = 300; + pieceValues[BISHOP] = 310; + pieceValues[QUEEN] = 900; + pieceValues[ROOK] = 500; + + for(uint_least8_t pieceType = QUEEN; pieceType < PIECES_LENGTH; ++pieceType) { + for(uint_least8_t color = BLACK; color <= WHITE; ++color) { + const struct piece_t piece = {pieceType, color}; + const int_least16_t sign = color == gameState.color ? 1 : -1; + value += sign * countOnes(bitboardGetMask(board, piece)) * pieceValues[pieceType]; + } + } + return value; +} + +static int_least16_t alphaBeta(const struct gameState_t gameState, int_fast16_t alpha, + int_least16_t beta, uint_least8_t depth) { + if(depth == 0) return evaluate(gameState); + struct move_t moves[MAX_VALID_MOVES]; + uint_least8_t movesLength = validMoves(gameState, moves); + if(movesLength == 0) { + if(kingInCheck(gameState.board, gameState.color)) { + return INT_16_SAFE_MIN + 1; + } + return 0; + } + for(uint_least8_t i = 0; i < movesLength; ++i) { + const struct move_t move = moves[i]; + const struct gameState_t newGameState = makeMove(gameState, move); + const int_least16_t score = -alphaBeta(newGameState, -beta, -alpha, depth - 1); + undoMove(gameState.board, move, gameState.color); + if(score >= beta) { + return beta; // beta-cutoff + } + if(score > alpha) { + alpha = score; // alpha acts like max in MiniMax + } + } + return alpha; +} + +static struct move_t searchRoot(const struct gameState_t gameState, uint_least8_t depth) { + struct move_t moves[MAX_VALID_MOVES]; + uint_least8_t movesLength = validMoves(gameState, moves); + int_least16_t alpha = INT_16_SAFE_MIN; + int_least16_t beta = INT_16_SAFE_MAX; + struct move_t selectedMove; + for(uint_least8_t i = 0; i < movesLength; ++i) { + const struct move_t move = moves[i]; + const struct gameState_t newGameState = makeMove(gameState, move); + const int_least16_t score = -alphaBeta(newGameState, -beta, -alpha, depth - 1); + undoMove(gameState.board, move, gameState.color); + if(score > alpha) { + alpha = score; // alpha acts like max in MiniMax + selectedMove = move; + } + } + return selectedMove; +} + +struct move_t bestMove(const struct gameState_t gameState) { + const uint_least8_t depth = 6; + return searchRoot(gameState, depth); +} diff --git a/src/chess/evaluate.h b/src/chess/evaluate.h new file mode 100644 index 0000000..161c82f --- /dev/null +++ b/src/chess/evaluate.h @@ -0,0 +1,8 @@ +#ifndef CHESS_EVALUATE_H +#define CHESS_EVALUATE_H + +#include + +struct move_t bestMove(const struct gameState_t gameState); + +#endif diff --git a/src/chess/main.c b/src/chess/main.c index b2aa665..e8e4973 100644 --- a/src/chess/main.c +++ b/src/chess/main.c @@ -9,6 +9,7 @@ #include #include #include +#include "evaluate.h" #define drawPiece(file, rank, piece) do { \ const RsvgRectangle rect = {xOffset + (file) * fieldSize, yOffset + (rank) * fieldSize, fieldSize, fieldSize};\ @@ -49,7 +50,8 @@ static struct gameState_t userMove(struct gameState_t gameState, struct move_t m #ifdef NO_COMPUTER turn = !turn; #else - gameState = computerMove(gameState); + move = bestMove(gameState); + gameState = makeMove(gameState, move); #endif return gameState; } diff --git a/src/chess/move.c b/src/chess/move.c index 6c8249d..e31b624 100644 --- a/src/chess/move.c +++ b/src/chess/move.c @@ -10,7 +10,9 @@ #include "magicNumber.h" #include -#define trailingBits(num) __builtin_ctzll(num) +#ifdef _MSC_VER +#include +#endif struct addMoveCtx_t { struct gameState_t gameState; @@ -20,6 +22,23 @@ struct addMoveCtx_t { const static uint_least8_t MAIN_DIRECTION[] = {NORTH, WEST, EAST, SOUTH}; +static uint_least8_t trailingBits(uint_least64_t num) { +#ifdef __GNUC__ + return __builtin_ctzll(num); +#elif defined(_MSC_VER) + unsigned long i; + _BitScanForward(&i, num); + return i; +#elif + uint_least8_t count = 0; + while ((num & 1) == 0) { // Keep shifting num right until the least significant bit is 1 + num >>= 1; + count++; + } + return count; +#endif +} + static uint_least8_t getDirectionOffset(uint_least8_t field, uint_least8_t direction) { defineDirectionOffset; return DIRECTION_OFFSET[field * DIRECTION_LENGTH + direction]; @@ -109,6 +128,11 @@ static bool inCheck(const uint_least64_t *board, const uint_least64_t kingMask, checkMagic(board, queen, rookMagic, kingMask) || checkMagic(board, queen, bishopMagic, kingMask); } +bool kingInCheck(const uint_least64_t *board, const bool color) { + const struct piece_t king = {KING, color}; + return inCheck(board, bitboardGetMask(board, king), color); +} + static uint_least8_t getCastleRankI(bool color) { if(color == WHITE) { return (BOARD_SIZE - 1) * BOARD_SIZE; @@ -163,8 +187,7 @@ static void addMove(struct addMoveCtx_t ctx, struct piece_t movedPiece, uint_lea makeMove(ctx.gameState, move); { const bool color = ctx.gameState.color; - const struct piece_t king = {KING, color}; - if(!inCheck(board, bitboardGetMask(board, king), color)) { + if(!kingInCheck(board, color)) { ctx.moves[*movesLength] = move; ++*movesLength; } @@ -360,9 +383,3 @@ struct gameState_t makeMove(struct gameState_t gameState, struct move_t move) { gameState.color = !gameState.color; return gameState; } - -struct gameState_t computerMove(struct gameState_t gameState) { - struct move_t moves[MAX_VALID_MOVES]; - validMoves(gameState, moves); - return makeMove(gameState, moves[0]); -} diff --git a/src/common/print.c b/src/common/print.c index 20240c0..5ef5128 100644 --- a/src/common/print.c +++ b/src/common/print.c @@ -53,3 +53,8 @@ void printMove(const struct move_t move) { printf("%c", piece); } } + +void printMoveln(const struct move_t move) { + printMove(move); + printf("\n"); +}