Python과 PySimpleGUI를 사용하여 행맨 게임 구축

2023. 12. 11. 23:00python/basic

데모: Python과 PySimpleGUI를 사용한 행맨

ㅇ PySimpleGUI를 사용하여 행맨 게임용 GUI 만들기 ㅇ 행맨 게임에 대한 논리 구현 ㅇ 연결 게임 로직을 GUI 레이어와 연결 ㅇ 게임 결과 처리 및 기타 옵션 제공

프로젝트 개요

PySimpleGUI는 Tkinter, 와 같은 잘 알려진 여러 GUI 프레임워크 및 라이브러리를 둘러싼 래퍼입니다. wxPython 및 PyQt. GUI를 데이터로 디자인할 수 있습니다.

ㅇ 게임 설정: 행맨 게임은 두 명 이상의 플레이어가 참여하는 게임으로, 선택된 한 명과 한 명 이상의 플레이어 선택으로 구성됩니다. ㅇ 단어 선택: 선택 플레이어는 추측 플레이어가 추측할 단어를 선택합니다. - 선택한 단어는 전통적으로 단어의 각 문자에 대한 일련의 밑줄로 표시됩니다. ㅇ 선택 플레이어는 교수형 집행인 그림을 고정할 비계도 그립니다. ㅇ 추측: 추측하는 플레이어는 한 번에 하나씩 문자를 선택하여 단어를 추측하려고 시도합니다. ㅇ 피드백: 선택하는 플레이어는 추측한 각 문자가 단어에 나타나는지 여부를 나타냅니다. - 문자가 나타나면 선택 플레이어는 각 밑줄을 단어에 나타나는 문자로 바꿉니다. - 문자가 나타나지 않으면 선택 플레이어는 추측된 문자 목록에 문자를 씁니다. 그런 다음 매달린 남자의 다음 조각을 그립니다. 목매달린 남자를 그리기 위해서는 머리부터 시작해 몸통, 팔, 다리 총 6개의 부분을 그려야 한다. ㅇ 승리 조건: 6번의 잘못된 추측 후에 매달린 사람 그리기가 완료되면 선택한 플레이어가 승리하며, 이 경우 게임이 종료됩니다. 추측하는 플레이어가 단어를 추측하면 승리합니다. - 추측이 맞으면 게임이 종료되고 추측한 플레이어가 승리합니다. - 추측이 틀리면 게임은 계속됩니다.

Python으로 행맨 게임을 작성하는 동안 몇 가지 추가 디자인 결정을 내릴 것입니다.

ㅇ 게임은 컴퓨터와 한 명의 인간 플레이어 ㅇ 컴퓨터가 선택 플레이어 역할을 하며 선택 추측할 단어, 처리 사람의 입력 및 처리 모두 출력합니다. ㅇ 인간 플레이어가 추측 플레이어입니다. 간단히 플레이어라고 합니다. 플레이어가 단어를 알면 단어가 완성될 때까지 계속해서 올바른 문자를 추측합니다.

전제조건

ㅇ PySimpleGUI를 사용하여 GUI 앱 만들기 ㅇ 파일 및 with 문 작업 ㅇ 자신만의 클래스 정의클래스 및 객체 지향 프로그래밍 작업 ㅇ 쓰기 for 루프, while 루프 및 이해 ㅇ 작업조건문 ㅇ Python 작업 문자열, 목록 및 세트

1단계: 행맨 프로젝트 설정

!python -m venv venv
!source venv/bin/activate
!python -m pip install pysimplegui
DEPRECATION: Loading egg at /home/pulisul/.pyenv/versions/3.11.0/lib/python3.11/site-packages/llama-0.0.1-py3.11.egg is deprecated. pip 24.3 will enforce this behaviour change. A possible replacement is to use pip for package installation.. Discussion can be found at https://github.com/pypa/pip/issues/12330
Requirement already satisfied: pysimplegui in ./.pyenv/versions/3.11.0/lib/python3.11/site-packages (4.60.5)

