Tic-Tac-Toe(틱택토)는 다음과 같은 규칙을 가진 게임입니다:
O, X를 3x3 보드에 놓습니다.현재 주어진 보드 상태에서 현재 플레이어가 이길 수 있는지, 질지, 무승부일지를 미리 예측하는 알고리즘을 구현합니다.
틱택토는 최대 9개의 칸에 O, X, .(빈칸) 중 하나가 들어갈 수 있습니다.
enum
{
DEFAULT = 2, // 계산 전 상태
WIN = 1, // 현재 플레이어가 이김
DRAW = 0, // 무승부
LOSE = -1 // 현재 플레이어가 짐
};
DEFAULT: 아직 계산되지 않은 상태WIN / DRAW / LOSE: 현재 플레이어 기준의 게임 결과int HashKey(const vector<vector<char>>& board)
{
int ret = 0;
for (int y = 0; y < 3; y++)
{
for (int x = 0; x < 3; x++)
{
ret = ret * 3;
if (board[y][x] == 'o') ret += 1;
else if (board[y][x] == 'x') ret += 2;
}
}
return ret;
}
'.' = 0, 'o' = 1, 'x' = 2int로 표현 가능bool IsFinished(const vector<vector<char>>& board, char turn)
{
for (int i = 0; i < 3; i++)
if (board[i][0] == turn && board[i][1] == turn && board[i][2] == turn)
return true;
for (int i = 0; i < 3; i++)
if (board[0][i] == turn && board[1][i] == turn && board[2][i] == turn)
return true;
if (board[0][0] == turn && board[1][1] == turn && board[2][2] == turn)
return true;
if (board[0][2] == turn && board[1][1] == turn && board[2][0] == turn)
return true;
return false;
}
turn이면 승리CanWin()int CanWin(vector<vector<char>>& board, char turn)
{
// 1. 기저 사례: 상대가 이긴 경우 → 나의 패배
if (IsFinished(board, 'o' + 'x' - turn))
return LOSE;
// 2. 캐시 확인
int key = HashKey(board);
int& ret = cache[key];
if (ret != DEFAULT)
return ret;
// 3. 가능한 모든 착수 시도
int minValue = DEFAULT;
for (int y = 0; y < 3; y++)
{
for (int x = 0; x < 3; x++)
{
if (board[y][x] != '.') continue;
board[y][x] = turn; // 착수
minValue = min(minValue, CanWin(board, 'o' + 'x' - turn)); // 상대 차례
board[y][x] = '.'; // 복원
}
}
// 4. 결과 정리
if (minValue == DRAW || minValue == DEFAULT)
return ret = DRAW;
return ret = -minValue;
}
minValue는 상대방 기준 최선의 결과내가 이기려면 → 상대가 지게 만들어야 함-minValue를 리턴해서 나의 결과로 전환main() 함수)int main()
{
board = {
{'o', 'x', 'x'},
{'.', 'o', '.'},
{'o', '.', '.'}
};
for (int i = 0; i < 19683; i++)
cache[i] = DEFAULT;
int result = CanWin(board, 'x');
switch (result)
{
case WIN: cout << "Win" << endl; break;
case DRAW: cout << "Draw" << endl; break;
case LOSE: cout << "Lose" << endl; break;
}
}
보드 상태:
o x x
. o .
o . .
X의 차례Win → 현재 X가 반드시 이길 수 있는 경로 존재memset 주의// int형 배열에 memset으로 -1 사용 가능
memset(cache, -1, sizeof(cache)); // -1은 0xFFFFFFFF → 가능
// 하지만 -2는 안 됨! (1바이트로는 표현 불가)