[pygame] 슬라이드 퍼즐 - 1

서희찬·2021년 4월 8일
0

슬라이드 퍼즐을 만들어보쟈 ~!
슬라이드퍼즐이 무엇인지 아는가?
뭐징// 하다가 이 사진을 보면ㅇ ㅏ !! 할테다

짜 잔 !
무엇인지 알겠는가?
4*4 인 보드판에 저 손꾸락으로 하나를 올리면 16개의 칸에 15개의 카드들을 움직이며 123456~15까지 순서대로 맞추는 게임이다 !

오늘은..아마내일도 모레도..
이 게임을 만들어보자 !!!

상수 선언

# Slide Puzzle
# By Al Sweigart al@inventwithpython.com
# http://inventwithpython.com/pygame
# Released under a "Simplified BSD" license

import pygame, sys, random
from pygame.locals import *

# Create the constants (go ahead and experiment with different values)
BOARDWIDTH = 4  # number of columns in the board
BOARDHEIGHT = 4 # number of rows in the board
TILESIZE = 80
WINDOWWIDTH = 640
WINDOWHEIGHT = 480
FPS = 30
BLANK = None

#                 R    G    B
BLACK =         (  0,   0,   0)
WHITE =         (255, 255, 255)
BRIGHTBLUE =    (  0,  50, 255)
DARKTURQUOISE = (  3,  54,  73)
GREEN =         (  0, 204,   0)

BGCOLOR = DARKTURQUOISE
TILECOLOR = GREEN
TEXTCOLOR = WHITE
BORDERCOLOR = BRIGHTBLUE
BASICFONTSIZE = 20

BUTTONCOLOR = WHITE
BUTTONTEXTCOLOR = BLACK
MESSAGECOLOR = WHITE

XMARGIN = int((WINDOWWIDTH - (TILESIZE * BOARDWIDTH + (BOARDWIDTH - 1))) / 2)
YMARGIN = int((WINDOWHEIGHT - (TILESIZE * BOARDHEIGHT + (BOARDHEIGHT - 1))) / 2)

UP = 'up'
DOWN = 'down'
LEFT = 'left'
RIGHT = 'right'

어떤가 뭔가 많이 본 코드 아닌가..?
이전 메모리퍼즐 만들기에서의 상수선언과 유사하다는 것을 알 수 있다.

Main 함수

버튼 설정하기

이 게임에서는 3가지의 버튼이 필요하다

  • Reset 버튼 : 원래대로 되돌린다.
  • New 버튼 : 새 슬라이드 퍼즐 게임을 시작한다.
  • Solve 버튼 : 문제를 풀어준다.

이 게임에는 게임판 데이터 구조가 2개가 필요하다
왜냐 !?
하나는 현재 게임판의 상태이고 나머지는 다 풀린 상태이다
이 둘을 비교하여 일치하면 플레이어가 승리한 것이기 때문이다!
즉, 비교용으로 필요로 하다 !

이를 토대로 main 함수의 초반부를 적어보자

def main():
        global FPSCLOCK, DISPLAYSURF, BASICFONT, RESET_SURF, RESET_RECT, NEW_SURF, NEW_RECT, SOLVE_SURF, SOLVE_RECT
    pygame.init()
    FPSCLOCK = pygame.time.Clock()
    DISPLAYSURF=pygame.display.set_mode((WINDOWWIDTH,WINDOWHEIGHT))
    pygame.display.set_caption("Chans Slide Puzzle")
    BASICFONT = pygame.font.Font("freesansbold.ttf",BASICFONTSIZE)
    #### 위에까지 가본 설정 ㅇㅇ

    #옵션 버튼과 둘러싸는 사각형은 options에 저장
    RESET_SURF, RESET_RECT = makeText("Reset", TEXTCOLOR,TILECOLOR,WINDOWWIDTH - 120, WINDOWHEIGHT - 90)
    NEW_SURF,NEW_RECT = makeText("New Game",TEXTCOLOR,TILECOLOR,WINDOWWIDTH - 120, WINDOWHEIGHT - 60)
    SOLVE_SURF,SOLVE_RECT = makeText("Solve",TEXTCOLOR,TILECOLOR,WINDOWWIDTH - 120, WINDOWHEIGHT - 30) ##그린당 여기에 이색으로 ㅇㅇ

    mainBoard, solutionSeq = generateNewPuzzle(80) #80번 움직여서 보드를 섞는다.
    SOLVEDBOARD = getStartingBoard() # 다 맞춘 게임판은 처음 게임판의 상태와 동일 