2단계: 행맨 게임의 GUI 스케치

행맨용 UI를 디자인할 때 다음 원칙을 염두에 두어야 합니다.

ㅇ 게임 컨트롤은 사용자에게 직관적이어야 합니다. ㅇ 게임 컨트롤은 현재 게임 상태를 반영해야 합니다. ㅇ 게임 입력 및 출력은 적절한 GUI 구성요소를 활용해야 합니다.

행맨 게임의 GUI는 몇 가지 작업을 수행해야 합니다.

ㅇ 목매달린 남자를 그립니다. ㅇ 표시대상 단어. ㅇ 이전에 추측한 문자를 표시합니다. ㅇ 플레이어가 추측할 수 있도록 허용합니다새 문자. ㅇ 플레이어가 게임을 종료하도록 허용합니다.

3단계: 행맨용 스켈레톤 GUI 앱 코딩

아래의 코드들을 hangman.py 파일에 입력하십시요.

import PySimpleGUI as sg

class Hangman:
    def __init__(self) -> None:
        self._window = sg.Window(
            title="Hangman",
            layout=[[]],
            finalize=True,
            margins=(100, 100),
        )

    def read_event(self):
        event = self._window.read()
        event_id = event[0] if event is not None else None
        return event_id

    def close(self):
        self._window.close()
# ...

if __name__ == "__main__":
    game = Hangman()
    # Event loop
    while True:
        event_id = game.read_event()
        if event_id in {sg.WIN_CLOSED}:
            break
    game.close()

4단계: 행맨 GUI 코딩행맨 GUI 레이아웃

빨간색 윤곽선은 행으로 구분된 주요 UI 요소를 보여줍니다.

ㅇ 첫 번째 행에는 매달린 사람 그리기 영역 및 문자 버튼이 포함되어 있습니다. ㅇ 두 번째 행에는 추측되는 단어가 포함됩니다. ㅇ 세 번째 행에는 새로 만들기, 재설정, 및 종료 버튼이 있습니다.

class Hangman:
    def __init__(self) -> None:
        layout = [
            [
                self._build_canvas_frame(),
                self._build_letters_frame(),
            ],
            [
                self._build_guessed_word_frame(),
            ],
            [
                self._build_action_buttons_frame(),
            ],
        ]
        self._window = sg.Window(
            title="Hangman",
            layout=layout,
            finalize=True,
        )

행의 각 요소를 시각적으로 구분하려면 Frame 클래스를 사용합니다. 따라서 다음과 같은 4개의 프레임이 있습니다.

ㅇ 목매달린 사람 그림 영역 프레임 ㅇ 문자 버튼 프레임 ㅇ 타겟 단어 프레임 ㅇ 새, 재설정 및 종료 버튼 프레임

class Hangman:
    # ...

    def _build_canvas_frame(self):
        return sg.Frame(
            "Hangman",
            [
                [
                    sg.Graph(
                        key="-CANVAS-",
                        canvas_size=(200, 400),
                        graph_bottom_left=(0, 0),
                        graph_top_right=(200, 400),
                    )
                ]
            ],
            font="Any 20",
        )
from string import ascii_uppercase
# ...

class Hangman:
    # ...

    def _build_letters_frame(self):
        letter_groups = [
            ascii_uppercase[i : i + 4]
            for i in range(0, len(ascii_uppercase), 4)
        ]
        letter_buttons = [
            [
                sg.Button(
                    button_text=f" {letter} ",
                    font="Courier 20",
                    border_width=0,
                    button_color=(None, sg.theme_background_color()),
                    key=f"-letter-{letter}-",
                    enable_events=True,
                )
                for letter in letter_group
            ]
            for letter_group in letter_groups
        ]
        return sg.Column(
            [
                [
                    sg.Frame(
                        "Letters",
                        letter_buttons,
                        font="Any 20",
                    ),
                    sg.Sizer(),
                ]
            ]
        )
