[python] 기억력 테스트 게임

기억력 테스트 게임, 침팬지를 이겨라


🦧 코딩 계기

내일배움캠프 2기의 사전과제를 수행하기 위해 코딩을 하게 되었다. 어제 진행했던 GUI 계산기를 코딩하면서도 재미있었는데, 게임을 만들게 되어 뭔가 더 새롭다. 코딩 테스트 공부 하다가 막히면 이런 프로젝트를 찾아서 해볼걸이라고 후회하게 되는 것 같다. 여러 번 복습해 내 것으로 만들도록 할 것이다.


🦧 개념

🔥 pygame

  • 비디오 게임 작성용으로 설계된 크로스 플랫폼 Python 모듈 세트

🦧 코드

🔥게임의 프레임

import pygame

# 초기화
pygame.init()
# 가로 및 세로 크기
screen_width = 1280
screen_height = 720
# 게임 창 크기 설정(위의 값 사용)
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Memory Game") # 게임제목

# 게임 루프
running = True # 게임이 실행중인가?
while running:
    # 이벤트 루프(사용자의 동작, 조이스틱 키보드 클릭 등으로 넣는다.)
    for event in pygame.event.get(): # 어떤 이벤트가 발생하였는가?
        if event.type == pygame.QUIT: # 사용자가 창을 닫는 이벤트인가?(게임종료)
            running = False # 게임이 더 이상 실행중이 아닌 상태

#게임 종료
pygame.quit()
  • 기본적인 게임 창의 정보를 세팅


🔥 게임 시작화면

# 시작화면 보여주기
def display_start_screen():
    # 화면에 버튼을 그리는 데 하얀색이고 두께가 5인 반지름 60짜리의 원으로 그림.
    # 중심점은 이전에 설정한 시작버튼의 중심점 좌표를 사용함.
    pygame.draw.circle(screen, WHITE, start_button.center, 60, 5)

# 시작 버튼
start_button = pygame.Rect(0, 0, 120, 120) # 가로 세로가 120인 사각형 버튼을 생성
# 해당 버튼의 중심점 좌표를 (120, 120)으로 설정.
# x좌표는 0에서 120으로 해주면되고 y좌표는 전체 높이에서 120 뺀 값.
start_button.center = (120, screen_height - 120)

# 색깔
BLACK = (0, 0, 0) # RGB
WHITE = (255, 255, 255) #RGB

# 게임 루프
running = True # 게임이 실행중인가?
while running:
    # 이벤트 루프(사용자의 동작, 조이스틱 키보드 클릭 등으로 넣는다.)
    for event in pygame.event.get(): # 어떤 이벤트가 발생하였는가?
        if event.type == pygame.QUIT: # 사용자가 창을 닫는 이벤트인가?(게임종료)
            running = False # 게임이 더 이상 실행중이 아닌 상태

    # 화면 전체를 까맣게 칠함
    screen.fill(BLACK)
    # 시작 화면 세팅
    display_start_screen()
    # 화면 업데이트
    pygame.display.update()
  • 검은 바탕화면에 동그란 버튼을 생성한 게임 화면


🔥 게임시작 여부 확인

# 게임 화면 보여주기
def display_game_screen():
    print('game start!')

# 포지션에 대응하는 버튼 확인
def check_buttons(position):
    global start
    if start_button.collidepoint(position):
        start = True
        
# 게임 시작 여부
start = False

# 게임 루프
running = True # 게임이 실행중인가?
while running:
    click_pos = None

    # 이벤트 루프(사용자의 동작, 조이스틱 키보드 클릭 등으로 넣는다.)
    for event in pygame.event.get(): # 어떤 이벤트가 발생하였는가?
        if event.type == pygame.QUIT: # 사용자가 창을 닫는 이벤트인가?(게임종료)
            running = False # 게임이 더 이상 실행중이 아닌 상태
        elif event.type == pygame.MOUSEBUTTONUP: # 사용자가 마우스를 클릭했을 때
            click_pos = pygame.mouse.get_pos()
            print(click_pos)

    # 화면 전체를 까맣게 칠함
    screen.fill(BLACK)

    if start:
        display_game_screen() # 게임 화면 표시
    else:
        display_start_screen() # 시작 화면 표시

    # 사용자가 클릭한 좌표값이 있다면(어딘가 클릭했따면)
    if click_pos:
        check_buttons(click_pos)

    # 화면 업데이트
    pygame.display.update()
  • 게임의 시작 여부를 통해 게임의 시작 화면을 보여주기 위해 세팅
  • 시작 버튼 이외의 좌표가 입력된다면