따로 설명은 주석에..!

단순한 코드로 스마트 해지자 !

슬라이드 퍼즐을 푸는 방법은 매우 어렵고 컴퓨터가 풀게 할려면 알고리즘을 짜야하고 복잡...하고 많은 노력이 필요하다.
하지만 ! 만약! 컴퓨터가 처음에 게임판을 만들기 위해 무작위로 옮긴 순서를 모두 기억하고 있다면 어떨까!?

그렇다면 단지 순서를 거꾸로 해서 처음 상태로 만들 수 있다. 만약 "오른쪽,오른쪽,아래" 로 움직였다면 "위, 왼쪽, 왼쪽" 으로 움직이면 처음 상태가된다.

이를 위한 코드가 바로

allMoves = [] # 처음 상태에서 시작한 움직임을 저장한 리스트

이다 !!
이 한줄이 그 ~ 많은 생각들을 해결할 수 있다는게 신기하지 않는가!!!
이게 바로 프로그래밍의 묘미인것같다.

게임 루프


   while True: # main Game Loop
       slideTo = None # 플레이어가 어느 방향으로 타일을 미뤘는지 기록
       msg = "Click tile or press arrow keys to slide" # 왼쪽 상단 구석에 보여주자
       if mainBoard = SOLVEDBOARD:
           msg = "Solved !!! "

       drawBoard(mainBoard,msg) #DISPLAYSURF surface 객체 상에 반영

게임루프를 시작하고 기본적인것들을 선언하고있다.

버튼 클릭하기

    
    checkForQuit() #종료를 눌렀는지 확인 
    for event in pygame.event.get(): # 이벤트 처리 루프
         if event.type == MOUSEBUTTONUP:
             spotx, spoty = getSpotClicked(mainBoard,event.pos[0],event.pos[1]) # mouse를 클릭했다면 함수에 마우스 위치를 넘긴다.
             # 위치를 넘기면 게임판의 좌표계를 반환한다. (메인보드와 게임보드는 다르게 ! )

             if(spotx,spoty) ==(None,None):
                 #게임판 밖의 다른 버튼을 클릭했는지 본다.
                 if RESET_RECT.collidepoint(event.pos):
                     resetAnimation(mainBoard, allMoves) # clicked on Reset button
                     allMoves = []
                 elif NEW_RECT.collidepoint(event.pos):
                     mainBoard, solutionSeq = generateNewPuzzle(80) # clicked on New Game button
                     allMoves = []
                 elif SOLVE_RECT.collidepoint(event.pos):
                     resetAnimation(mainBoard, solutionSeq + allMoves) # clicked on Solve button
                     allMoves = []

이벤트가 발생했는지 확인한다.
우선 QUIT를 따로 확인후, 다른 이벤트 들이 발생했는지 체크한다.
이유는 나중에 !
만약 플레이어가 게임보드안을 클릭했다면 게임보드 안의 x,y좌표를 반환해주는 함수를 짜고 그렇지 않다면 None이 되는 함수를 짠다.
None이 된다면 게임보드 밖이니 버튼을 클릭했는지 확인한다!

마우스로 타일 밀기

                else:
                    #클릭한 타일이 빈칸 옆에 있는지 확인한다.

                    blankx, blanky = getBlankPosition(mainBoard)
                    if spotx == blankx + 1 and spoty == blanky :
                        slideTo = LEFT 
                    elif spotx == blankx - 1 and spoty == blanky:
                        slideTo = RIGHT
                    elif spotx == blankx and spoty == blanky + 1:
                        slideTo = UP
                    elif spotx == blankx and spoty == blanky - 1:
                        slideTo = DOWN

