pygame-hangman


Pygame

파이게임 설치

pip install pygame

행맨 게임

  • 컴퓨터가 단어를 하나 생각함
  • 밑줄로 표시
  • 유저가 단어에 포함될 거 같은 글자를 하나씩 입력
  • 포함이 되면 그 위치에 글자를 표시
  • 포함이 안되면 사람을 하나씩 그림
  • 사람이 먼저 완성이 되면 컴퓨터 승
  • 단어가 먼저 완성이 되면 유저 승

게임 구성 요소

  • 프로그래밍
  • 시나리오
  • 디자인

프로그래밍

  • 단어를 하나 생각
word = "man"
word = word.upper() #대문자만 가정
  • 유저는 이 글자를 몰라야 함
word_show = "_" * len(word)
  • 게임이 끝날 때 까지 알고리즘 반복
    • 보통 while문 사용
try_num = 0
ok_list = []
no_list = []
while True:
    ans = input().upper() #유저 입력 저장 변수
    print(ans)

    result = word.find(ans)
    if result == -1: #없음
        print("X")
        try_num += 1
        no_list.append(ans)
    else:
        print("O")
        ok_list.append(ans)
        for i in range(len(word)):
            if word[i] == ans:
                word_show = word_show[:i] + ans + word_show[i+1:]
        print(word_show)
    if try_num == 7: break
    if word_show.find("_") == -1: break
  • 이 코드는 행맨 게임의 기본적인 알고리즘
  • 유저의 입력을 받는다
  • 단어에서 그 글자가 있는지 찾는다
  • 글자가 단어에 없으면 실패, 있으면 성공 카운트
  • 성공하면 word_show밑줄이 그 글자로 바뀜

단어 랜덤으로 불러오기

import random
f = open("voca.txt", "r", encoding = 'UTF-8')
raw_data = f.read()
f.close

data_list = raw_data.split("\n") #voca.txt에 있는 한글 뜻은 필요없음. 전처리
data_list = data_list[:-1]
print(data_list[:-1])
while True:
    random_index = random.randrange(0, len(data_list))
    word = data_list[random_idx].replace(u"\xa0", u" ").split(" ")[1]
    if len(word) <= 6:
        break
word = word.upper()
word_show = "_" * len(word)
quit()#이 코드를 쓰면 밑에 줄은 실행X

게임창 띄우기

import pygame
pygame.init()
size = [500, 900]
screen = pygame.display.set_mode(size)
title = "hangman"
clock = pygame.time.Clock()
black = (0,0,0)
white = (255,255,255)

#실수를 정수로 만들어줌
def tup_r(tup):
    temp_list = []
    for a in tup:
        temp_list.append(round(a))
    return tuple(temp_list)

exit = False

# 4. 메인 이벤트
while not exit:
    # 4-1. FPS 설정
    clock.tick(60)
    # 4-2. 각종 입력 감지
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            exit = True
    # 4-3. 입력, 시간에 따른 변화
    # 4-4. 그리기
    screen.fill(black)
    A = tup_r((0, size[1]*2/3))
    B = (size[0], A[1])
    C = tup_r((size[0]/6 , A[1]))
    D = (C[0], C[0])
    E = tup_r((size[0]/2, D[1]))
    pygame.draw.line(screen, white, A, B, 3)
    pygame.draw.line(screen, white, C, D, 3)
    pygame.draw.line(screen, white, D, E, 3)
    # 4-5. 업데이트
    pygame.display.flip()
# 5. 게임 종료
pygame.quit()
  • 파이게임에서 게임창은 픽셀 단위
  • 게임창에 무언가를 그리려면 픽셀 단위로 입력(좌표 활용)
  • 좌표 계산을 잘해야함
  • 원은 원의 중심 좌표와 반지름 길이를 이용하여 그림
  • 꺾이는 부분은 분기점에서 각도 계산

새로운 이벤트 추가(시간이지나면서 빨간선 그려지고 다 그려지면 사람 떨어짐)

