지뢰찾기 게임

지뢰찾기 게임 제작기 2

잡코신 2024. 3. 12. 18:00
728x90
반응형

지난 시간

지난번엔 간단한 코딩으로 지뢰찾기 게임을 만들었다.

이번엔 지난번 코드를 더 발전 시켜서 GUI를 적용시키고 클릭 이벤트를 처리해 본다.

지뢰찾기 게임 만들기

언어는 파이썬을 사용하고, 만드는 기준은 지뢰찾기 게임 룰에 기반한다.

import tkinter as tk

이번에 또 다른 라이브러리가 필요하다.

GUI로 표시해야 하기 때문에 tkinter 라이브러리를 추가해 주는 것이다.

tkinter GUI에 대한 표준 Python 인터페이스이며 Window 창을 생성할 수 있다.

4. GUI 적용

root = tk.Tk()
root.title("지뢰찾기")
root.geometry("400x400+100+100")
root.resizable(False, False)

root.mainloop()

윈도우이름 = tkinter.Tk()를 이용하여 가장 상위 레벨의 윈도우 창을 생성할 수 있다. 

위 코드에선 실행했을 때 나타나는 창이 바로 코드의 root에 해당한다.

 

윈도우이름.mainloop()를 사용하여 윈도우이름의 윈도우 창을 윈도우가 종료될 때까지 실행시킨다.

위 코드에선 mainloop()에 의해 root 창은 종료되지 않고 버튼 클릭 등의 이벤트를 수신하거나 사용자의 입력을 처리하는 등의 일을 계속 수행할 수 있게 되는 것이다.

 

윈도우이름.title("제목")을 이용하여 윈도우 창의 제목을 설정할 수도 있다.

위 코드에선 root 창의 이름을 지뢰찾기로 설정한 것을 알 수 있다.

 

윈도우이름.geometry("너비x높이+x좌표+y좌표")를 이용하여 윈도우 창의 너비와 높이, 초기 화면 위치의 x좌표와 y좌표를 설정할 수 있다.

위 코드에선 가로 400 세로 400의 정사각형 창을 좌표(100, 100)에 나오게 만든 것을 알 수 있다.

 

윈도우이름.resizeable(상하, 좌우)을 이용하여 윈도우 창의 창 크기 조절 가능 여부를 설정할 수 있다. 

True로 설정할 경우 윈도우 창의 크기를 조절할 수 있지만 위 코드에선 False로 막아놓은 것을 알 수 있다.

resizeable()을 적용할 때, True=1, False=0을 의미하여 상수를 입력해도 적용이 가능하다.

 

buttons = [] # 버튼들을 저장할 2차원 리스트

for row in range(board_size):
    row_buttons = [] # 각 행에 속하는 버튼들을 저장할 리스트
    
    # 버튼 생성 후 각 행과 열에 버튼 배치
    for col in range(board_size):
        button = tk.Button(root, text='', width=4, height=2)
        button.grid(row=row, column=col)
        row_buttons.append(button)
        
    buttons.append(row_buttons)

아까 만든 창에 지뢰들을 표시할 버튼들을 배치해 준다.

버튼을 누르면 그 셀이 지뢰인지 아닌지 보여줄 것이기 때문에 버튼으로 하는 것이다.

tk.Button()은 창, 들어갈 텍스트, 너비, 높이로 버튼을 만들어준다.
button.grid(row=row, column=col)는 그리드 레이아웃으로 행과 열에 버튼을 배치해준다.

 

5. 클릭 이벤트 처리

for row in range(board_size):
    for col in range(board_size):
    	# 마우스 왼쪽 버튼
        buttons[row][col].bind("<Button-1>", lambda event, row=row, col=col: left_click(event, row, col))
        # 마우스 오른쪽 버튼
        buttons[row][col].bind("<Button-3>", lambda event, row=row, col=col: right_click(event, row, col))

GUI를 사용하면 전 코드처럼 main의 while 문 없이 tk.Tk()와  mainloop() 사이에 코드를 작동하는 식으로 기능하기 때문에 그 안쪽에만 코드를 작성하면 된다.

그렇기에 안쪽에 버튼의 클릭 이벤트 코드를 작성했다.

위젯.Bind("이벤트", 함수)를 사용하여 위젯 이벤트가 작동할 때 실행할 함수를 설정할 수 있다.

이벤트 작동
<Button-1> 마우스 왼쪽 버튼을 누를 때
<Button-2> 마우스 휠 버튼을 누를 때
<Button-3> 마우스 오른쪽 버튼을 누를 때
<Button-4> 마우스 휠 버튼을 올릴 때
<Button-5> 마우스 휠 버튼을 내 때
<MouseWheel> 마우스 휠을 이동할 때

Bind 이벤트 부분에 들어갈 수 있는 마우스 이벤트 들은 이런 것들이 있다.

우린 마우스 우클릭과 좌클릭으로 셀에 깃발과 셀 오픈을 관리할 것이기에 1번과 3번을 이용한다.

 

함수 부분에선 파이썬에 lamda를 이용하여 익명함수를 만들었다.

간단하게 람다함수에 대해 설명하고 넘어가겠다.

# 기본 함수 표현식
def hap(x, y):
	return x + y
    
hap(10, 20)

# 람다 함수 표현식
(lambda x,y: x + y)(10, 20)

위에 두 코드의 결과는 같게 나오는데 그 이유는 두 코드의 생김새는 다르지만 동작은 같기 때문이다.

