틱택토(Tic Tac Toe)는 누구나 한 번쯤 해본 간단한 게임이지만, 이번에 소개할 버전은 조금 특별합니다. 플레이어가 직접 참여하지 않아도 되는 완전 자동 게임으로, 컴퓨터가 난수를 이용해 스스로 말을 놓으며 게임을 끝까지 진행합니다.
이 구현에서는 파이썬의 두 가지 모듈인 numpy와 random을 사용합니다. 사용자에게 말을 놓을 위치를 입력받는 대신, 보드의 빈 칸 중 하나가 무작위로 선택되어 마크가 놓입니다. 승자가 결정될 때까지 매 턴이 끝날 때마다 보드 상태가 화면에 출력되며, 모든 칸이 채워졌는데도 승부가 나지 않으면 무승부로 판정해 -1을 반환합니다.
게임의 핵심 동작 방식
1. 보드 생성
my_create_board() 함수는 3×3 크기의 넘파이 배열을 만들어 모든 값을 0으로 초기화합니다. 여기서 0은 빈 칸, 1과 2는 각각 두 플레이어의 마크를 의미합니다.
2. 빈 칸 탐색과 랜덤 배치
my_possibilities() 함수는 보드에서 값이 0인 좌표를 모두 찾아 리스트로 반환하고, my_random_place() 함수는 그중 하나를 random.choice()로 골라 현재 플레이어의 마크를 놓습니다.
3. 승리 조건 판정
my_row_win(), my_col_win(), my_diag_win() 함수가 각각 가로줄, 세로줄, 대각선에 같은 마크가 세 개 이어졌는지 검사합니다. evaluate_game() 함수는 이 세 가지 조건을 종합해 승자를 판별하며, 보드가 가득 찼는데도 승자가 없다면 무승부(-1)를 반환합니다.
4. 게임 진행
my_play_game()은 실제 게임을 구동하는 메인 함수입니다. 두 플레이어가 번갈아 가며 무작위로 말을 놓고, 매 수마다 sleep(2)로 2초씩 멈추어 보드의 변화를 눈으로 확인할 수 있도록 합니다.
예제 코드
import numpy as np
import random
from time import sleep
# 먼저 비어 있는 보드를 생성합니다
def my_create_board():
return(np.array([[0, 0, 0], [0, 0, 0], [0, 0, 0]]))
# 보드에서 비어 있는 칸을 확인합니다
def my_possibilities(board):
l = []
for i in range(len(board)):
for j in range(len(board)):
if board[i][j] == 0:
l.append((i, j))
return(l)
# 플레이어의 말을 놓을 랜덤한 위치를 선택합니다
def my_random_place(board, my_player):
selection = my_possibilities(board)
current_loc = random.choice(selection)
board[current_loc] = my_player
return(board)
# 가로줄(row)에서의 승리 여부를 검사합니다
def my_row_win(board, my_player):
for x in range(len(board)):
win = True
for y in range(len(board)):
if board[x, y] != my_player:
win = False
continue
if win == True:
return(win)
return(win)
# 세로줄(column)에서의 승리 여부를 검사합니다
def my_col_win(board, my_player):
for x in range(len(board)):
win = True
for y in range(len(board)):
if board[y][x] != my_player:
win = False
continue
if win == True:
return(win)
return(win)
# 대각선에서의 승리 여부를 검사합니다
def my_diag_win(board, my_player):
win = True
for x in range(len(board)):
if board[x, x] != my_player:
win = False
return(win)
# 게임 결과를 평가합니다
def evaluate_game(board):
my_winner = 0
for my_player in [1, 2]:
if (my_row_win(board, my_player) or
my_col_win(board, my_player) or
my_diag_win(board, my_player)):
my_winner = my_player
if np.all(board != 0) and my_winner == 0:
my_winner = -1
return my_winner
# 게임을 시작하는 메인 함수
def my_play_game():
board, my_winner, counter = my_create_board(), 0, 1
print(board)
sleep(2)
while my_winner == 0:
for my_player in [1, 2]:
board = my_random_place(board, my_player)
print(str(counter) + '번째 수 이후의 보드')
print(board)
sleep(2)
counter += 1
my_winner = evaluate_game(board)
if my_winner != 0:
break
return(my_winner)
# 실행 코드
print('승자는: ' + str(my_play_game()))
실행 결과
[[0 0 0] [0 0 0] [0 0 0]] 1번째 수 이후의 보드 [[0 1 0] [0 0 0] [0 0 0]] 2번째 수 이후의 보드 [[0 1 0] [0 0 0] [0 0 2]] 3번째 수 이후의 보드 [[0 1 0] [0 0 0] [0 1 2]] 4번째 수 이후의 보드 [[0 1 0] [0 0 2] [0 1 2]] 5번째 수 이후의 보드 [[0 1 1] [0 0 2] [0 1 2]] 6번째 수 이후의 보드 [[0 1 1] [2 0 2] [0 1 2]] 7번째 수 이후의 보드 [[0 1 1] [2 1 2] [0 1 2]] 승자는: 1
난수를 기반으로 진행되기 때문에 실행할 때마다 말이 놓이는 순서가 달라지며, 게임의 흐름과 승자도 매번 바뀝니다. 위 실행 예시에서는 플레이어 1이 세로줄을 완성해 승리했습니다. 이 코드는 단순하지만, 여기에 미니맥스(minimax) 알고리즘이나 강화학습 에이전트를 추가하면 사람과 대결할 수 있는 틱택토 AI로 확장할 수 있습니다.