지뢰찾기 게임

지뢰찾기 게임 제작기 4

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

지난 시간

지난번엔 지뢰찾기 게임에 종료와 현재 상태를 알려주었다.

이번엔 지난번에 만든 게임의 기능을 더 추가하여 완성도를 높인다.

지뢰찾기 게임 만들기

지뢰찾기 룰에 기반해 파이썬으로 제작한다.

 

얼추 게임의 모습은 갖추었지만 아직 기능이 부족해 보인다.

오늘은 게임이 종료되었을 때 모든 셀이 오픈되고 칸을 클릭했을 때 주위의 빈칸이 있으면 열리는 기능을 만들 것이다.

def reveal_all_cells():
    for row in range(board_size):
        for col in range(board_size):
            if board[row][col] == '*':
                revealed[row][col] = 'X'  # 지뢰 셀은 'X'로 공개
            else:
                revealed[row][col] = str(count_mines_around(board, row, col))  # 나머지 셀은 주변 지뢰 개수로 공개

    update_board()  # 보드 업데이트

모든 셀을 공개하는 함수이다.

반복문을 돌면서 모든 셀을 오픈한다.

이 함수는 게임이 종료되었을 때 작동하기에 게임 종료 함수 부분에 넣어준다.

def open_empty_cells(board, revealed, row, col):
    revealed[row][col] = str(count_mines_around(board, row, col))
    global remaining_cells
    remaining_cells -= 1
    
    if revealed[row][col] == '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]):
                    open_empty_cells(board, revealed, r, c)

이 함수는 주위에 지뢰개수가 0개인 셀을 같이 열어주는 함수다.

원래 셀 오픈 식에서 셀이 0이면 셀 주위 9칸을 반복문으로 돌면서 open_empty_cells 함수를 실행한다.

재귀함수 형식으로 주위 셀이 0이 아닐 때까지 오픈하게 된다.

leftclick else문을 대신에 이 함수를 넣어준다.

 

그리고 지뢰가 없는 부분을 0으로 표현하다 보니 너무 난잡한 것이 있어 빈칸으로 표현해 주겠다.

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', bg='light gray')
            else:
                # 표시할 텍스트가 '0'인 경우 빈 문자열로 표시하고, 그렇지 않은 경우에는 지뢰 개수로 표시
                text_to_display = revealed[row][col] if revealed[row][col] != '0' else ''
                buttons[row][col].config(text=text_to_display, state='disabled', bg='white')

 

update_board 부분의 else문을 수정해 준다.

그리고 빈칸으로 표시하니 열린 셀과 안 열린 셀 구분이 안 돼서 버튼에 색을 넣어줬다.

def right_click(event, row, col):
    if revealed[row][col] == ' ':
        revealed[row][col] = 'F'  # 깃발 표시
        buttons[row][col].config(text='F', disabledforeground='red')  #빨간색으로 변경
    elif revealed[row][col] == 'F':
        revealed[row][col] = ' '  # 깃발 제거
        buttons[row][col].config(text='', disabledforeground='black')

    update_board()

또 종료 메시지와 깃발 색도 넣어줬다.

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

import tkinter as tk
import random

# 게임 설정
board_size = 9
num_mines = 10
remaining_cells = board_size * board_size - num_mines

# 게임 보드 초기화
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

# 주변 빈 칸들을 재귀적으로 열기
def open_empty_cells(board, revealed, row, col):
    if revealed[row][col] != ' ':
        return
    revealed[row][col] = str(count_mines_around(board, row, col))
    global remaining_cells
    remaining_cells -= 1
    if revealed[row][col] == '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]):
                    open_empty_cells(board, revealed, r, c)

def update_info_labels():
    mine_count_label.config(text=f"지뢰 개수: {num_mines}")
    remaining_cells_label.config(text=f"남은 셀 개수: {remaining_cells}")

# 다시 시작
def restart_game():
    global board, revealed, remaining_cells

    board = initialize_board(board_size, num_mines)
    revealed = [[' ' for _ in range(board_size)] for _ in range(board_size)]
    remaining_cells = board_size * board_size - num_mines

    message_label.config(text="")

    update_board()
    update_info_labels()

# 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)


# 게임 종료 메시지 표시
message_label = tk.Label(root, text="", font=("Helvetica", 16))
message_label.grid(row=board_size, columnspan=board_size)


# 다시하기 버튼 추가
restart_button = tk.Button(root, text="다시하기", command=restart_game, font=("Helvetica", 12))
restart_button.grid(row=board_size + 1, column=0, columnspan=board_size)

info_label_frame = tk.Frame(root)
info_label_frame.grid(row=board_size + 2, column=0, columnspan=board_size)

mine_count_label = tk.Label(info_label_frame, text=f"지뢰 개수: {num_mines}", font=("Helvetica", 12))
mine_count_label.grid(row=0, column=0)

remaining_cells_label = tk.Label(info_label_frame, text=f"남은 셀 개수: {remaining_cells}", font=("Helvetica", 12))
remaining_cells_label.grid(row=0, column=1)

# 게임 종료
def check_game_over():
    for row in range(board_size):
        for col in range(board_size):
            if revealed[row][col] == 'X':
                message_label.config(text="지뢰를 밟았습니다! 게임 종료!", fg='red')
                reveal_all_cells()  # 게임 종료 시 모든 셀 공개
                root.update()  # 화면 갱신
                # root.after(2000, root.destroy)  # 2초 후에 게임 종료
                return
    if remaining_cells == 0:
        message_label.config(text="축하합니다! 모든 안전한 셀을 찾았습니다. 게임 승리!")
        reveal_all_cells()  # 게임 종료 시 모든 셀 공개
        # root.update()  # 화면 갱신
        root.after(2000, root.destroy)  # 2초 후에 게임 종료

# 모든 셀을 공개하는 함수
def reveal_all_cells():
    for row in range(board_size):
        for col in range(board_size):
            if board[row][col] == '*':
                revealed[row][col] = 'X'  # 지뢰 셀은 'X'로 공개
            else:
                revealed[row][col] = str(count_mines_around(board, row, col))  # 나머지 셀은 주변 지뢰 개수로 공개

    update_board()  # 보드 업데이트

# 게임 실행
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', bg='light gray')
            else:
                # 표시할 텍스트가 '0'인 경우 빈 문자열로 표시하고, 그렇지 않은 경우에는 지뢰 개수로 표시
                text_to_display = revealed[row][col] if revealed[row][col] != '0' else ''
                buttons[row][col].config(text=text_to_display, state='disabled', bg='white')

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

    update_board()
    update_info_labels()
    check_game_over()

def right_click(event, row, col):
    if revealed[row][col] == ' ':
        revealed[row][col] = 'F'  # 깃발 표시
        buttons[row][col].config(text='F', disabledforeground='red')
    elif revealed[row][col] == 'F':
        revealed[row][col] = ' '  # 깃발 제거
        buttons[row][col].config(text='', disabledforeground='black')

    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()

전체 코드이다.

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

Next

다음엔 게임 외적인 부분을 보안해 주도록 하겠다.

728x90
반응형

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

지뢰찾기 게임 제작기 5  (0) 2024.04.02
지뢰찾기 게임 제작기 3  (0) 2024.03.19
지뢰찾기 게임 제작기 2  (7) 2024.03.12
지뢰찾기 게임 제작기 1  (0) 2024.03.05