From bc74fa50ab472b31c8bf13e5c24ad5c5c0fafe6e Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 13 Aug 2024 21:35:39 +0300 Subject: [PATCH 1/4] * Chess: Add a basic "engine" that performs random moves. --- apps/Chess/src/chess.c | 12 +++++++ apps/Chess/src/chess.h | 20 +++++++++--- apps/Chess/src/engine.c | 71 +++++++++++++++++++++++++++++++++++++++++ apps/Chess/src/main.c | 14 ++++++-- 4 files changed, 110 insertions(+), 7 deletions(-) create mode 100644 apps/Chess/src/engine.c diff --git a/apps/Chess/src/chess.c b/apps/Chess/src/chess.c index a8d9451..bfdc932 100644 --- a/apps/Chess/src/chess.c +++ b/apps/Chess/src/chess.c @@ -947,6 +947,18 @@ eErrorCode ChessCommitMove(int rowSrc, int colSrc, int rowDst, int colDst) } } +void PerformBestMove() +{ + BoardMove bm = FindBestMove(g_CurrentState); + + if (bm.rowSrc < 0) + return; + + eErrorCode ec = ChessCommitMove(bm.rowSrc, bm.colSrc, bm.rowDst, bm.colDst); + if (ec != ERROR_SUCCESS) + LogMsg("ERROR: couldn't perform move %d, %d -> %d, %d", bm.rowSrc, bm.colSrc, bm.rowDst, bm.colDst); +} + extern int g_nMoveNumber; void SetupBoard(BoardState* pState) { diff --git a/apps/Chess/src/chess.h b/apps/Chess/src/chess.h index b1a3b79..230f4fb 100644 --- a/apps/Chess/src/chess.h +++ b/apps/Chess/src/chess.h @@ -78,8 +78,8 @@ eColor; typedef struct { - eColor color; - ePiece piece; + eColor color : 2; + ePiece piece : 6; } BoardPiece; @@ -125,9 +125,16 @@ typedef struct } BoardState; -extern BoardState* g_CurrentState; - +typedef struct +{ + int rowSrc; + int colSrc; + int rowDst; + int colDst; +} +BoardMove; +extern BoardState* g_CurrentState; extern BoardPiece g_pieces[BOARD_SIZE][BOARD_SIZE]; extern Window* g_pWindow; @@ -160,4 +167,9 @@ void ClearFlashingTiles(); void ChessUpdateMoveList(); +void PerformBestMove(); + +// Engine +BoardMove FindBestMove(BoardState* pState); + #endif//CHESS_H diff --git a/apps/Chess/src/engine.c b/apps/Chess/src/engine.c new file mode 100644 index 0000000..fa522a3 --- /dev/null +++ b/apps/Chess/src/engine.c @@ -0,0 +1,71 @@ +#include "chess.h" + +typedef struct +{ + +} x; + +BoardMove FindBestMove(BoardState* pState) +{ + BoardState state; + +#define MAX_MOVES 256 + BoardMove* pMoves = calloc(MAX_MOVES, sizeof(BoardMove)); + int nMoves = 0; + + // backup the current state pointer, we'll modify itoa + BoardState * pCurrentState = g_CurrentState; + g_CurrentState = &state; + +#define ADD_MOVE(a, b, c, d) do { \ + if (nMoves == MAX_MOVES) { \ + LogMsg("ERROR: too many moves!"); \ + } else { \ + BoardMove move = { a, b, c, d }; \ + pMoves[nMoves++] = move; \ + } \ +} while (0) + + // Enumerate every possible move that the current player can make. + for (int i = 0; i < BOARD_SIZE * BOARD_SIZE; i++) + { + int iRow = i % BOARD_SIZE, iCol = i / BOARD_SIZE; + BoardPiece* pPiece = GetPiece(pState, iRow, iCol); + if (pPiece->color != pState->m_Player) + continue; + + // TODO: Add moves depending on the piece that's being moved. Don't try every move ever, in a dumb way. + for (int j = 0; j < BOARD_SIZE * BOARD_SIZE; j++) + { + int jRow = j % BOARD_SIZE, jCol = j / BOARD_SIZE; + if (i == j) continue; + + // there's a legal move. + // The explanation for this is: + // If the king is in check, then ChessCheckMove only returns true if the move would take the king out of check. + + UNUSED eCastleType castleType; + UNUSED bool bEnPassant; + if (ChessCheckMove(pState, iRow, iCol, jRow, jCol, &castleType, &bEnPassant, false) == ERROR_SUCCESS) + ADD_MOVE(iRow, iCol, jRow, jCol); + } + } + + free(pMoves); + + g_CurrentState = pCurrentState; + + // TODO: evaluate each one + + + if (nMoves == 0) + { + LogMsg("No moves available!"); + BoardMove bm = { -1, -1, -1, -1 }; + return bm; + } + + return pMoves[rand() % nMoves]; +} + + diff --git a/apps/Chess/src/main.c b/apps/Chess/src/main.c index 7d83074..3f51f77 100644 --- a/apps/Chess/src/main.c +++ b/apps/Chess/src/main.c @@ -496,15 +496,17 @@ void CALLBACK ChessWndProc (Window* pWindow, int messageType, long parm1, long p RECT(rect, 10, g_BoardY + (BOARD_SIZE * PIECE_SIZE / 2) + 5, 40, (BOARD_SIZE * PIECE_SIZE / 2) - 5); AddControl(pWindow, CONTROL_LISTVIEW, rect, NULL, CHESS_CAPTURES_BLACK, 0, 0); - int btnWidth = RIGHT_BAR_WIDTH / 2; + int btnWidth = RIGHT_BAR_WIDTH; RECT(rect, CHESS_WIDTH - btnWidth - 5, 10, btnWidth - 5, 20); - AddControl(pWindow, CONTROL_BUTTON, rect, "Resign", CHESS_RESIGN_BLACK, ICON_RESIGN, 0); + //AddControl(pWindow, CONTROL_BUTTON, rect, "Resign", CHESS_RESIGN_BLACK, ICON_RESIGN, 0); + AddControl(pWindow, CONTROL_BUTTON, rect, "Find Best Move", CHESS_RESIGN_BLACK, ICON_RESIGN, 0); RECT(rect, CHESS_WIDTH - btnWidth * 2 - 10, 10, btnWidth - 5, 20); AddControl(pWindow, CONTROL_BUTTON, rect, "Draw", CHESS_DRAW_BLACK, ICON_SCALE16, 0); SetControlDisabled(pWindow, CHESS_DRAW_BLACK, true); RECT(rect, CHESS_WIDTH - btnWidth - 5, CHESS_HEIGHT - 30, btnWidth - 5, 20); - AddControl(pWindow, CONTROL_BUTTON, rect, "Resign", CHESS_RESIGN_WHITE, ICON_RESIGN, 0); + //AddControl(pWindow, CONTROL_BUTTON, rect, "Resign", CHESS_RESIGN_WHITE, ICON_RESIGN, 0); + AddControl(pWindow, CONTROL_BUTTON, rect, "Find Best Move", CHESS_RESIGN_WHITE, ICON_RESIGN, 0); RECT(rect, CHESS_WIDTH - btnWidth * 2 - 10, CHESS_HEIGHT - 30, btnWidth - 5, 20); AddControl(pWindow, CONTROL_BUTTON, rect, "Draw", CHESS_DRAW_WHITE, ICON_SCALE16, 0); SetControlDisabled(pWindow, CHESS_DRAW_WHITE, true); @@ -542,10 +544,13 @@ void CALLBACK ChessWndProc (Window* pWindow, int messageType, long parm1, long p break; } + /* if (MessageBox(pWindow, "Are you sure you want to resign as white?", "Chess", ICON_QUES << 16 | MB_YESNO) == MBID_YES) { ChessOnGameEnd(ERROR_RESIGNATION_WHITE, BLACK); } + */ + PerformBestMove(); break; } case CHESS_RESIGN_BLACK: @@ -556,10 +561,13 @@ void CALLBACK ChessWndProc (Window* pWindow, int messageType, long parm1, long p break; } + /* if (MessageBox(pWindow, "Are you sure you want to resign as black?", "Chess", ICON_QUES << 16 | MB_YESNO) == MBID_YES) { ChessOnGameEnd(ERROR_RESIGNATION_BLACK, WHITE); } + */ + PerformBestMove(); break; } } From 9ceaaf3d2fc7d0af78ecbd0a04896c4ebffab364 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 13 Aug 2024 21:43:34 +0300 Subject: [PATCH 2/4] * Chess: Clean up random engine a little bit. --- apps/Chess/src/engine.c | 77 ++++++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 20 deletions(-) diff --git a/apps/Chess/src/engine.c b/apps/Chess/src/engine.c index fa522a3..3aab3ac 100644 --- a/apps/Chess/src/engine.c +++ b/apps/Chess/src/engine.c @@ -1,22 +1,37 @@ #include "chess.h" -typedef struct -{ - -} x; +const int nPieceValues[] = { + 0, // None + 999, // King + 9, // Queen + 3, // Bishop + 3, // Knight + 5, // Rook + 1, // Pawn +}; -BoardMove FindBestMove(BoardState* pState) +int EvaluateBoardState(BoardState* pState) { - BoardState state; + int State = 0; -#define MAX_MOVES 256 - BoardMove* pMoves = calloc(MAX_MOVES, sizeof(BoardMove)); - int nMoves = 0; + for (int i = 0; i < BOARD_SIZE * BOARD_SIZE; i++) + { + int iRow = i % BOARD_SIZE, iCol = i / BOARD_SIZE; + BoardPiece* pPiece = GetPiece(pState, iRow, iCol); + + int value = nPieceValues[pPiece->piece]; + if (pPiece->color == BLACK) + value = -value; + + State += value; + } - // backup the current state pointer, we'll modify itoa - BoardState * pCurrentState = g_CurrentState; - g_CurrentState = &state; + return State; +} +#define MAX_MOVES 256 +BoardMove* GetAllMoves(BoardState* pState, int* outMoveCount) +{ #define ADD_MOVE(a, b, c, d) do { \ if (nMoves == MAX_MOVES) { \ LogMsg("ERROR: too many moves!"); \ @@ -26,6 +41,14 @@ BoardMove FindBestMove(BoardState* pState) } \ } while (0) + // backup the current state pointer, we'll modify it + BoardState state; + BoardState * pOldCurrentState = g_CurrentState; + g_CurrentState = &state; + + int nMoves = 0; + BoardMove* pMoves = calloc(MAX_MOVES, sizeof(BoardMove)); + // Enumerate every possible move that the current player can make. for (int i = 0; i < BOARD_SIZE * BOARD_SIZE; i++) { @@ -51,21 +74,35 @@ BoardMove FindBestMove(BoardState* pState) } } - free(pMoves); + g_CurrentState = pOldCurrentState; - g_CurrentState = pCurrentState; + *outMoveCount = nMoves; + return pMoves; +} + +int NegativeMax(BoardState* pState, int Depth) +{ + if (Depth == 0) + return EvaluateBoardState(pState); - // TODO: evaluate each one + return 0; +} + +BoardMove FindBestMove(BoardState* pState) +{ + BoardMove* pMoves = NULL; + int nMoves = 0; + pMoves = GetAllMoves(pState, &nMoves); + BoardMove bm = { -1, -1, -1, -1 }; if (nMoves == 0) - { LogMsg("No moves available!"); - BoardMove bm = { -1, -1, -1, -1 }; - return bm; - } + else + bm = pMoves[rand() % nMoves]; - return pMoves[rand() % nMoves]; + free(pMoves); + return bm; } From 14fc3fbf37ce551621818a094f4a837638176b07 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 13 Aug 2024 23:29:50 +0300 Subject: [PATCH 3/4] * Chess: Slightly smarter engine. Ever so slightly. * CommonMakefile: Add OPTIMIZATIONS overridable field --- apps/Chess/Makefile | 1 + apps/Chess/src/chess.c | 48 +++++----- apps/Chess/src/chess.h | 2 +- apps/Chess/src/engine.c | 196 ++++++++++++++++++++++++++++++++++------ apps/Chess/src/main.c | 2 +- apps/CommonMakefile | 4 +- 6 files changed, 197 insertions(+), 56 deletions(-) diff --git a/apps/Chess/Makefile b/apps/Chess/Makefile index d362617..fe16903 100644 --- a/apps/Chess/Makefile +++ b/apps/Chess/Makefile @@ -5,5 +5,6 @@ SRC_DIR=src APP_C_FILES=$(shell find $(SRC_DIR) -type f -name '*.c') APP_S_FILES=$(shell find $(SRC_DIR) -type f -name '*.asm') USERCFLAGS=-Wno-switch +OPTIMIZATIONS = -O3 include ../CommonMakefile diff --git a/apps/Chess/src/chess.c b/apps/Chess/src/chess.c index bfdc932..0f8fd40 100644 --- a/apps/Chess/src/chess.c +++ b/apps/Chess/src/chess.c @@ -862,10 +862,8 @@ void ChessGeneratePGN(char* pgnOut, BoardState* pState, int rowSrc, int colSrc, strcpy(pgnOut, moveList); } -eErrorCode ChessCommitMove(int rowSrc, int colSrc, int rowDst, int colDst) +eErrorCode ChessCommitMove(BoardState* pState, int rowSrc, int colSrc, int rowDst, int colDst) { - BoardState* pState = g_CurrentState; - BoardPiece* pcSrc = GetPiece(pState, rowSrc, colSrc); eColor col = pcSrc->color; @@ -881,29 +879,31 @@ eErrorCode ChessCommitMove(int rowSrc, int colSrc, int rowDst, int colDst) if (errCode != ERROR_SUCCESS) return errCode; - if (g_CurrentState != &g_History[g_HistorySize - 1]) + if (g_CurrentState == pState) { - if (g_CurrentState < g_History || g_History + g_HistorySize <= g_CurrentState) + if (g_CurrentState != &g_History[g_HistorySize - 1]) { - return ERROR_CANT_OVERWRITE_HISTORY; - } - // Show a message box asking whether the user wants to overwrite the history. - if (ChessMessageBox("Performing this move will overwrite the move history.\n\nDo you wish to perform the move?", "Chess", MB_YESNO | ICON_WARNING << 16) != MBID_YES) - { - return ERROR_CANT_OVERWRITE_HISTORY; + if (g_CurrentState < g_History || g_History + g_HistorySize <= g_CurrentState) + { + return ERROR_CANT_OVERWRITE_HISTORY; + } + // Show a message box asking whether the user wants to overwrite the history. + if (ChessMessageBox("Performing this move will overwrite the move history.\n\nDo you wish to perform the move?", "Chess", MB_YESNO | ICON_WARNING << 16) != MBID_YES) + { + return ERROR_CANT_OVERWRITE_HISTORY; + } + + // this ptr diff should be fine, since we already check the bounds... + g_HistorySize = (int)(g_CurrentState - g_History) + 1; } - // this ptr diff should be fine, since we already check the bounds... - g_HistorySize = (int)(g_CurrentState - g_History) + 1; + // commit the move!! + AddHistoryFrame(); + + pState = g_CurrentState; } - // commit the move!! - AddHistoryFrame(); - - pState = g_CurrentState; - pState->m_PlayerState[pcSrc->color].m_nEnPassantColumn = -1; - pState->m_PlayerState[pcSrc->color].m_bKingInCheck = false; bool bCapture = false; @@ -933,9 +933,11 @@ eErrorCode ChessCommitMove(int rowSrc, int colSrc, int rowDst, int colDst) // fill in the move struct MoveInfo* pmi = &pState->m_MoveInfo; - ChessGeneratePGN(pmi->pgn, pState, rowSrc, colSrc, rowDst, colDst, castleType, bCheck, bCapture, bEnPassant, mateType); - - ChessUpdateMoveList(); + if (g_CurrentState == pState) + { + ChessGeneratePGN(pmi->pgn, pState, rowSrc, colSrc, rowDst, colDst, castleType, bCheck, bCapture, bEnPassant, mateType); + ChessUpdateMoveList(); + } pState->m_Player = GetNextPlayer(pState->m_Player); @@ -954,7 +956,7 @@ void PerformBestMove() if (bm.rowSrc < 0) return; - eErrorCode ec = ChessCommitMove(bm.rowSrc, bm.colSrc, bm.rowDst, bm.colDst); + eErrorCode ec = ChessCommitMove(g_CurrentState, bm.rowSrc, bm.colSrc, bm.rowDst, bm.colDst); if (ec != ERROR_SUCCESS) LogMsg("ERROR: couldn't perform move %d, %d -> %d, %d", bm.rowSrc, bm.colSrc, bm.rowDst, bm.colDst); } diff --git a/apps/Chess/src/chess.h b/apps/Chess/src/chess.h index 230f4fb..775bf16 100644 --- a/apps/Chess/src/chess.h +++ b/apps/Chess/src/chess.h @@ -138,7 +138,7 @@ extern BoardState* g_CurrentState; extern BoardPiece g_pieces[BOARD_SIZE][BOARD_SIZE]; extern Window* g_pWindow; -eErrorCode ChessCommitMove(int rowSrc, int colSrc, int rowDst, int colDst); +eErrorCode ChessCommitMove(BoardState* pState, int rowSrc, int colSrc, int rowDst, int colDst); eErrorCode ChessCheckMove(BoardState* pState, int rowSrc, int colSrc, int rowDst, int colDst, eCastleType* castleType, bool * bWouldDoEP, bool bFlashTiles); // Check if a color is in checkmate or stalemate. diff --git a/apps/Chess/src/engine.c b/apps/Chess/src/engine.c index 3aab3ac..f1d3d6c 100644 --- a/apps/Chess/src/engine.c +++ b/apps/Chess/src/engine.c @@ -1,32 +1,119 @@ #include "chess.h" +#define INF (1000000) + +const int g_InitialDepth = 4; + const int nPieceValues[] = { - 0, // None - 999, // King - 9, // Queen - 3, // Bishop - 3, // Knight - 5, // Rook - 1, // Pawn + 0, // None + 99999, // King + 900, // Queen + 300, // Bishop + 300, // Knight + 500, // Rook + 100, // Pawn +}; + +// NOTE: I pulled these values out of my own ass. + +const char nKingValueMult[] = { + -50,-40,-30,-20,-20,-30,-40,-50, + -30,-20,-10, 0, 0,-10,-20,-30, + -30,-10, 20, 30, 30, 20,-10,-30, + -30,-10, 30, 40, 40, 30,-10,-30, + -30,-10, 30, 40, 40, 30,-10,-30, + -30,-10, 20, 30, 30, 20,-10,-30, + -30,-30, 0, 0, 0, 0,-30,-30, + -50,-30,-30,-30,-30,-30,-30,-50 +}; + +const char nRookValueMult[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 5, 10, 10, 10, 10, 10, 10, 5, + -5, 0, 0, 0, 0, 0, 0, -5, + -5, 0, 0, 0, 0, 0, 0, -5, + -5, 0, 0, 0, 0, 0, 0, -5, + -5, 0, 0, 0, 0, 0, 0, -5, + -5, 0, 0, 0, 0, 0, 0, -5, + 0, 0, 0, 5, 5, 0, 0, 0 +}; + +const char nBishopValueMult[] = { + -20,-10,-10,-10,-10,-10,-10,-20, + -10, 0, 0, 0, 0, 0, 0,-10, + -10, 0, 5, 10, 10, 5, 0,-10, + -10, 5, 5, 10, 10, 5, 5,-10, + -10, 0, 10, 10, 10, 10, 0,-10, + -10, 10, 10, 10, 10, 10, 10,-10, + -10, 5, 0, 0, 0, 0, 5,-10, + -20,-10,-10,-10,-10,-10,-10,-20 +}; + +const char nKnightValueMult[] = { + -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 char nQueenValueMult[] = { + -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 +}; + +const char nPawnValueMult[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 50, 50, 50, 50, 50, 50, 50, 50, + 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 +}; + +const char* const nValueMultPiece[] = { + NULL, // None + nKingValueMult, // King, + nQueenValueMult, // Queen + nBishopValueMult, // Bishop + nKnightValueMult, // Knight + nRookValueMult, // Rook + nPawnValueMult, // Pawn }; int EvaluateBoardState(BoardState* pState) { - int State = 0; + int eval = 0; for (int i = 0; i < BOARD_SIZE * BOARD_SIZE; i++) { int iRow = i % BOARD_SIZE, iCol = i / BOARD_SIZE; BoardPiece* pPiece = GetPiece(pState, iRow, iCol); + if (pPiece->piece == PIECE_NONE) + continue; + + bool isBlack = pPiece->color == BLACK; + int index = isBlack ? i : (BOARD_SIZE * BOARD_SIZE - 1 - i); - int value = nPieceValues[pPiece->piece]; - if (pPiece->color == BLACK) + int value = nPieceValues[pPiece->piece] + nValueMultPiece[pPiece->piece][index]; + if (isBlack) value = -value; - State += value; + eval += value; } - return State; + return eval; } #define MAX_MOVES 256 @@ -41,11 +128,6 @@ BoardMove* GetAllMoves(BoardState* pState, int* outMoveCount) } \ } while (0) - // backup the current state pointer, we'll modify it - BoardState state; - BoardState * pOldCurrentState = g_CurrentState; - g_CurrentState = &state; - int nMoves = 0; BoardMove* pMoves = calloc(MAX_MOVES, sizeof(BoardMove)); @@ -74,34 +156,88 @@ BoardMove* GetAllMoves(BoardState* pState, int* outMoveCount) } } - g_CurrentState = pOldCurrentState; - *outMoveCount = nMoves; return pMoves; } -int NegativeMax(BoardState* pState, int Depth) +int MinMax(BoardState* pState, int Depth, BoardMove* winningMoveOut, bool isMaxing) { + BoardMove winningMove = { -1, -1, -1, -1 }; + if (Depth == 0) return EvaluateBoardState(pState); - return 0; + BoardState state; + int nMoves = 0; + BoardMove* pMoves = GetAllMoves(pState, &nMoves); + + int max = -INF, min = INF; + + for (int i = 0; i < nMoves; i++) + { + BoardMove bm = pMoves[i]; + + // copy the state + state = *pState; + + // mutate the state to perform the move + eErrorCode ec = ChessCommitMove(&state, bm.rowSrc, bm.colSrc, bm.rowDst, bm.colDst); + + if (ec != ERROR_SUCCESS) + { + if (ec == ERROR_CHECKMATE) { + // checkmate!! don't hesitate!!!! + winningMove = pMoves[i]; + break; + } + + if (ec != ERROR_STALEMATE) { + LogMsg("ERROR: couldn't perform move - MinMax. %d,%d -> %d,%d ==> %d", bm.rowSrc, bm.colSrc, bm.rowDst, bm.colDst, ec); + continue; + } + } + + // the move has been performed. control was passed to the other player. call the MinMax again + + int score = MinMax(&state, Depth - 1, &bm, !isMaxing); + + bm = pMoves[i]; + + if (Depth == g_InitialDepth) + { + LogMsg(">> Eval predicted after %d,%d -> %d,%d ==> %d [%d/%d]", bm.rowSrc, bm.colSrc, bm.rowDst, bm.colDst, score, i+1, nMoves); + } + + if (isMaxing) + { + if (max < score) { + max = score; + winningMove = pMoves[i]; + } + } + else + { + if (min > score) { + min = score; + winningMove = pMoves[i]; + } + } + } + + free(pMoves); + *winningMoveOut = winningMove; + return nMoves == 0 ? 0 : (isMaxing ? max : min); } BoardMove FindBestMove(BoardState* pState) { - BoardMove* pMoves = NULL; - int nMoves = 0; + BoardMove bm = { -1, -1, -1, -1 }; - pMoves = GetAllMoves(pState, &nMoves); + BoardState bs = *pState; + int eval = MinMax(&bs, g_InitialDepth, &bm, bs.m_Player == WHITE); - BoardMove bm = { -1, -1, -1, -1 }; - if (nMoves == 0) - LogMsg("No moves available!"); - else - bm = pMoves[rand() % nMoves]; + LogMsg("Eval after %d,%d -> %d,%d ==> %d", bm.rowSrc, bm.colSrc, bm.rowDst, bm.colDst, eval); - free(pMoves); return bm; } diff --git a/apps/Chess/src/main.c b/apps/Chess/src/main.c index 3f51f77..a587006 100644 --- a/apps/Chess/src/main.c +++ b/apps/Chess/src/main.c @@ -385,7 +385,7 @@ void ChessReleaseCursor(int x, int y) //bPromotePawn = true; col = pPc->color; - err = ChessCommitMove(g_DraggedPieceRow, g_DraggedPieceCol, boardRow, boardCol); + err = ChessCommitMove(g_CurrentState, g_DraggedPieceRow, g_DraggedPieceCol, boardRow, boardCol); } } diff --git a/apps/CommonMakefile b/apps/CommonMakefile index ce30b02..c36ab95 100644 --- a/apps/CommonMakefile +++ b/apps/CommonMakefile @@ -7,6 +7,8 @@ BLD_DIR=build LIS_DIR=../../crt/src LII_DIR=../../crt/include +OPTIMIZATIONS ?= -O2 + LGCC=../../tools/libgcc-i686.a CC=clang @@ -14,7 +16,7 @@ LD=ld AS=nasm STRIP=strip -CFLAGS=-I $(INC_DIR) -I $(LII_DIR) -DNANOSHELL -ffreestanding -target i686-elf -O2 -nostdinc -fno-exceptions -Wall -Wextra -std=c99 -mno-sse2 -MMD $(USERCFLAGS) +CFLAGS=-I $(INC_DIR) -I $(LII_DIR) -DNANOSHELL -ffreestanding -target i686-elf -nostdinc -fno-exceptions -Wall -Wextra -std=c99 -mno-sse2 -MMD $(OPTIMIZATIONS) $(USERCFLAGS) LDFLAGS=-T link.ld -g -nostdlib -zmax-page-size=0x1000 ASFLAGS=-f elf32 From 182e91277661a421cdbbd8f7c8f850c1f057ed43 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 13 Aug 2024 23:46:27 +0300 Subject: [PATCH 4/4] * Chess: Slightly more intelligent and optimized engine --- apps/Chess/src/engine.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/apps/Chess/src/engine.c b/apps/Chess/src/engine.c index f1d3d6c..d8f2d01 100644 --- a/apps/Chess/src/engine.c +++ b/apps/Chess/src/engine.c @@ -160,7 +160,7 @@ BoardMove* GetAllMoves(BoardState* pState, int* outMoveCount) return pMoves; } -int MinMax(BoardState* pState, int Depth, BoardMove* winningMoveOut, bool isMaxing) +int MinMax(BoardState* pState, int Depth, BoardMove* winningMoveOut, bool isMaxing, int alpha, int beta) { BoardMove winningMove = { -1, -1, -1, -1 }; @@ -199,14 +199,13 @@ int MinMax(BoardState* pState, int Depth, BoardMove* winningMoveOut, bool isMaxi // the move has been performed. control was passed to the other player. call the MinMax again - int score = MinMax(&state, Depth - 1, &bm, !isMaxing); + int score = MinMax(&state, Depth - 1, &bm, !isMaxing, alpha, beta); bm = pMoves[i]; + // For debugging and tracking: if (Depth == g_InitialDepth) - { LogMsg(">> Eval predicted after %d,%d -> %d,%d ==> %d [%d/%d]", bm.rowSrc, bm.colSrc, bm.rowDst, bm.colDst, score, i+1, nMoves); - } if (isMaxing) { @@ -214,6 +213,8 @@ int MinMax(BoardState* pState, int Depth, BoardMove* winningMoveOut, bool isMaxi max = score; winningMove = pMoves[i]; } + if (alpha < score) + alpha = score; } else { @@ -221,7 +222,12 @@ int MinMax(BoardState* pState, int Depth, BoardMove* winningMoveOut, bool isMaxi min = score; winningMove = pMoves[i]; } + if (beta > score) + beta = score; } + + if (beta <= alpha) + break; } free(pMoves); @@ -234,7 +240,7 @@ BoardMove FindBestMove(BoardState* pState) BoardMove bm = { -1, -1, -1, -1 }; BoardState bs = *pState; - int eval = MinMax(&bs, g_InitialDepth, &bm, bs.m_Player == WHITE); + int eval = MinMax(&bs, g_InitialDepth, &bm, bs.m_Player == WHITE, -INF, INF); LogMsg("Eval after %d,%d -> %d,%d ==> %d", bm.rowSrc, bm.colSrc, bm.rowDst, bm.colDst, eval);