쉬운 부분이다.
만약 spotx,spoty가 None이 아닐때
blankx,blanky 를 찾은 후 그 slideTo 변수를 타일이 움직여야 하는 방향으로 설정해준다.

키보드로 타일 밀기

            elif event.type == KEYUP:
                # check if the user pressed a key to slide a tile
                if event.key in (K_LEFT, K_a) and isValidMove(mainBoard, LEFT):
                    slideTo = LEFT
                elif event.key in (K_RIGHT, K_d) and isValidMove(mainBoard, RIGHT):
                    slideTo = RIGHT
                elif event.key in (K_UP, K_w) and isValidMove(mainBoard, UP):
                    slideTo = UP
                elif event.key in (K_DOWN, K_s) and isValidMove(mainBoard, DOWN):
                    slideTo = DOWN

isvalidMove 함수를 통해서 플레이어가 명령한 방향으로 밀 수 있는지 검사한다.

in연산자로 "이들 중 하나와 일치" 트릭

event.key in (k_left, k_a)
event.key == k_left or event.key == K_a 

위 둘은 동일 하지만 in 연산자로 보다 간결하게 짤수 있다.

타일 밀기를 실제로 실행하기

        if slideTo:
            slideAnimation(mainBoard,slideTo,"Click tile or press arrow keys to slide.", 8) #화면상에 슬라이드를 보여준다.
            makeMove(mainBoard,slideTo) #실제 게임판 데이터 구조 변경 
            allMoves.append(slideTo) # 슬라이드를 기록한다.
        pygame.display.update()
        FPSCLOCK.tick(FPS)

에니메이션을 보여준 후 실제 데이터를 변경 후 그 값을 나중에 원래 자리로 되돌릴 수 있게 allMove list에 추가한다.

이로써 main 함수의 정의는 끝이 났다 !

여기까지의 코드

import pygame, sys, random
from pygame.locals import *

# Create the constants (go ahead and experiment with different values)
BOARDWIDTH = 4  # number of columns in the board
BOARDHEIGHT = 4 # number of rows in the board
TILESIZE = 80
WINDOWWIDTH = 640
WINDOWHEIGHT = 480
FPS = 30
BLANK = None

#                 R    G    B
BLACK =         (  0,   0,   0)
WHITE =         (255, 255, 255)
BRIGHTBLUE =    (  0,  50, 255)
DARKTURQUOISE = (  3,  54,  73)
GREEN =         (  0, 204,   0)

BGCOLOR = DARKTURQUOISE
TILECOLOR = GREEN
TEXTCOLOR = WHITE
BORDERCOLOR = BRIGHTBLUE
BASICFONTSIZE = 20

BUTTONCOLOR = WHITE
BUTTONTEXTCOLOR = BLACK
MESSAGECOLOR = WHITE

XMARGIN = int((WINDOWWIDTH - (TILESIZE * BOARDWIDTH + (BOARDWIDTH - 1))) / 2)
YMARGIN = int((WINDOWHEIGHT - (TILESIZE * BOARDHEIGHT + (BOARDHEIGHT - 1))) / 2)

UP = 'up'
DOWN = 'down'
LEFT = 'left'
RIGHT = 'right'