import pygame, math

# 1. 게임 초기화
pygame.init()
# 2. 게임창 옵션 설정
size = [500,900]
screen = pygame.display.set_mode(size)
title = "HANGMAN"
pygame.display.set_caption(title)
# 3. 게임 내 필요한 설정
clock = pygame.time.Clock()
black = (0,0,0)
white = (255,255,255)
red = (255,0,0)

def tup_r(tup):
    temp_list = []
    for a in tup:
        temp_list.append(round(a))
    return tuple(temp_list)
drop = False
exit = False
k = 0
# 4. 메인 이벤트
while not exit:
    # 4-1. FPS 설정
    clock.tick(60)
    # 4-2. 각종 입력 감지
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            exit = True
    # 4-3. 입력, 시간에 따른 변화
    k += 1
    # 4-4. 그리기
    screen.fill(black)
    A = tup_r((0, size[1]*2/3))
    B = (size[0], A[1])
    C = tup_r((size[0]/6 , A[1]))
    D = (C[0], C[0])
    E = tup_r((size[0]/2, D[1]))
    pygame.draw.line(screen, white, A, B, 3)
    pygame.draw.line(screen, white, C, D, 3)
    pygame.draw.line(screen, white, D, E, 3)
    F = tup_r((E[0], E[1]+size[0]/6))
    if drop == False:
        pygame.draw.line(screen, white, E, F, 3)
    r_head = round(size[0]/12)
    if drop == True : G = (F[0],F[1]+r_head+k*10)
    else : G = (F[0],F[1]+r_head)
    pygame.draw.circle(screen, white, G, r_head, 3)
    H = (G[0], G[1]+r_head)
    I = (H[0], H[1]+r_head)
    pygame.draw.line(screen, white, H, I, 3)
    l_arm = r_head*2
    J = (I[0]-l_arm*math.cos(30*math.pi/180),
         I[1]+l_arm*math.sin(30*math.pi/180))
    K = (I[0]+l_arm*math.cos(30*math.pi/180),
         I[1]+l_arm*math.sin(30*math.pi/180))
    J = tup_r(J)
    K = tup_r(K)
    pygame.draw.line(screen, white, I, J, 3)
    pygame.draw.line(screen, white, I, K, 3)
    L = (I[0], I[1]+l_arm)
    pygame.draw.line(screen, white, I, L, 3)
    l_leg = round(l_arm * 1.5)
    M = (L[0]-l_leg*math.cos(60*math.pi/180),
         L[1]+l_leg*math.sin(60*math.pi/180))
    N = (L[0]+l_leg*math.cos(60*math.pi/180),
         L[1]+l_leg*math.sin(60*math.pi/180))  
    M = tup_r(M)
    N = tup_r(N)  
    pygame.draw.line(screen, white, L, M, 3)
    pygame.draw.line(screen, white, L, N, 3)  
    if drop == False:
        O = tup_r((size[0]/2-size[0]/6, E[1]/2+F[1]/2))
        P = (O[0]+k*2, O[1])
        if P[0] > size[0]/2+size[0]/6 :
            P = tup_r((size[0]/2+size[0]/6, O[1]))
            drop = True
            k = 0
        pygame.draw.line(screen, red, O, P, 3)
    # 4-5. 업데이트
    pygame.display.flip()
# 5. 게임 종료
pygame.quit()
  • 시간이 지나면서 k가 1 증가
  • drop을 False로 초기화
  • drop이 false면 선을 그림(O부터 P까지)
  • O는 고정, P는 계속 증가(k값 이용)
  • 만약 P가 일정 길이까지 도달하면
  • drop이 True가 됨, k는 다시 0으로 됨

사용자의 입력 받음

import pygame, math