class Hangman:
    # ...

    def _build_guessed_word_frame(self):
        return sg.Frame(
            "",
            [
                [
                    sg.Text(
                        key="-DISPLAY-WORD-",
                        font="Courier 20",
                    )
                ]
            ],
            element_justification="center",
        )
class Hangman:
    
    def _build_action_buttons_frame(self):
        return sg.Frame(
            "",
            [
                [
                    sg.Sizer(h_pixels=90),
                    sg.Button(
                        button_text="New",
                        key="-NEW-",
                        font="Any 20",
                    ),
                    sg.Sizer(h_pixels=60),
                    sg.Button(
                        button_text="Restart",
                        key="-RESTART-",
                        font="Any 20",
                    ),
                    sg.Sizer(h_pixels=60),
                    sg.Button(
                        button_text="Quit",
                        key="-QUIT-",
                        font="Any 20",
                    ),
                    sg.Sizer(h_pixels=90),
                ]
            ],
            font="Any 20",
        )

매달린 남자 그리기

class Hangman:
    def __init__(self) -> None:
        # ...
        self._canvas = self._window["-CANVAS-"]
class Hangman:
    
    def _draw_scaffold(self):
        lines = [
            ((40, 55), (180, 55), 10),
            ((165, 60), (165, 365), 10),
            ((160, 360), (100, 360), 10),
            ((100, 365), (100, 330), 10),
            ((100, 330), (100, 310), 1),
        ]
        for *points, width in lines:
            self._canvas.DrawLine(*points, color="black", width=width)
class Hangman:
    
    def _draw_hanged_man(self):
        head = (100, 290)
        torso = [((100, 270), (100, 170))]
        left_arm = [
            ((100, 250), (80, 250)),
            ((80, 250), (60, 210)),
            ((60, 210), (60, 190)),
        ]
        right_arm = [
            ((100, 250), (120, 250)),
            ((120, 250), (140, 210)),
            ((140, 210), (140, 190)),
        ]
        left_leg = [
            ((100, 170), (80, 170)),
            ((80, 170), (70, 140)),
            ((70, 140), (70, 80)),
            ((70, 80), (60, 80)),
        ]
        right_leg = [
            ((100, 170), (120, 170)),
            ((120, 170), (130, 140)),
            ((130, 140), (130, 80)),
            ((130, 80), (140, 80)),
        ]
        body = [
            torso,
            left_arm,
            right_arm,
            left_leg,
            right_leg,
        ]
        if self._wrong_guesses == 1:
            self._canvas.DrawCircle(
                head,
                20,
                line_color="red",
                line_width=2,
            )
        elif self._wrong_guesses > 1:
            for part in body[self._wrong_guesses - 2]:
                self._canvas.DrawLine(*part, color="red", width=2)
MAX_WRONG_GUESSES = 6

class Hangman:
    def __init__(self) -> None:
        # ...
        self._canvas = self._window["-CANVAS-"]
        # Temporary code
        self._draw_scaffold()
        for index in range(MAX_WRONG_GUESSES):
            self._wrong_guesses = index + 1
            self._draw_hanged_man()

5단계: 게임의 로직을 코딩하고 GUI에 연결

다음 작업을 처리하는 코드를 작성해야 합니다.

ㅇ 타겟 단어 선택 ㅇ 추적하여 추측한 단어 또는 문자 ㅇ 새 게임 설정 ㅇ 플레이어의 움직임 처리 ㅇ 게임 종료 여부 확인

타겟 단어 선택

hangman.py와 같은 디렉토리에 넣어 주세요.

words.txt

