[python] 지뢰찾기 만들기

울상냥·2021년 5월 2일
2

Python

목록 보기
1/3

나도 드디어 무언가를 하는구나 ..

그놈의 깃허브도 개설해보고 아무것도 커밋하지 않다가 드디어 허접한 무언가라도 만들게 되어 신나서 호다닥 블로그 첫 글을 쓰러왔다!!!😊 글재주 없는 설명충인데 어떡하지

작년 10월이었나.. 심심하면 지뢰찾기를 즐겨하는 친구한테 우스갯소리로 지뢰찾기를 만들어주겠다고 했었다. 대학교 1학년 시절 재미있어보여 사두고 묵혀두었던 Python과 Pygame으로 게임만들기 를 이번에 파이썬 공부를 하면서 꺼내보게 되었다. 이참에 지뢰찾기나 만들어볼까 싶어 만든 choco_sweeper_v1 (사실은 정처기 공부가 하기 싫었어요)

생각보다 만들기 어렵거나 오래걸리진 않았는데 뭔가 내 코드가 썩 맘에든다거나 깔끔한거 같지는 않은 기분.. 분명 나중에 보면 허접 찌끄레기 일테지만🙉🙊 그래도 한게 있으니 일단 올려놓기는 해야지 나중에 시간나면 pygame에 관한 글도 올려보려고 한다.


0. 실행화면

시작 화면

게임 화면

게임 실패 화면

(초코 똥 밟았다 !)



1. 보드판 생성하기

board 자료구조

게임판인 board의 자료구조는 다음과 같은 딕셔너리의 리스트 형태이다.
x와 y는 게임판에서의 좌표, count는 지뢰의 갯수, flag는 상태정보를 담고 있다.

board = [{'x': x, 'y': y, 'count': count, 'flag': flag}, ...]

  • count의 상수

    • POO = -1
  • flag의 상수

    • NONE = 0
    • FLAG = -1
    • OPENED = 1

count는 0~8의 주변 지뢰의 갯수 또는 지뢰인 경우 POO의 값을 갖는다.
flag는 초기상태인 NONE 또는 깃발상태인 FLAG 또는 열린상태인 OPENED의 값을 갖는다.

(지뢰를 초코 똥으로 그렸기 때문에 지뢰는 POO💩인 것으로..)

랜덤으로 지뢰 생성하기

def getRandomPoo():

      poos = []

      while len(poos) < POOCOUNT :
            x = random.randint(0, CELLWIDTH-1)
            y = random.randint(0, CELLHEIGHT-1)
            poo = {'x': x, 'y': y}
            if poo not in poos:
                  poos.append(poo)

      return poos

지뢰의 리스트인 poos의 길이가 지뢰의 갯수인 POOCOUNT가 될때까지 지뢰의 좌표를 생성한다.
중복되는 좌표가 아닌경우에만 리스트에 좌표를 추가한다.

보드판 생성하기

def getStartingBoard():

      poos = getRandomPoo()

      board = []
      for x in range(CELLWIDTH):
            for y in range(CELLHEIGHT):

                  count = 0
                  if {'x': x, 'y': y} in poos:
                        count = POO
                  else:
                        if {'x': x-1, 'y': y-1} in poos:
                              count +=1
                        if {'x': x, 'y': y-1} in poos:
                              count +=1
                        if {'x': x+1, 'y': y-1} in poos:
                              count +=1
                        if {'x': x-1, 'y': y} in poos:
                              count +=1                              
                        if {'x': x+1, 'y': y} in poos:
                              count +=1                  
                        if {'x': x-1, 'y': y+1} in poos:
                              count +=1
                        if {'x': x, 'y': y+1} in poos:
                              count +=1
                        if {'x': x+1, 'y': y+1} in poos:
                              count +=1

                  cell = {'x': x, 'y': y, 'count' : count, 'flag': NONE}
                  board.append(cell)
                  
      return board

getRandomPoo()를 실행하여 지뢰를 생성하고 해당좌표의 주변 지뢰갯수를 계산하여 board를 생성한다.



2. 게임 실행하기

초기 설정하기

def runGame():

      resetButton, boardSurf = drawBorder()
      board = getStartingBoard()
      startTime = 0

drawBorder() 함수로 게임화면을 그리고 리셋버튼과 보드의 위치를 저장하고 getStartingBoard()로 보드판을 생성한다.