click_pos = pygame.mouse.get_pos()
            print(click_pos)

로 인해 터미널에 좌표값이 찍히지만, 만약 시작 버튼 안에 있는 좌표가 눌리게 된다면 게임 시작이 되고, display_game_screen 함수에 넣어놓은 print 값이 while 문으로 인해 무한적으로 찍히게 된다.

  • 마우스가 클릭된 것을 전달하기 위해 click_pos 변수를 선언하였고, 이벤트 루프 안에 pygame.MOUSEBUTTONUP를 이벤트 타입으로 설정해 사용자의 마우스 클릭을 감지할 수 있게 설정하였다.

🔥게임화면 기본

  • 게임의 레벨 설정에 따른 보여지는 숫자들을 결정
  • grid의 형태로 숫자들을 보여주는 작업 수행
    아직은 list의 형태로 보여지지만, 랜덤한 숫자들의 위치를 파악할 수 있음
from random import *

# 레벨에 맞게 설정
def setup(level):
    # 얼마나 많은 숫자를 보여줄 것인가?
    number_count = (level // 3) + 5
    number_count = min(number_count, 20) # 만약 20 초과하면 20으로 처리

    # 실제 화면에 grid 형태로 숫자를 랜덤 배치
    shuffle_grid(number_count)

# 숫자 섞기
def shuffle_grid(number_count):
    rows = 5
    columns = 9

    # [[0, 0, 0, 0, 0, 0, 0, 0, 0],
    #  [0, 0, 0, 0, 0, 0, 0, 0, 0],
    #  [0, 0, 0, 0, 0, 0, 0, 0, 0],
    #  [0, 0, 0, 0, 0, 0, 0, 0, 0],
    #  [0, 0, 0, 0, 0, 0, 0, 0, 0]]
    grid = [[0 for col in range(columns)] for row in range(rows)]

    number = 1 # 시작 숫자를 1부터 number_count까지, 만약 5라면 5까지의 숫자를 랜덤으로 배치
    while number <= number_count:
        row_idx = randrange(0, rows) # 0, 1, 2, 3, 4 중에서 랜덤으로 뽑기
        col_idx = randrange(0, columns) # 0 ~ 8 중에서 랜덤으로 뽑기

        if grid[row_idx][col_idx] == 0:
            grid[row_idx][col_idx] = number # 숫자 지정
            number += 1

    # 배치된 랜덤 숫자 확인
     print(grid)

# 게임 시작 직전에 게임 설정 함수 수행
setup(1)

### 🔥게임화면 심화
def shuffle_grid(number_count):
	...
    ...
            if grid[row_idx][col_idx] == 0:
            grid[row_idx][col_idx] = number # 숫자 지정
            number += 1

            # 현재 grid cell 위치 기준으로 x, y 위치 구함
            center_x = screen_left_margin + (col_idx * cell_size) + (cell_size / 2)
            center_y = screen_top_margin + (row_idx * cell_size) + (cell_size / 2)

            # 숫자 버튼 만들기
            button = pygame.Rect(0, 0, button_size, button_size)
            button.center = (center_x, center_y)

            number_buttons.append(button)
    # 배치된 랜덤 숫자 확인
    print(grid)
    
# 게임 화면 보여주기
def display_game_screen():
    for idx, rect in enumerate(number_buttons, start=1):
        pygame.draw.rect(screen, GRAY, rect) # rect에는 center_x와 center_y의 정보가 있음. button_size의 정보도 있음.

        # 실제 숫자 텍스트
        cell_text = game_font.render(str(idx), True, WHITE)
        # 앞에서 가져온 버튼 rect의 center 값을 우리가 그릴 숫자 텍스트의 center 값으로 활용
        text_rect = cell_text.get_rect(center=rect.center)
        screen.blit(cell_text, text_rect)
        
game_font = pygame.font.Font(None, 120) # 폰트 정의
GRAY = (50, 50, 50)

# 실제 플레이어가 눌러야 하는 버튼
number_buttons = []
  • 게임화면에 회색 바탕을 랜덤으로 선택된 각 grid에 그려주고, 그 안에 하얀색의 숫자를 그려준다.



🔥숫자 숨기기 1

# 게임 화면 보여주기
def display_game_screen():
    for idx, rect in enumerate(number_buttons, start=1):
        if hidden: # 숨김처리
            # 버튼 사각형 그리기
            pygame.draw.rect(screen, WHITE, rect) # rect에는 center_x와 center_y의 정보가 있음. button_size의 정보도 있음.
        else:
            # 실제 숫자 텍스트
            cell_text = game_font.render(str(idx), True, WHITE)
            # 앞에서 가져온 버튼 rect의 center 값을 우리가 그릴 숫자 텍스트의 center 값으로 활용
            text_rect = cell_text.get_rect(center=rect.center)
            screen.blit(cell_text, text_rect)

# 포지션에 대응하는 버튼 확인
def check_buttons(pos):
    global start
    if start: # 게임이 시작했으면?
        check_number_buttons(pos)
    elif start_button.collidepoint(pos):
        start = True

def check_number_buttons(pos):
    global hidden
    for button in number_buttons:
        if button.collidepoint(pos):
            if button == number_buttons[0]: # 올바른 숫자 클릭
                print("correct")
                # 올바른 숫자 눌렀을 시 올바른 숫자가 삭제됨
                del number_buttons[0]
                if not hidden:
                    hidden = True # 화면에서 숫자 숨김 처리
            else: # 잘못된 숫자 클릭
                print("Wrong")
            break
    
# 숫자 숨김 여부(사용자가 1을 클릭했거나, 보여주는 시간을 초과했을 때)
hidden = False
  • 이전까지의 화면과 다르게 맨 처음 게임이 시작하게 되면 숫자들의 배경색은 없어지게 된다.
  • hidden 변수를 사용해 number_buttons list에 들어있는 올바른 첫째 숫자, 즉 1을 누르게 되면 1이 사라지면서 나머지 숫자들이 하얀색 바탕으로 변하게 되어 다음 숫자들이 가려지게 된다.
  • check_number_buttons 함수에 넣어 놓은 print를 통해 순서에 맞는 올바른 숫자를 누르게 되면 correct, 틀린 숫자를 누르면 Wrong이 출력되며 아직은 잘못된 숫자를 누른다고 하여 게임이 종료되지는 않는다.

🔥 숫자 숨기기 2

# 숫자를 보여주는 시간
display_time = None
# 시간 계산(현재 시간 정보를 저장)
start_ticks = None

def setup(level):
    global display_time
    # 얼마동안 숫자를 보여줄지
    display_time = 5 - (level // 3)
    display_time = max(display_time, 1) # 1초 미만이면 1초로 처리

    # 얼마나 많은 숫자를 보여줄 것인가?
    number_count = (level // 3) + 5
    number_count = min(number_count, 20) # 만약 20 초과하면 20으로 처리

    # 실제 화면에 grid 형태로 숫자를 랜덤 배치
    shuffle_grid(number_count)
    
# 게임 화면 보여주기
def display_game_screen():
    global hidden
    if not hidden:
        elapsed_time = (pygame.time.get_ticks() - start_ticks) / 1000 # ms -> sec 단위로 변경
        if elapsed_time > display_time:
            hidden = True

    for idx, rect in enumerate(number_buttons, start=1):
        if hidden: # 숨김처리
            # 버튼 사각형 그리기
            pygame.draw.rect(screen, WHITE, rect) # rect에는 center_x와 center_y의 정보가 있음. button_size의 정보도 있음.
        else:
            # 실제 숫자 텍스트
            cell_text = game_font.render(str(idx), True, WHITE)
            # 앞에서 가져온 버튼 rect의 center 값을 우리가 그릴 숫자 텍스트의 center 값으로 활용
            text_rect = cell_text.get_rect(center=rect.center)
            screen.blit(cell_text, text_rect)

# 포지션에 대응하는 버튼 확인
def check_buttons(pos):
    global start, start_ticks
    if start: # 게임이 시작했으면?
        check_number_buttons(pos)
    elif start_button.collidepoint(pos):
        start = True
        start_ticks = pygame.time.get_ticks() # 타이머 시작(현재 시간을 저장)
  • 아까와는 다르게, 이제는 숫자를 누르지 않더라도 일정 시간이 지나면 숫자들이 하얀 박스로 가려지도록 처리하였다.

🔥레벨 설정 & 최종 코드

import pygame
from random import *

# 레벨에 맞게 설정
def setup(level):
    global display_time
    # 얼마동안 숫자를 보여줄지
    display_time = 5 - (level // 3)
    display_time = max(display_time, 1) # 1초 미만이면 1초로 처리

    # 얼마나 많은 숫자를 보여줄 것인가?
    number_count = (level // 3) + 5
    number_count = min(number_count, 20) # 만약 20 초과하면 20으로 처리

    # 실제 화면에 grid 형태로 숫자를 랜덤 배치
    shuffle_grid(number_count)

# 숫자 섞기
def shuffle_grid(number_count):
    rows = 5
    columns = 9

    cell_size = 130 # 각 grid cell 별 가로, 세로 크기
    button_size = 110 # grid cell 내에 실제로 그려질 버튼 크기
    screen_left_margin = 55 # 전체 스크린의 왼쪽 여백
    screen_top_margin = 20 # 전체 스크린의 위쪽 여백

    grid = [[0 for col in range(columns)] for row in range(rows)]

    number = 1 # 시작 숫자를 1부터 number_count까지, 만약 5라면 5까지의 숫자를 랜덤으로 배치
    while number <= number_count:
        row_idx = randrange(0, rows) # 0, 1, 2, 3, 4 중에서 랜덤으로 뽑기
        col_idx = randrange(0, columns) # 0 ~ 8 중에서 랜덤으로 뽑기

        if grid[row_idx][col_idx] == 0:
            grid[row_idx][col_idx] = number # 숫자 지정
            number += 1

            # 현재 grid cell 위치 기준으로 x, y 위치 구함
            center_x = screen_left_margin + (col_idx * cell_size) + (cell_size / 2)
            center_y = screen_top_margin + (row_idx * cell_size) + (cell_size / 2)

            # 숫자 버튼 만들기
            button = pygame.Rect(0, 0, button_size, button_size)
            button.center = (center_x, center_y)

            number_buttons.append(button)
    # 배치된 랜덤 숫자 확인
    print(grid)

# 시작화면 보여주기
def display_start_screen():
    # 화면에 버튼을 그리는 데 하얀색이고 두께가 5인 반지름 60짜리의 원으로 그림.
    # 중심점은 이전에 설정한 시작버튼의 중심점 좌표를 사용함.
    pygame.draw.circle(screen, WHITE, start_button.center, 60, 5)

    msg = game_font.render(f'{curr_level}', True, WHITE)
    msg_rect = msg.get_rect(center=start_button.center)

    screen.blit(msg, msg_rect)

# 게임 화면 보여주기
def display_game_screen():
    global hidden
    if not hidden:
        elapsed_time = (pygame.time.get_ticks() - start_ticks) / 1000 # ms -> sec 단위로 변경
        if elapsed_time > display_time:
            hidden = True

    for idx, rect in enumerate(number_buttons, start=1):
        if hidden: # 숨김처리
            # 버튼 사각형 그리기
            pygame.draw.rect(screen, WHITE, rect) # rect에는 center_x와 center_y의 정보가 있음. button_size의 정보도 있음.
        else:
            # 실제 숫자 텍스트
            cell_text = game_font.render(str(idx), True, WHITE)
            # 앞에서 가져온 버튼 rect의 center 값을 우리가 그릴 숫자 텍스트의 center 값으로 활용
            text_rect = cell_text.get_rect(center=rect.center)
            screen.blit(cell_text, text_rect)

# 포지션에 대응하는 버튼 확인
def check_buttons(pos):
    global start, start_ticks
    if start: # 게임이 시작했으면?
        check_number_buttons(pos)
    elif start_button.collidepoint(pos):
        start = True
        start_ticks = pygame.time.get_ticks() # 타이머 시작(현재 시간을 저장)

def check_number_buttons(pos):
    global start, hidden, curr_level
    for button in number_buttons:
        if button.collidepoint(pos):
            if button == number_buttons[0]: # 올바른 숫자 클릭
                print("correct")
                # 올바른 숫자 눌렀을 시 올바른 숫자가 삭제됨
                del number_buttons[0]
                if not hidden:
                    hidden = True # 화면에서 숫자 숨김 처리
            else: # 잘못된 숫자 클릭
                game_over()
            break
    # 오든 숫자를 다 맞혔다면? 다음 레벨로 넘어간다.
    if len(number_buttons) == 0:
        start = False
        hidden = False
        curr_level += 1
        setup(curr_level)

# 게임 종료 처리. 메세지도 보여줌
def game_over():
    global running

    running = False

    msg = game_font.render(f'Your level is {curr_level}', True, WHITE)
    msg_rect = msg.get_rect(center=(screen_width/2, screen_height/2))

    screen.fill(BLACK)
    screen.blit(msg, msg_rect)

# 초기화
pygame.init()
# 가로 및 세로 크기
screen_width = 1280
screen_height = 720
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Memory Game")
game_font = pygame.font.Font(None, 120) # 폰트 정의

# 시작 버튼
start_button = pygame.Rect(0, 0, 120, 120) # 가로 세로가 120인 사각형 버튼을 생성
# 해당 버튼의 중심점 좌표를 (120, 120)으로 설정.
# x좌표는 0에서 120으로 해주면되고 y좌표는 전체 높이에서 120 뺀 값.
start_button.center = (120, screen_height - 120)

# 색깔
BLACK = (0, 0, 0) # RGB
WHITE = (255, 255, 255) #RGB
GRAY = (50, 50, 50)

# 실제 플레이어가 눌러야 하는 버튼
number_buttons = []
# 현재 레벨
curr_level = 1
# 숫자를 보여주는 시간
display_time = None
# 시간 계산(현재 시간 정보를 저장)
start_ticks = None

# 게임 시작 여부
start = False
# 숫자 숨김 여부(사용자가 1을 클릭했거나, 보여주는 시간을 초과했을 때)
hidden = False

# 게임 시작 직전에 게임 설정 함수 수행, 현재 레벨
setup(curr_level)

# 게임 루프
running = True # 게임이 실행중인가?
while running:
    click_pos = None

    # 이벤트 루프(사용자의 동작, 조이스틱 키보드 클릭 등으로 넣는다.)
    for event in pygame.event.get(): # 어떤 이벤트가 발생하였는가?
        if event.type == pygame.QUIT: # 사용자가 창을 닫는 이벤트인가?(게임종료)
            running = False # 게임이 더 이상 실행중이 아닌 상태
        elif event.type == pygame.MOUSEBUTTONUP: # 사용자가 마우스를 클릭했을 때
            click_pos = pygame.mouse.get_pos()
            print(click_pos)

    # 화면 전체를 까맣게 칠함
    screen.fill(BLACK)

    if start:
        display_game_screen() # 게임 화면 표시
    else:
        display_start_screen() # 시작 화면 표시

    # 사용자가 클릭한 좌표값이 있다면(어딘가 클릭했다면)
    if click_pos:
        check_buttons(click_pos)

    # 화면 업데이트
    pygame.display.update()

# 결과를 5초정도 보여줌
pygame.time.delay(5000)

#게임 종료
pygame.quit()
  • 앞의 코드에서는 정답을 맞춰도 틀려도 게임이 끝나지 않았다. 이번 단계에서는 정답을 맞춘다면 다음 단계가 실행되고, 정답이 아니라면 게임이 끝나 게임의 메인 루프를 빠져나갈 수 있도록 하였다. 또한 처음 시작 시, 현재의 게임단계를 시작 버튼을 통해 보여주고, game_over 함수를 통해 게임이 끝나면 몇 단계에서 끝났는지 보여주도록 하였다.
  • 시작 화면
  • 게임오버 화면

좋은 웹페이지 즐겨찾기