prettiest close dog massive hollow cultured seashore explode dizzy minister competent thoughtful harbor tidy dance children zesty clean ball nostalgic plan week strap board slope bat steep mourn cat girl ancient street mice dare wasteful tub limping whimsical eager eggs detail experience beds train place cows admit rare respect loose group enjoy internal macabre imported superb crooked confused hug feigned unkempt coal meddle hapless country zealous sick pray lake tiny key empty labored delirious ants need omniscient onerous damp subtract sack connection toad gather record new trashy flow river sparkling kneel daughter glue allow raspy eminent weak wrong pretend receipt celery plain fire heal damaging honorable foot ignorant substance box crime giant learned itchy smoke likable station jaded innocent dead straw tray chin pack geese guess wealthy slippery book curly swing cure flowers rate ignore insidious necessary snakes entertaining rich comb lamentable fuel camera multiply army exist sulky brief worried third magical wary laborer end somber authority rainstorm anxious purpose agreeable spiky toe mixed waiting hungry lopsided flagrant windy ground slap please white hurry governor abandoned reject spiritual abrasive hunt weather endurable hobbies occur bake print tire juicy blush listen trousers daffy scarecrow rude stem bustling nail sneeze bellicose love

from random import choice
# ...

class Hangman:
    # ...

    def _select_word(self):
        with open("words.txt", mode="r", encoding="utf-8") as words:
            word_list = words.readlines()
        return choice(word_list).strip().upper()

추측된 단어 추적

class Hangman:
    # ...

    def _build_guessed_word(self):
        current_letters = []
        for letter in self._target_word:
            if letter in self._guessed_letters:
                current_letters.append(letter)
            else:
                current_letters.append("_")
        return " ".join(current_letters)

게임 설정 또는 다시 시작

ㅇ 타겟 단어, ._target_word ㅇ 추측한 글자, ._guessed_letters ㅇ 잘못 추측한 횟수, ._wrong_guesses ㅇ 추측 단어 표현 ._guessed_word

class Hangman:
    # ...

    def _new_game(self):
        self._target_word = self._select_word()
        self._restart_game()
class Hangman:
    # ...

    def _restart_game(self):
        self._guessed_letters = set()
        self._wrong_guesses = 0
        self._guessed_word = self._build_guessed_word()
        # Restart GUI
        self._canvas.erase()
        self._draw_scaffold()
        for letter in ascii_uppercase:
            self._window[f"-letter-{letter}-"].update(disabled=False)
        self._window["-DISPLAY-WORD-"].update(self._guessed_word)

플레이어의 입력 처리

class Hangman:
    # ...

    def read_event(self):
        # ...

    def process_event(self, event):
        if event[:8] == "-letter-":
            self._play(letter=event[8])
        elif event == "-RESTART-":
            self._restart_game()
        elif event == "-NEW-":
            self._new_game()
class Hangman:
    # ...

    def _restart_game(self):
        # ...

    def _play(self, letter):
        if letter not in self._target_word:
            self._wrong_guesses += 1
        self._guessed_letters.add(letter)
        self._guessed_word = self._build_guessed_word()
        # Update GUI
        self._window[f"-letter-{letter}-"].update(disabled=True)
        self._window["-DISPLAY-WORD-"].update(self._guessed_word)
        self._draw_hanged_man()

게임이 끝났는지 확인

  1. 플레이어가 6번의 잘못된 추측을 했으며, 이는 플레이어가 패
  2. 플레이어가 단어를 정확하게 추측했습니다. 즉, 플레이어가 승리

class Hangman:
    # ...

    def process_event(self, event):
        # ...

    def is_over(self):
        return any(
            [
                self._wrong_guesses == MAX_WRONG_GUESSES,
                set(self._target_word) <= self._guessed_letters,
            ]
        )

이벤트 루프 업데이트 및 게임 실행

if __name__ == "__main__":
    game = Hangman()
    # Event loop
    while not game.is_over():
        event_id = game.read_event()
        if event_id in {sg.WIN_CLOSED}:
            break
        game.process_event(event_id)
    game.close()

6단계: 게임 결과 처리