# 1. 게임 초기화
pygame.init()
# 2. 게임창 옵션 설정
size = [500,900]
screen = pygame.display.set_mode(size)
title = "HANGMAN"
pygame.display.set_caption(title)
# 3. 게임 내 필요한 설정
clock = pygame.time.Clock()
black = (0,0,0)
white = (255,255,255)
red = (255,0,0)
hint_font = pygame.font.Font("C:/Windows/Fonts/arial.ttf", 80)
entry_font = pygame.font.Font("C:/Windows/Fonts/arial.ttf", 60)
def tup_r(tup):
    temp_list = []
    for a in tup:
        temp_list.append(round(a))
    return tuple(temp_list)
entry_text = ""
try_num = 0
drop = False
exit = False
k = 0
# 4. 메인 이벤트
while not exit:
    # 4-1. FPS 설정
    clock.tick(60)
    # 4-2. 각종 입력 감지
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            exit = True
        if event.type == pygame.KEYDOWN:
            key_name = pygame.key.name(event.key)
            if len(key_name) == 1:
                if (ord(key_name) >= 65 and ord(key_name) <= 90) or (ord(key_name) >= 97 and ord(key_name) <= 122):
                    entry_text = key_name.upper()
                else : entry_text = ""
            else : entry_text = ""
            print(key_name)

    # 4-3. 입력, 시간에 따른 변화
    k += 1
    # 4-4. 그리기
    screen.fill(black)
    A = tup_r((0, size[1]*2/3))
    B = (size[0], A[1])
    C = tup_r((size[0]/6 , A[1]))
    D = (C[0], C[0])
    E = tup_r((size[0]/2, D[1]))
    pygame.draw.line(screen, white, A, B, 3)
    pygame.draw.line(screen, white, C, D, 3)
    pygame.draw.line(screen, white, D, E, 3)
    F = tup_r((E[0], E[1]+size[0]/6))
    if drop == False:
        pygame.draw.line(screen, white, E, F, 3)
    r_head = round(size[0]/12)
    if drop == True : G = (F[0],F[1]+r_head+k*10)
    else : G = (F[0],F[1]+r_head)
    pygame.draw.circle(screen, white, G, r_head, 3)
    H = (G[0], G[1]+r_head)
    I = (H[0], H[1]+r_head)
    pygame.draw.line(screen, white, H, I, 3)
    l_arm = r_head*2
    J = (I[0]-l_arm*math.cos(30*math.pi/180),
         I[1]+l_arm*math.sin(30*math.pi/180))
    K = (I[0]+l_arm*math.cos(30*math.pi/180),
         I[1]+l_arm*math.sin(30*math.pi/180))
    J = tup_r(J)
    K = tup_r(K)
    pygame.draw.line(screen, white, I, J, 3)
    pygame.draw.line(screen, white, I, K, 3)
    L = (I[0], I[1]+l_arm)
    pygame.draw.line(screen, white, I, L, 3)
    l_leg = round(l_arm * 1.5)
    M = (L[0]-l_leg*math.cos(60*math.pi/180),
         L[1]+l_leg*math.sin(60*math.pi/180))
    N = (L[0]+l_leg*math.cos(60*math.pi/180),
         L[1]+l_leg*math.sin(60*math.pi/180))  
    M = tup_r(M)
    N = tup_r(N)  
    pygame.draw.line(screen, white, L, M, 3)
    pygame.draw.line(screen, white, L, N, 3)  
    if drop == False and try_num == 8:
        O = tup_r((size[0]/2-size[0]/6, E[1]/2+F[1]/2))
        P = (O[0]+k*2, O[1])
        if P[0] > size[0]/2+size[0]/6 :
            P = tup_r((size[0]/2+size[0]/6, O[1]))
            drop = True
            k = 0
        pygame.draw.line(screen, red, O, P, 3)
    # 힌트 표시하기
    word_show = "___"
    hint = hint_font.render(word_show, True, white)
    hint_size = hint.get_size()
    hint_pos = tup_r((size[0]/2-hint_size[0]/2, size[1]*5/6-hint_size[1]/2))
    screen.blit(hint, hint_pos)
    # 입력창 표시하기
    entry = entry_font.render(entry_text, True, black)
    entry_size = entry.get_size()
    entry_pos = tup_r((size[0]/2-entry_size[0]/2, size[1]*17/18-entry_size[1]/2))
    entry_bg_size = 80
    pygame.draw.rect(screen, white, tup_r((size[0]/2-entry_bg_size/2, size[1]*17/18-entry_bg_size/2
                                     ,entry_bg_size ,entry_bg_size)))
    screen.blit(entry, entry_pos)
    # 4-5. 업데이트
    pygame.display.flip()
