Tic Tac Toe done

This commit is contained in:
2023-03-01 21:21:16 -08:00
parent eba27a0040
commit f1a3ee8579
8 changed files with 203 additions and 60 deletions

View File

@ -17,7 +17,7 @@ enum TicTacToeTileState Dawn::ticTacToeDetermineWinner(
// Check rows
for(i = 0; i < 9; i += 3) {
if(board.at(i) == board.at(i + 1) && board.at(i) == board.at(i + 2) && board.at(i) != 0) {
*winningCombo = { i, (uint8_t)i + 1, (uint8_t)i + 2 };
*winningCombo = { i, (uint8_t)(i + 0x01), (uint8_t)(i + 0x02) };
return board.at(i);
}
}
@ -25,21 +25,102 @@ enum TicTacToeTileState Dawn::ticTacToeDetermineWinner(
// Check columns
for(i = 0; i < 3; i++) {
if(board.at(i) == board.at(i + 3) && board.at(i) == board.at(i + 6) && board.at(i) != 0) {
*winningCombo = { i, (uint8_t)i + 3, (uint8_t)i + 6 };
*winningCombo = { i, (uint8_t)(i + 0x03), (uint8_t)(i + 0x06) };
return board.at(i);
}
}
// Check diagonals
if(board.at(0) == board.at(4) && board.at(0) == board.at(8) && board.at(0) != 0) {
*winningCombo = {0, 4, 8};
*winningCombo = { 0, 0x04, 0x08 };
return board.at(0);
}
if(board.at(2) == board.at(4) && board.at(2) == board.at(6) && board.at(2) != 0) {
*winningCombo = { 2, 4, 6 };
*winningCombo = { 0x02, 0x04, 0x06 };
return board.at(2);
}
return TIC_TAC_TOE_EMPTY;
}
}
int32_t Dawn::ticTacToeGetBoardScore(
std::map<uint8_t, enum TicTacToeTileState> board,
enum TicTacToeTileState player
) {
int32_t score = 0;
uint8_t lines[8][3] = {
{0, 1, 2}, {3, 4, 5}, {6, 7, 8},
{0, 3, 6}, {1, 4, 7}, {2, 5, 8},
{0, 4, 8}, {2, 4, 6}
};
for (uint8_t i = 0; i < 8; i++) {
uint8_t countPlayer = 0;
uint8_t countEmpty = 0;
for (uint8_t j = 0; j < 3; j++) {
if (board[lines[i][j]] == player) {
countPlayer++;
} else if (board[lines[i][j]] == TIC_TAC_TOE_EMPTY) {
countEmpty++;
}
}
if (countPlayer == 2 && countEmpty == 1) {
score += 10;
} else if (countPlayer == 1 && countEmpty == 2) {
score += 1;
}
}
return score;
}
uint8_t Dawn::ticTacToeGetAiMove(
std::map<uint8_t, enum TicTacToeTileState> board,
enum TicTacToeTileState player
) {
std::vector<uint8_t> winningCombo;
// First, check if there's an immediate winning move for the AI
for(uint8_t i = 0; i < 9; i++) {
if(board[i] != TIC_TAC_TOE_EMPTY) continue;
board[i] = player;
if(ticTacToeDetermineWinner(board, &winningCombo) == player) {
board[i] = TIC_TAC_TOE_EMPTY;
return i;
}
board[i] = TIC_TAC_TOE_EMPTY;
}
// Next, check if the player has an immediate winning move and block it
auto opponent = (player == TIC_TAC_TOE_NOUGHT) ? TIC_TAC_TOE_CROSS : TIC_TAC_TOE_NOUGHT;
for(uint8_t i = 0; i < 9; i++) {
if(board[i] != TIC_TAC_TOE_EMPTY) continue;
board[i] = opponent;
if(ticTacToeDetermineWinner(board, &winningCombo) == opponent) {
board[i] = TIC_TAC_TOE_EMPTY;
return i;
}
board[i] = TIC_TAC_TOE_EMPTY;
}
// If neither player has an immediate winning move, use the simple heuristic to choose a move
uint8_t bestMove = -1;
int32_t bestScore = -1000;
for(uint8_t i = 0; i < 9; i++) {
if(board[i] != TIC_TAC_TOE_EMPTY) continue;
board[i] = player;
auto score = ticTacToeGetBoardScore(board, player);
board[i] = TIC_TAC_TOE_EMPTY;
if(score > bestScore) {
bestMove = i;
bestScore = score;
}
}
return bestMove;
}

View File

@ -13,12 +13,41 @@ namespace Dawn {
TIC_TAC_TOE_CROSS
};
/**
* Determine the winner of the given board.
*
* @param board Tic tac toe board.
* @param winningCombo The output winning combo (if any).
* @return The winning player, or EMPTY if no winner is present.
*/
enum TicTacToeTileState ticTacToeDetermineWinner(
const std::map<uint8_t, enum TicTacToeTileState> board,
std::vector<uint8_t> *winningCombo
);
int32_t ticTacToeGetAiMove(
/**
* Returns the score / value of a given board for the given player. Mostly
* used by the AI to determine whether a given board is better or worse than
* any other.
*
* @param board Board to get the score of.
* @param player Player to get the score for.
* @return The weighted score of this board.
*/
int32_t ticTacToeGetBoardScore(
std::map<uint8_t, enum TicTacToeTileState> board,
enum TicTacToeTileState player
);
/**
* Returns which cell should be used by the given player for their AI as the
* best move for them.
*
* @param board Tic tac toe board.
* @param player Player to get the AI move for.
* @return The recommended cell to fill.
*/
uint8_t ticTacToeGetAiMove(
std::map<uint8_t, enum TicTacToeTileState> board,
enum TicTacToeTileState player
);