파이썬에는 pygame이라는 멀티미디어 표현을 위한 라이브러리가 있습니다. 해당 라이브러리를 통해 다양한 게임들을 제작할 수 있습니다. 해당 라이브러리 소개를 위한 첫 프로젝트로 간단하게 제작이 가능한 틱택토를 구현해보았습니다.
완성된 소스 코드가 필요하신 분은 아래 링크 클릭하여 소스 코드로 바로 이동하면 됩니다.
Tic-Tac-Toe (틱택토)
틱택토는 서양 놀이 중 하나로 3x3 보드에서 가로, 세로, 대각선 중 한 줄을 만들면 승리하는 게임입니다. 간단하게 삼목입니다. 플레이 방법은 다음과 같습니다.
- 가로 두 줄 세로 두 줄을 그립니다.
- 1P와 2P는 3x3 공간 안에 각각 O, X를 번갈아 가면서 그립니다.
- 가로, 세로, 대각선 중 한 줄이 완성되는 경우 만든 사람이 승리합니다.
게임을 만들어보자
위에서 적은 플레이 방법을 알고리즘도로 표현하면 다음과 같습니다.
해당 알고리즘도를 이용하여 간단하게 표시하였습니다.
여기서 주요한 부분이 되는 것은
- init & 3x3 보드 제작
- O, X 표시
- 승리, 무승부 여부 확인
- 리셋
해당부분들을 한개 씩 해결해보겠습니다.
Step1) init & 3x3 보드 제작
pip install pygame
보드를 제작하기 전에 기반이 되는 pygame 라이브러리를 설치합니다.
그리고 초기 환경 구축을 합니다.
import pygame
import numpy
#step1) init
pygame.init()
#color
WHITE = (255,255,255)
YELLOW = (255,255,0)
BLACK = (0,0,0)
RED = (255,0,0)
#font
OX_font = pygame.font.SysFont("arial", 72, True, False) #(폰트,사이즈,볼드,기울기)
result_font = pygame.font.SysFont("arial", 120, True, False) #(폰트,사이즈,볼드,기울기)
#game
grid = numpy.array([[" ", " ", " "],[" ", " ", " "],[" ", " ", " "]]) #3x3 배열 선언
turn = 0
playGame = 1
#design
window =pygame.display.set_mode((600,600))
pygame.display.set_caption('Tic-Tac-Toe')
clock = pygame.time.Clock()
Size = 200
while playGame:
clock.tick(10)
window.fill(BLACK) #검정색 화면으로 물들이기
for event in pygame.event.get(): # 종료키를 누를때 종료하는 기능
if event.type == pygame.QUIT:
playGame = 0
pygame.display.update() #그리기 화면 업데이트
pygame.quit() #pygame 종료
먼저 가로 init 과정까지의 소스 코드입니다. 그리드 부분은 이중 배열을 이용하여 가로 세로를 쉽게 확인할 수 있도록 numpy를 이용하여 진행하였습니다. 게임에서 필요한 변수와 환경을 구현하였습니다.
이제 그리드를 만들어보겠습니다.
def drawGrid(Size):
COL = 3
ROW = 3
for column_index in range(COL):
for row_index in range(ROW):
rect = (Size * column_index, Size * row_index, Size, Size)
pygame.draw.rect(window, WHITE, rect, 1)
그리드 기능은 함수로 구현하였습니다. 현실에서 게임을 할 때는 가로줄 두 개, 세로줄 두 개를 그려 게임을 진행하지만 여기서는 각각의 위치에 사각형으로 분리하였습니다.
Step2) O, X 표시
마우스를 이용하여 O, X를 표시하려고 합니다. 이를 이행하기 위한 순서는 다음과 같습니다.
- 마우스를 이용해 화면에 둘 위치를 선정합니다.
- O, X를 순서대로 설정합니다.
- O 또는 X를 그립니다.
먼저는 게임을 실행하는 while 문에 나머지를 이용하여 OX를 순서대로 분리할 수 있습니다. (2번 과정)
for event in pygame.event.get():
if event.type == pygame.QUIT: # 종료키를 누를 때 이벤트 처리
playGame=False
elif event.type == pygame.MOUSEBUTTONDOWN: #클릭할 때 이벤트 처리
COL = pygame.mouse.get_pos()[1] #세로 마우스 좌표
ROW = pygame.mouse.get_pos()[0] #가로 마우스 좌표
loc = locate(COL, ROW, Size)
if checkBlank(grid, loc[0], loc[1]):
if turn % 2 == 0:
grid[loc[0], loc[1]] = 'O'
turn = turn +1
else:
grid[loc[0], loc[1]] = 'X'
turn = turn +1
클릭한 좌표를 배열로 변환시키는 과정을 locate라는 함수를 제작하여 인식하였습니다. 그리고 채워진 칸이 중복인 경우 작동을 하지 않도록 checkBlank 함수로 구현하였습니다.
def locate(COL, ROW, Size):
for column_index in range(COL):
for row_index in range(ROW):
if (column_index == int(COL/Size) and row_index == int(ROW/Size)):
return (column_index, row_index)
def checkBlank(grid, COL, ROW):
if grid[COL][ROW] == ' ':
return True
else:
return False
마지막으로 그리드 안에 O, X를 표시하는 것은 drawOX 함수를 이용하여 구현하였습니다.
def drawOX(grid, Size):
COL = 3
ROW = 3
for column_index in range(COL):
for row_index in range(ROW):
if grid[column_index][row_index] == 'O':
text = OX_font.render('O', False, WHITE)
elif grid[column_index][row_index] == 'X':
text = OX_font.render('X', True, YELLOW)
else:
text = OX_font.render('X', True, BLACK)
window.blit(text, (Size * row_index + Size / 3, Size * column_index + Size / 4))
Step3) 승리, 무승부 여부 확인
승리 여부를 알기 위해서는 한 줄이 만들어지는 것을 확인하여야 합니다. 한 줄이 나타나는 가능성은 아래와 같습니다.
해당 조건들을 먼저 확인한 다음 없는 경우 다음 턴으로 넘깁니다. 그리고 보드에 모든 공간이 다 차 있고 승리 조건이 맞지 않는 경우 무승부로 마무리합니다.
def winGame(grid, OX):
if (grid[0][0] == OX and grid[0][1] == OX and grid[0][2] == OX) or \
(grid[1][0] == OX and grid[1][1] == OX and grid[1][2] == OX) or \
(grid[2][0] == OX and grid[2][1] == OX and grid[2][2] == OX) or \ #가로
(grid[0][0] == OX and grid[1][0] == OX and grid[2][0] == OX) or \
(grid[0][1] == OX and grid[1][1] == OX and grid[2][1] == OX) or \
(grid[0][2] == OX and grid[1][2] == OX and grid[2][2] == OX) or \ #세로
(grid[0][0] == OX and grid[1][1] == OX and grid[2][2] == OX) or \ #좌대각
(grid[0][2] == OX and grid[1][1] == OX and grid[2][0] == OX): #우대각
return True
else:
return False
def drawGame(turn):
if turn == 9:
return True
else:
return False
해당 결과를 인식하여 텍스트로 출력하는 drawResult함수를 추가로 제작하였습니다.
def drawResult(grid, Size, turn):
if winGame(grid, 'O'):
text = OX_font.render('"O" player win!', False, WHITE)
elif winGame(grid, 'X'):
text = OX_font.render('"X" player win!', False, WHITE)
elif drawGame(turn):
text = OX_font.render('draw game', False, WHITE)
else:
text = OX_font.render(' ', True, BLACK)
window.blit(text, (Size, Size))
Step4) 리셋
승리 혹은 무승부가 된 경우에 게임 다시 시작하시겠습니까? 기능을 제작한 뒤 게임에 영향을 주는 변수들을 초기화한 뒤 다시 게임을 플레이합니다.
if event.type == pygame.KEYUP: #R 버튼을 누를때 이벤트 처리
if event.key == pygame.K_r:
grid = numpy.array([[" ", " ", " "],[" ", " ", " "],[" ", " ", " "]])
turn = 0
전체 소스코드
import pygame
import numpy
pygame.init()
#step1) init
WHITE = (255,255,255)
YELLOW = (255,255,0)
BLACK = (0,0,0)
RED = (255,0,0)
OX_font = pygame.font.SysFont("arial", 72, True, False) #(폰트,사이즈,볼드,기울기)
result_font = pygame.font.SysFont("arial", 90, True, False) #(폰트,사이즈,볼드,기울기)
grid = numpy.array([[" ", " ", " "],[" ", " ", " "],[" ", " ", " "]]) #3x3 배열 선언
turn = 0
playGame = 1
window =pygame.display.set_mode((600,600)) #가로 600, 세로 600화면
pygame.display.set_caption('Tic-Tac-Toe')
clock = pygame.time.Clock()
Size = 200
#step1) grid
def drawGrid(Size): #draw Grid
COL = 3
ROW = 3
for column_index in range(COL):
for row_index in range(ROW):
rect = (Size * column_index, Size * row_index, Size, Size)
pygame.draw.rect(window, WHITE, rect, 1)
#step2) play game and write O or X
def locate(COL, ROW, Size): #find aray with location
for column_index in range(COL):
for row_index in range(ROW):
if (column_index == int(COL/Size) and row_index == int(ROW/Size)):
return (column_index, row_index)
def checkBlank(grid, COL, ROW): #duplicate prevention
if grid[COL][ROW] == ' ':
return True
else:
return False
def drawOX(grid, Size): #show O and X
COL = 3
ROW = 3
for column_index in range(COL):
for row_index in range(ROW):
if grid[column_index][row_index] == 'O':
text = OX_font.render('O', False, WHITE)
elif grid[column_index][row_index] == 'X':
text = OX_font.render('X', True, YELLOW)
else:
text = OX_font.render(' ', True, BLACK)
window.blit(text, (Size * row_index + Size / 3, Size * column_index + Size / 4))
#step3) who's win? or draw?
def winGame(grid, OX): #check Win
if (grid[0][0] == OX and grid[0][1] == OX and grid[0][2] == OX) or \
(grid[1][0] == OX and grid[1][1] == OX and grid[1][2] == OX) or \
(grid[2][0] == OX and grid[2][1] == OX and grid[2][2] == OX) or \
(grid[0][0] == OX and grid[1][0] == OX and grid[2][0] == OX) or \
(grid[0][1] == OX and grid[1][1] == OX and grid[2][1] == OX) or \
(grid[0][2] == OX and grid[1][2] == OX and grid[2][2] == OX) or \
(grid[0][0] == OX and grid[1][1] == OX and grid[2][2] == OX) or \
(grid[0][2] == OX and grid[1][1] == OX and grid[2][0] == OX):
return True
else:
return False
def drawGame(turn): #check Draw
if turn == 9:
return True
else:
return False
def drawResult(grid, turn): #show Result
if winGame(grid, 'O'):
text = result_font.render('"O" player win!', False, RED)
elif winGame(grid, 'X'):
text = result_font.render('"X" player win!', False, RED)
elif drawGame(turn):
text = result_font.render('draw game', False, RED)
else:
text = result_font.render(' ', True, BLACK)
window.blit(text, (0, 0))
#step ALL) play it!
while playGame:
clock.tick(10)
window.fill(BLACK)
drawGrid(Size)
drawOX(grid, Size)
drawResult(grid, turn)
for event in pygame.event.get():
if event.type == pygame.QUIT: # 종료키를 누를때 이벤트 처리
playGame = 0
if event.type == pygame.MOUSEBUTTONDOWN: # 마우스 클릭을 할때 이벤트 처리
COL = pygame.mouse.get_pos()[1] #세로 마우스 좌표
ROW = pygame.mouse.get_pos()[0] #가로 마우스 좌표
loc = locate(COL, ROW, Size)
if checkBlank(grid, loc[0], loc[1]):
if turn % 2 == 0:
grid[loc[0], loc[1]] = 'O'
turn = turn +1
else:
grid[loc[0], loc[1]] = 'X'
turn = turn +1
print(grid)
#step4) reset
if event.type == pygame.KEYUP: #R 버튼을 누를때 이벤트 처리
if event.key == pygame.K_r:
grid = numpy.array([[" ", " ", " "],[" ", " ", " "],[" ", " ", " "]])
turn = 0
pygame.display.update() #그리기 화면 업데이트
pygame.quit() #pygame 종료
pygame을 이용하여 미디어 관련으로 다양한 프로젝트를 실행할 수 있으나 pygame 라이브러리를 한번 정리해야겠다는 생각이 들었습니다. 시간이 날 때 자주 사용하는 메서드를 모아서 정리해보겠습니다.
'컴퓨터 > Python' 카테고리의 다른 글
Python (16) - TTS와 STT를 구현하자 (pyttsx3, Speech Recogition) (0) | 2023.05.10 |
---|---|
Python (15) - 파이썬 가상환경을 만들자 (venv) (0) | 2023.04.15 |
Python (13) - 파이썬으로 QR코드를 만들어보자 (qrcode) (0) | 2023.02.09 |
Python (12) - 재귀함수를 이용하여 팩토리얼, 하노이탑을 풀어보자 (0) | 2023.01.14 |
Python (11) - 스택(Stack)과 큐(Queue) 구현하기 (0) | 2023.01.09 |