lambda함수는 lambda라는 키워드를 입력하고 뒤에는 매개변수(인자)를 입력하고 콜론(:)을 넣은 다음에 그 매개변수(인자)를 이용한 동작들을 적으면 된다.
위 코드로 보면 인자로 들어온 값 x와 y를 더해서 반환하는 코드가 되는 것이다.

 

다시 돌아가서 코드를 보면  Bind 함수 부분에 lambda event, row=row, col=col: right_click(event, row, col)라고 람다 함수로 이벤트, 열, 행을 넣어 클릭 함수를 실행시키게 만들었다.

# 좌클릭
def left_click(event, row, col):
    if board[row][col] == '*':
        revealed[row][col] = 'X'  # 지뢰 밟음
    else:
        revealed[row][col] = str(count_mines_around(board, row, col))

# 우클릭
def right_click(event, row, col):
    if revealed[row][col] == ' ':
        revealed[row][col] = 'F'  # 깃발 표시
    elif revealed[row][col] == 'F':
        revealed[row][col] = ' '  # 깃발 제거

좌우클릭의 함수들이다.

  • 좌클릭 : 셀 오픈으로 지뢰면 지뢰를 밟아 X로 표시하고 아니라면 주위 지뢰 개수를 표시해 준다.
  • 우클릭 : 지뢰가 있다고 깃발로 표시하는 것으로 클릭한다면 F로 표시하고 다시 클릭하면 지운다.

이제 다 만들어 작동이 될 것이라고 생각하겠지만 사실 작동시키고 셀을 누르면 아무 일도 일어나지 않는다.

이유는 사용자가 셀을 클릭할 때마다 GUI가 최신 정보로 업데이트되어 현재 게임 상태를 잘 표시해야 하는데

그런 지금 코드엔 없기 때문이다.

def update_board():
    for row in range(board_size):
        for col in range(board_size):
            if revealed[row][col] == ' ':
                # 현재 셀이 가려진 상태라면 버튼의 텍스트를 비워두고 활성 상태로 설정
                buttons[row][col].config(text='', state='normal')
            else:
                # 현재 셀이 공개된 상태라면 버튼의 텍스트를 해당 값으로 설정하고 비활성 상태로 설정
                buttons[row][col].config(text=revealed[row][col], state='disabled')

셀을 반복하면서 업데이트해주는 코드이다. 현재 상태를 버튼에 반영한다.

그리고 이 함수를 적절한 부분에 넣어주면 판이 업데이트되면서 코드가 잘 작동되게 된다.

 

지뢰찾기(ver.2) 플레이해보기

import tkinter as tk
import random

# 게임 설정
board_size = 9
num_mines = 10

# 게임 보드 생성
def initialize_board(size, mines):
    board = [[' ' for _ in range(size)] for _ in range(size)]
    placed_mines = 0
    while placed_mines < mines:
        row, col = random.randint(0, size - 1), random.randint(0, size - 1)
        if board[row][col] != '*':
            board[row][col] = '*'
            placed_mines += 1
    return board

# 지뢰 주변의 지뢰 개수 계산
def count_mines_around(board, row, col):
    count = 0
    for r in range(row - 1, row + 2):
        for c in range(col - 1, col + 2):
            if 0 <= r < len(board) and 0 <= c < len(board[0]) and board[r][c] == '*':
                count += 1
    return count

# GUI 초기화
root = tk.Tk()
root.title("지뢰찾기")
root.resizable(False, False)

# 게임 보드 생성
board = initialize_board(board_size, num_mines)
revealed = [[' ' for _ in range(board_size)] for _ in range(board_size)]

# 게임 보드의 각 셀을 버튼으로 만들기
buttons = []

for row in range(board_size):
    row_buttons = []
    for col in range(board_size):
        button = tk.Button(root, text='', width=4, height=2)
        button.grid(row=row, column=col)
        row_buttons.append(button)
    buttons.append(row_buttons)

# 게임 보드 업데이트
def update_board():
    for row in range(board_size):
        for col in range(board_size):
            if revealed[row][col] == ' ':
                buttons[row][col].config(text='', state='normal')
            else:
                buttons[row][col].config(text=revealed[row][col], state='disabled')

def left_click(event, row, col):
    if board[row][col] == '*':
        revealed[row][col] = 'X'  # 지뢰 밟음
    else:
        revealed[row][col] = str(count_mines_around(board, row, col))

    update_board()

def right_click(event, row, col):
    if revealed[row][col] == ' ':
        revealed[row][col] = 'F'  # 깃발 표시
    elif revealed[row][col] == 'F':
        revealed[row][col] = ' '  # 깃발 제거

    update_board()

# 각 버튼에 클릭 이벤트 바인딩
for row in range(board_size):
    for col in range(board_size):
        buttons[row][col].bind("<Button-1>", lambda event, row=row, col=col: left_click(event, row, col))
        buttons[row][col].bind("<Button-3>", lambda event, row=row, col=col: right_click(event, row, col))

update_board()

root.mainloop()

최종 코드는 이렇게 된다. 이제 실행해 보자

 

지뢰찾기 ver2

잘 동작하는 것을 확인할 수 있었다.

 

Next

간단한 로직과 UI를 만들었으니 이제 부가 기능적인 것을 만들어보겠다.

728x90
반응형

'지뢰찾기 게임' 카테고리의 다른 글

지뢰찾기 게임 제작기 5  (0) 2024.04.02
지뢰찾기 게임 제작기 4  (0) 2024.03.26
지뢰찾기 게임 제작기 3  (0) 2024.03.19
지뢰찾기 게임 제작기 1  (0) 2024.03.05