클릭 이벤트 감지하기


      while True:

            cellx, celly = None, None

            #click event
            for event in pygame.event.get():
                  if event.type == QUIT:
                        terminate()
                  elif event.type == MOUSEBUTTONUP:

                        cellx, celly =  getSpotClicked(board, event.pos[0], event.pos[1])
                        
                        if event.button == 1:  #left click
                              
                              if resetButton.collidepoint(event.pos): #resetButton pressed
                                    playButtonSound()
                                    return

                              elif boardSurf.collidepoint(event.pos) : #board pressed
                                    if startTime == 0: #timer on
                                          startTime = time.time()
                                    index = getBoardIndex(cellx, celly)
                                    setFlagOpened(board[index])
                                    
                        elif event.button == 3:  #right click
                              if boardSurf.collidepoint(event.pos):
                                    index = getBoardIndex(cellx, celly)
                                    setFlagFlag(board[index])

event가 발생했을때

  • QUIT의 경우
    terminate()함수를 실행하여 게임을 종료시킨다.

  • 마우스 클릭의 경우

    getSpotClicked() 함수를 실행하여 마우스 좌표에 해당하는 보드 좌표를 반환하여 cellx ,celly에 저장한다.

    • 왼쪽 클릭
      리셋 버튼이 눌린 경우 return하여 runGame()함수가 다시 실행될 수 있게 한다.

      보드판이 눌린 경우 타이머를 시작하고 해당 칸을OPENED로 바꾼다.

    • 오른쪽 클릭
      setFlagFlag()함수는 보드판이 눌린 경우 해당 칸을FLAGED로 바꾼다.
      이미 FLAGED인 경우 NONE으로 바꾼다.


주변에 지뢰가 없는 칸 열기

            for cell in board:
                  if cell['count'] == 0 and cell['flag'] == OPENED: #open cell if there`s no poo around
                        setAroundOpened(board, cell)
                        

주변에 지뢰가 없고 해당 칸이 열려있는 경우 setAroundOpened()는 주변의 모든 칸을 연다.
runGame()의 무한 루프안에서 실행되기 때문에 주변으로 퍼져나가며 열리게 된다.

게임 상태를 확인하기

            for cell in board:
                  if cell['count'] == POO and cell['flag'] == OPENED: #GAME OVER

                        for cell in board: #open all poo left
                              if cell['count'] == POO :
                                    setFlagOpened(cell)
                                    
                        drawBoard(board, startTime)
                        showGameOverScreen(board, cellx, celly)

                        if checkForNext(resetButton) == 1:
                              return
                        
            else:
                  drawBoard(board, startTime)

                  if getPooCount(board) == 0:
                        for cell in board:
                              if cell['flag'] == FLAG and cell['count'] != POO: #wrong flag
                                    break
                              if cell['flag'] == NONE and cell['count'] != POO: #not poo cell not opened
                                    break
                        else: #YOUWIN!
                              showYouWinScreen()
                              if checkForNext(resetButton) == 1:
                                    return

지뢰칸이 열린 경우 남은 지뢰칸을 모두 열고 게임 실패화면을 보인다.
지뢰칸을 제외한 모든 칸이 열린 경우 게임 성공화면을 보인다.



3. MAIN 함수

def main():

      global DISPLAYSURF, BASICFONT, CHOCOIMG

      pygame.init()
      DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
      BASICFONT = pygame.font.Font('source/Jalnan.ttf',18)
      pygame.display.set_caption('Choco Sweeper')
      CHOCOIMG = pygame.image.load('source/choco.png')
      pygame.display.set_icon(CHOCOIMG)

      showStartScreen()
      while True:
            runGame()

showStartScreen()으로 시작화면이 보여지고 runGame()함수가 게임 종료시까지 무한루프를 도는 구조이다.



4. 전체 코드는..

깃헙에서 확인하는 것으로 😺
https://github.com/yevini118/choco_sweeper.git


choco_sweeper_v2를 만들어야겠어


사실 코딩하는 시간보다 그림그리고 디자인하는데 시간을 더 많이 쓴거같다. 그래도 나름 초코 잘그린거 같음 (멍멍..)

중간에 약간의 삽질을 하느라 시간을 좀 버렸다.☹️
조금이라도 더 빠른 코드를 쓰고 싶어서 보드의 인덱스를 구해 칸에 접근하는 방법으로 for문을 줄여봤는데 이게 효과가 있기는 한걸까라는 의문이 들긴해..

친구하라고 exe로 만들어 보내줬는데 pyinstaller로 windows10에서 만든 exe가 친구의 컴퓨터인 windows7에서 돌아가지 않는 덕분에🙃 virtualBox까지 써가며 windows7에서 만들어서 보내주니 다행히 잘 돌아갔다 ㅠ(눙물😢)
다른 친구들한테도 하라고 보내주니까 생각보다 열심히하는게 웃겼다.

v1에서는 더블클릭 구현을 못해서 아쉽다. 정처기 필기가 끝나면 언넝 v2를 만들어야겠다.
choco_sweeper_v2에서는 더블클릭과 보드사이즈+지뢰 갯수 조절기능을 넣어야지!

profile
안되면 되게하라

0개의 댓글