TicTacToeDialog/TicTacToeGame.cpp
2022-02-23 22:40:21 +03:00

127 lines
2.7 KiB
C++

#include "TicTacToeGame.h"
/**
* @brief Sets field cell to current player
* @param coord - Cell coordinate
*/
void TicTacToeGame::playerMove(UINT coord)
{
field[coord] = player;
}
/**
* @brief Calculates best move for computer using minimax algorithm
*/
UINT TicTacToeGame::findComputerMove()
{
INT bestScore = INT_MIN, score;
UINT bestCell = -1;
for (UINT i = 0; i < 9; i++)
if (field[i] == SqState::E) {
field[i] = SqState::O;
score = minimax(0, SqState::O);
if (score > bestScore) {
bestScore = score;
bestCell = i;
}
field[i] = SqState::E;
}
return bestCell;
}
/**
* @brief Calculates score of current game board for player. If the game is ended, it returns its result minus depth of this state in game tree. Else, it returns
* @param depth - Number of steps made in game graph
* @param player - Player for which we want to calculate current score
*/
INT TicTacToeGame::minimax(UINT depth, SqState player)
{
if (player == SqState::E) throw "Called with Empty player";
SqState enemy = getEnemy(player);
INT winScore = checkWin(player);
if (winScore) return winScore - depth;
INT maxEnemyScore = INT_MIN;
for (int i = 0; i < 9; i++)
if (field[i] == SqState::E) {
field[i] = enemy;
INT score = minimax(depth + 1, enemy);
if (score > maxEnemyScore)
maxEnemyScore = score;
field[i] = SqState::E;
}
if (maxEnemyScore == INT_MIN) return 0;
return -maxEnemyScore;
}
/**
* @brief Checks if current game state is wining for player or its enemy
* @param player - Player for which we check
* @returns 10 means win for player, -10 - for enemy, 0 - draw or unfinished
*/
INT TicTacToeGame::checkWin(SqState player)
{
if (player == SqState::E) throw "Called with Empty player";
SqState enemy = getEnemy(player);
for (INT i = 0; i < 3; i++) {
if ((field[i * 3] == field[1 + i * 3] && field[1 + i * 3] == field[2 + i * 3]) || // Row
(field[i] == field[3 + i] && field[3 + i] == field[6 + i])) { // Column
if (field[i * 4] == player)
return 10;
if (field[i * 4] == enemy)
return -10;
}
}
if ((field[0] == field[4] && field[4] == field[8]) || // first diagonal
(field[2] == field[4] && field[4] == field[6])) { // second diagonal
if (field[4] == player)
return 10;
if (field[4] == enemy)
return -10;
}
return 0;
}
/**
* @brief Checks if current game state is a draw
*/
BOOL TicTacToeGame::checkDraw()
{
for (int i = 0; i < 9; i++)
if (field[i] == SqState::E)
return FALSE;
ended = TRUE;
return TRUE;
}
/**
* @brief Utility for getting enemy of player
*/
SqState TicTacToeGame::getEnemy(SqState player)
{
if (player == SqState::E) throw "Called with Empty player";
if (player == SqState::X)
return SqState::O;
else
return SqState::X;
}