class Hangman:
    # ...

    def is_over(self):
        # ...

    def check_winner(self):
        if self._wrong_guesses < MAX_WRONG_GUESSES:
            answer = sg.PopupYesNo(
                "You've won! Congratulations!\n"
                "Another round?",
                title="Winner!",
            )
        else:
            answer = sg.PopupYesNo(
                f"You've lost! The word was '{self._target_word}'.\n"
                "Another round?",
                title="Sorry!",
            )
        self.quit = answer == "No"
        if not self.quit:
            self._new_game()
class Hangman:
    def __init__(self):
        # ...
        self._new_game()
        self.quit = False
if __name__ == "__main__":
    game = Hangman()
    # Rounds
    while not game.quit:
        # Event loop
        while not game.is_over():
            event_id = game.read_event()
            if event_id in {sg.WIN_CLOSED, "-QUIT-"}:
                game.quit = True
                break
            game.process_event(event_id)

        if not game.quit:
            game.check_winner()

    game.close()

점수 및 결과 유지

# ...

class Hangman:
    def __init__(self):
        # ...
        self.quit = False
        self._played_games = 0
        self._won_games = 0

    # ...
# ...

class Hangman:
    # ...

    def check_winner(self):
        self._played_games += 1
        if self._wrong_guesses < MAX_WRONG_GUESSES:
            self._won_games += 1
            answer = sg.PopupYesNo(
                "You've won! Congratulations!\n"
                f"That's {self._won_games} out of {self._played_games}!\n"
                "Another round?",
                title="Winner!",
            )
        else:
            answer = sg.PopupYesNo(
                f"You've lost! The word was '{self._target_word}'.\n"
                f"That's {self._won_games} out of {self._played_games}!\n"
                "Another round?",
                title="Sorry!",
            )
        self.quit = answer == "No"
        if not self.quit:
            self._new_game()

    # ...

다음 단계

이제 행맨 게임 제작을 마쳤으므로 몇 가지 추가 기능을 구현하여 한 단계 더 나아갈 수 있습니다. 새로운 기능을 직접 추가하면 흥미롭고 새로운 주제에 대해 배우는 데 도움이 됩니다.

새로운 기능에 대한 몇 가지 아이디어는 다음과 같습니다.

ㅇ 특수 단어 목록 사용: 게임에 다양한 단어 목록을 추가하여 청중과 관심 분야에 따라 가장 편리한 단어 목록을 선택할 수 있습니다. ㅇ 난이도 설정: 예를 들어 단어 길이, 인기도 등을 기준으로 난이도별로 대상 단어를 분류할 수 있습니다. ㅇ 외국어 단어 사용: 플레이어가 언어 능력을 연습할 수 있도록 다양한 언어로 단어 목록을 만들 수 있습니다. ㅇ 음향 효과 추가: 게임에 맞춤 음향 효과를 추가하여 가장 중요한 동작을 강조할 수 있습니다. 이 기능을 구현하려면 Python에서 사운드 재생에 사용 가능한 옵션 중 일부를 살펴보세요. ㅇ 문자 버튼의 배경색 조정: 플레이어가 이미 클릭한 문자 버튼에 대해 다른 배경색을 설정할 수 있습니다. 이 동작은 플레이어에게 시각적 힌트를 제공하고 게임에 대한 전체적인 그림을 향상시킵니다. 이제 행맨 게임 제작을 마쳤으므로 기어를 바꾸고 다른 멋진 프로젝트로 뛰어들 수 있습니다. Python을 계속 배우고 게임을 구축하기 위한 몇 가지 훌륭한 다음 단계는 다음과 같습니다.