# 5. 게임 종료
pygame.quit()
  • KEYDOWN(키가 눌러짐)이 이벤트의 조건
  • key, upper를 활용해 소문자를 대문자로 바꿈
  • key를 입력할 때 마다 key_name출력
  • 힌트 표시, 입력창 표시
  • 입력창 안에 입력한 값을 띄움
  • entry_text를 entry_font로 렌더링(entry변수에 저장)
  • 입력창 안에 entry 띄움

로직 코드 결합

import pygame, math, random

# 1. 게임 초기화
pygame.init()
# 2. 게임창 옵션 설정
size = [500,900]
screen = pygame.display.set_mode(size)
title = "HANGMAN"
pygame.display.set_caption(title)
# 3. 게임 내 필요한 설정
clock = pygame.time.Clock()
black = (0,0,0)
white = (255,255,255)
red = (255,0,0)
hint_font = pygame.font.Font("C:/Windows/Fonts/Arial.ttf", 80)
entry_font = pygame.font.Font("C:/Windows/Fonts/Arial.ttf", 60)
no_font = pygame.font.Font("C:/Windows/Fonts/Arial.ttf", 40)
def tup_r(tup):
    temp_list = []
    for a in tup:
        temp_list.append(round(a))
    return tuple(temp_list)
entry_text = ""
drop = False
enter_go = False
exit = False

#  A가 영어 단어를 1개 생각한다.
f = open("voca.txt","r",encoding='UTF-8')
raw_data = f.read()
f.close()
data_list = raw_data.split("\n")
data_list = data_list[:-1]
while True:
    r_index = random.randrange(0,len(data_list))
    word = data_list[r_index].replace(u"\xa0", u" ").split(" ")[1]
    if len(word) <= 6 :break
word = word.upper()

#  단어의 글자 수만큼 밑줄을 긋는다.
word_show = "_"*len(word)
try_num = 0
ok_list = []
no_list = []