def main():
        global FPSCLOCK, DISPLAYSURF, BASICFONT, RESET_SURF, RESET_RECT, NEW_SURF, NEW_RECT, SOLVE_SURF, SOLVE_RECT
    pygame.init()
    FPSCLOCK = pygame.time.Clock()
    DISPLAYSURF=pygame.display.set_mode((WINDOWWIDTH,WINDOWHEIGHT))
    pygame.display.set_caption("Chans Slide Puzzle")
    BASICFONT = pygame.font.Font("freesansbold.ttf",BASICFONTSIZE)
    #### 위에까지 가본 설정 ㅇㅇ

    #옵션 버튼과 둘러싸는 사각형은 options에 저장
    RESET_SURF, RESET_RECT = makeText("Reset", TEXTCOLOR,TILECOLOR,WINDOWWIDTH - 120, WINDOWHEIGHT - 90)
    NEW_SURF,NEW_RECT = makeText("New Game",TEXTCOLOR,TILECOLOR,WINDOWWIDTH - 120, WINDOWHEIGHT - 60)
    SOLVE_SURF,SOLVE_RECT = makeText("Solve",TEXTCOLOR,TILECOLOR,WINDOWWIDTH - 120, WINDOWHEIGHT - 30)

    mainBoard, solutionSeq = generateNewPuzzle(80) #80번 움직여서 보드를 섞는다.
    SOLVEDBOARD = getStartingBoard() # 다 맞춘 게임판은 처음 게임판의 상태와 동일   
    allMoves = [] 

    while True: # main Game Loop
        slideTo = None # 플레이어가 어느 방향으로 타일을 미뤘는지 기록
        msg = "Click tile or press arrow keys to slide" # 왼쪽 상단 구석에 보여주자
        if mainBoard = SOLVEDBOARD:
            msg = "Solved !!! "

        drawBoard(mainBoard,msg) #DISPLAYSURF surface 객체 상에 반영

        checkForQuit() #종료를 눌렀는지 확인 
        for event in pygame.event.get(): # 이벤트 처리 루프
            if event.type == MOUSEBUTTONUP:
                spotx, spoty = getSpotClicked(mainBoard,event.pos[0],event.pos[1]) # mouse를 클릭했다면 함수에 마우스 위치를 넘긴다.
                # 위치를 넘기면 게임판의 좌표계를 반환한다. (메인보드와 게임보드는 다르게 ! )

                if(spotx,spoty) ==(None,None):
                    #게임판 밖의 다른 버튼을 클릭했는지 본다.
                    if RESET_RECT.collidepoint(event.pos):
                        resetAnimation(mainBoard, allMoves) # clicked on Reset button
                        allMoves = []
                    elif NEW_RECT.collidepoint(event.pos):
                        mainBoard, solutionSeq = generateNewPuzzle(80) # clicked on New Game button
                        allMoves = []
                    elif SOLVE_RECT.collidepoint(event.pos):
                        resetAnimation(mainBoard, solutionSeq + allMoves) # clicked on Solve button
                        allMoves = []
                else:
                    #클릭한 타일이 빈칸 옆에 있는지 확인한다.

                    blankx, blanky = getBlankPosition(mainBoard)
                    if spotx == blankx + 1 and spoty == blanky :
                        slideTo = LEFT 
                    elif spotx == blankx - 1 and spoty == blanky:
                        slideTo = RIGHT
                    elif spotx == blankx and spoty == blanky + 1:
                        slideTo = UP
                    elif spotx == blankx and spoty == blanky - 1:
                        slideTo = DOWN
       
            elif event.type == KEYUP:
                # check if the user pressed a key to slide a tile
                if event.key in (K_LEFT, K_a) and isValidMove(mainBoard, LEFT):
                    slideTo = LEFT
                elif event.key in (K_RIGHT, K_d) and isValidMove(mainBoard, RIGHT):
                    slideTo = RIGHT
                elif event.key in (K_UP, K_w) and isValidMove(mainBoard, UP):
                    slideTo = UP
                elif event.key in (K_DOWN, K_s) and isValidMove(mainBoard, DOWN):
                    slideTo = DOWN
        if slideTo:
            slideAnimation(mainBoard,slideTo,"Click tile or press arrow keys to slide.", 8) #화면상에 슬라이드를 보여준다.
            makeMove(mainBoard,slideTo) #실제 게임판 데이터 구조 변경 
            allMoves.append(slideTo) # 슬라이드를 기록한다.
        pygame.display.update()
        FPSCLOCK.tick(FPS)
profile
부족한 실력을 엉덩이 힘으로 채워나가는 개발자 서희찬입니다 :)

0개의 댓글