ㅇ Python과 Tkinter를 사용하여 Tic-Tac-Toe 게임 만들기: 이 단계별 프로젝트에서는 tic-tac-toe 게임을 만드는 방법을 배우게 됩니다. Python과 Tkinter GUI 프레임워크를 사용합니다. Tkinter는 크로스 플랫폼이며 Python 표준 라이브러리에서 사용할 수 있습니다. ㅇ Python과 Pygame을 사용하여 Asteroids 게임 빌드: 이 튜토리얼에서는 Pygame을 사용하여 Python으로 Asteroids 게임의 복제본을 빌드합니다. 단계별로 이미지, 입력 처리, 게임 로직, 사운드, 텍스트를 프로그램에 추가하게 됩니다. ㅇ 아케이드를 사용하여 Python으로 플랫폼 게임 구축: 이 단계별 튜토리얼에서는 아케이드 라이브러리를 사용하여 Python으로 플랫폼 게임을 구축합니다. 레벨 디자인, 자산 소싱, 고급 기능 구현을 위한 기술을 다루게 됩니다.

hangman.py 전체

from random import choice
from string import ascii_uppercase

import PySimpleGUI as sg

MAX_WRONG_GUESSES = 6


class Hangman:
    def __init__(self):
        layout = [
            [
                self._build_canvas_frame(),
                self._build_letters_frame(),
            ],
            [
                self._build_guessed_word_frame(),
            ],
            [
                self._build_action_buttons_frame(),
            ],
        ]
        self._window = sg.Window(title="Hangman", layout=layout, finalize=True)
        self._canvas = self._window["-CANVAS-"]
        self._new_game()
        self.quit = False
        self._played_games = 0
        self._won_games = 0

    def _build_canvas_frame(self):
        return sg.Frame(
            "Hangman",
            [
                [
                    sg.Graph(
                        key="-CANVAS-",
                        canvas_size=(200, 400),
                        graph_bottom_left=(0, 0),
                        graph_top_right=(200, 400),
                    )
                ]
            ],
            font="Any 20",
        )

    def _build_letters_frame(self):
        letter_groups = [
            ascii_uppercase[i : i + 4]
            for i in range(0, len(ascii_uppercase), 4)
        ]
        letter_buttons = [
            [
                sg.Button(
                    button_text=f" {letter} ",
                    font="Courier 20",
                    border_width=0,
                    button_color=(None, sg.theme_background_color()),
                    key=f"-letter-{letter}-",
                    enable_events=True,
                )
                for letter in letter_group
            ]
            for letter_group in letter_groups
        ]
        return sg.Column(
            [
                [
                    sg.Frame(
                        "Letters",
                        letter_buttons,
                        font="Any 20",
                    ),
                    sg.Sizer(),
                ]
            ]
        )

    def _build_guessed_word_frame(self):
        return sg.Frame(
            "",
            [
                [
                    sg.Text(
                        key="-DISPLAY-WORD-",
                        font="Courier 20",
                    )
                ]
            ],
            element_justification="center",
        )

    def _build_action_buttons_frame(self):
        return sg.Frame(
            "",
            [
                [
                    sg.Sizer(h_pixels=90),
                    sg.Button(
                        button_text="New",
                        key="-NEW-",
                        font="Any 20",
                    ),
                    sg.Sizer(h_pixels=60),
                    sg.Button(
                        button_text="Restart",
                        key="-RESTART-",
                        font="Any 20",
                    ),
                    sg.Sizer(h_pixels=60),
                    sg.Button(
                        button_text="Quit",
                        key="-QUIT-",
                        font="Any 20",
                    ),
                    sg.Sizer(h_pixels=90),
                ]
            ],
            font="Any 20",
        )

    def _draw_scaffold(self):
        lines = [
            ((40, 55), (180, 55), 10),
            ((165, 60), (165, 365), 10),
            ((160, 360), (100, 360), 10),
            ((100, 365), (100, 330), 10),
            ((100, 330), (100, 310), 1),
        ]
        for *points, width in lines:
            self._canvas.DrawLine(*points, color="black", width=width)

    def _draw_hanged_man(self):
        head = (100, 290)
        torso = [((100, 270), (100, 170))]
        left_arm = [
            ((100, 250), (80, 250)),
            ((80, 250), (60, 210)),
            ((60, 210), (60, 190)),
        ]
        right_arm = [
            ((100, 250), (120, 250)),
            ((120, 250), (140, 210)),
            ((140, 210), (140, 190)),
        ]
        left_leg = [
            ((100, 170), (80, 170)),
            ((80, 170), (70, 140)),
            ((70, 140), (70, 80)),
            ((70, 80), (60, 80)),
        ]
        right_leg = [
            ((100, 170), (120, 170)),
            ((120, 170), (130, 140)),
            ((130, 140), (130, 80)),
            ((130, 80), (140, 80)),
        ]
        body = [
            torso,
            left_arm,
            right_arm,
            left_leg,
            right_leg,
        ]
        if self._wrong_guesses == 1:
            self._canvas.DrawCircle(head, 20, line_color="red", line_width=2)
        elif self._wrong_guesses > 1:
            for part in body[self._wrong_guesses - 2]:
                self._canvas.DrawLine(*part, color="red", width=2)

    def _select_word(self):
        with open("words.txt", mode="r", encoding="utf-8") as words:
            word_list = words.readlines()
        return choice(word_list).strip().upper()

    def _build_guessed_word(self):
        current_letters = []
        for letter in self._target_word:
            if letter in self._guessed_letters:
                current_letters.append(letter)
            else:
                current_letters.append("_")
        return " ".join(current_letters)

    def _new_game(self):
        self._target_word = self._select_word()
        self._restart_game()

    def _restart_game(self):
        self._guessed_letters = set()
        self._wrong_guesses = 0
        self._guessed_word = self._build_guessed_word()
        # Restart GUI
        self._canvas.erase()
        self._draw_scaffold()
        for letter in ascii_uppercase:
            self._window[f"-letter-{letter}-"].update(disabled=False)
        self._window["-DISPLAY-WORD-"].update(self._guessed_word)

    def _play(self, letter):
        if letter not in self._target_word:
            self._wrong_guesses += 1
        self._guessed_letters.add(letter)
        self._guessed_word = self._build_guessed_word()
        # Update GUI
        self._window[f"-letter-{letter}-"].update(disabled=True)
        self._window["-DISPLAY-WORD-"].update(self._guessed_word)
        self._draw_hanged_man()

    def read_event(self):
        event = self._window.read()
        event_id = event[0] if event is not None else None
        return event_id

    def process_event(self, event):
        if event[:8] == "-letter-":
            self._play(letter=event[8])
        elif event == "-RESTART-":
            self._restart_game()
        elif event == "-NEW-":
            self._new_game()

    def is_over(self):
        return any(
            [
                self._wrong_guesses == MAX_WRONG_GUESSES,
                set(self._target_word) <= self._guessed_letters,
            ]
        )

    def check_winner(self):
        self._played_games += 1
        if self._wrong_guesses < MAX_WRONG_GUESSES:
            self._won_games += 1
            answer = sg.PopupYesNo(
                "You've won! Congratulations!\n"
                f"That's {self._won_games} out of {self._played_games}!\n"
                "Another round?",
                title="Winner!",
            )
        else:
            answer = sg.PopupYesNo(
                f"You've lost! The word was '{self._target_word}'.\n"
                f"That's {self._won_games} out of {self._played_games}!\n"
                "Another round?",
                title="Sorry!",
            )
        self.quit = answer == "No"
        if not self.quit:
            self._new_game()

    def close(self):
        self._window.close()


if __name__ == "__main__":
    game = Hangman()
    # Rounds
    while not game.quit:
        # Event loop
        while not game.is_over():
            event_id = game.read_event()
            if event_id in {sg.WIN_CLOSED, "-QUIT-"}:
                game.quit = True
                break
            game.process_event(event_id)

        if not game.quit:
            game.check_winner()

    game.close()

출처 : https://realpython.com/hangman-python-pysimplegui/