k = 0
# 4. 메인 이벤트
while not exit:
    # 4-1. FPS 설정
    clock.tick(60)
    # 4-2. 각종 입력 감지
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            exit = True
        if event.type == pygame.KEYDOWN:
            key_name = pygame.key.name(event.key)
            if (key_name == "return" or key_name == "enter"):
                if entry_text != "" and (ok_list+no_list).count(entry_text) == 0 :
                    enter_go = True            
            elif len(key_name) == 1:
                if (ord(key_name) >= 65 and ord(key_name) <= 90) or (ord(key_name) >= 97 and ord(key_name) <= 122):
                    entry_text = key_name.upper()
                else : entry_text = ""
            else : entry_text = ""

    # 4-3. 입력, 시간에 따른 변화
    if try_num == 8 : k += 1
    if enter_go == True:
        ans = entry_text
        result = word.find(ans)
        if result == -1 : #없음
            try_num += 1
            no_list.append(ans)
        else : #있음
            ok_list.append(ans)
            for i in range(len(word)):
                if word[i] == ans:
                    word_show = word_show[:i] + ans + word_show[i+1:]
        enter_go = False
        entry_text = ""
    # 4-4. 그리기
    screen.fill(black)
    A = tup_r((0, size[1]*2/3))
    B = (size[0], A[1])
    C = tup_r((size[0]/6 , A[1]))
    D = (C[0], C[0])
    E = tup_r((size[0]/2, D[1]))
    pygame.draw.line(screen, white, A, B, 3)
    pygame.draw.line(screen, white, C, D, 3)
    pygame.draw.line(screen, white, D, E, 3)
    F = tup_r((E[0], E[1]+size[0]/6))
    if drop == False:
        pygame.draw.line(screen, white, E, F, 3)
    r_head = round(size[0]/12)
    if drop == True : G = (F[0],F[1]+r_head+k*10)
    else : G = (F[0],F[1]+r_head)
    if try_num >= 1 : pygame.draw.circle(screen, white, G, r_head, 3)
    H = (G[0], G[1]+r_head)
    I = (H[0], H[1]+r_head)
    if try_num >= 2 :pygame.draw.line(screen, white, H, I, 3)
    l_arm = r_head*2
    J = (I[0]-l_arm*math.cos(30*math.pi/180),
         I[1]+l_arm*math.sin(30*math.pi/180))
    K = (I[0]+l_arm*math.cos(30*math.pi/180),
         I[1]+l_arm*math.sin(30*math.pi/180))
    J = tup_r(J)
    K = tup_r(K)
    if try_num >= 3 :pygame.draw.line(screen, white, I, J, 3)
    if try_num >= 4 :pygame.draw.line(screen, white, I, K, 3)
    L = (I[0], I[1]+l_arm)
    if try_num >= 5 :pygame.draw.line(screen, white, I, L, 3)
    l_leg = round(l_arm * 1.5)
    M = (L[0]-l_leg*math.cos(60*math.pi/180),
         L[1]+l_leg*math.sin(60*math.pi/180))
    N = (L[0]+l_leg*math.cos(60*math.pi/180),
         L[1]+l_leg*math.sin(60*math.pi/180))  
    M = tup_r(M)
    N = tup_r(N)  
    if try_num >= 6 :pygame.draw.line(screen, white, L, M, 3)
    if try_num >= 7 :pygame.draw.line(screen, white, L, N, 3)  
    if drop == False and try_num == 8:
        O = tup_r((size[0]/2-size[0]/6, E[1]/2+F[1]/2))
        P = (O[0]+k*2, O[1])
        if P[0] > size[0]/2+size[0]/6 :
            P = tup_r((size[0]/2+size[0]/6, O[1]))
            drop = True
            k = 0
        pygame.draw.line(screen, red, O, P, 3)
    # 힌트 표시하기
    hint = hint_font.render(word_show, True, white)
    hint_size = hint.get_size()
    hint_pos = tup_r((size[0]/2-hint_size[0]/2, size[1]*5/6-hint_size[1]/2))
    screen.blit(hint, hint_pos)
    # 입력창 표시하기
    entry = entry_font.render(entry_text, True, black)
    entry_size = entry.get_size()
    entry_pos = tup_r((size[0]/2-entry_size[0]/2, size[1]*17/18-entry_size[1]/2))
    entry_bg_size = 80
    pygame.draw.rect(screen, white, tup_r((size[0]/2-entry_bg_size/2, size[1]*17/18-entry_bg_size/2
                                     ,entry_bg_size ,entry_bg_size)))
    screen.blit(entry, entry_pos)
    # 오답 표시하기
    no_text = " ".join(no_list)
    no = no_font.render(no_text, True, red)
    no_pos = tup_r((20, size[1]*2/3+20))
    screen.blit(no, no_pos)
    # 4-5. 업데이트
    pygame.display.flip()
# 5. 게임 종료
pygame.quit()
  • 게임의 로직(시나리오, 텍스트 파일에서 단어를 랜덤으로 가져옴. 한글자씩 입력해서 맞는지 틀렸는지 판단)을 결합
  • 오답 표시
    • no_text변수에 저장
  • 승리 판단(종료)은 그림 그리기 전에 구현
  • set_alpha = 투명도
  • 시작화면은 while문 시작하기 전에 구현