본문으로 건너뛰기

python-implementation

지뢰찾기 by Python

이 글은 기존 블로그의 원문에서 가져왔습니다. 원문 작성일: 2021-07-24

Python으로 지뢰찾기를 만들게 된 이유는

지뢰찾기 판을 자동으로 만드는 과제를 받았는데

그것을 실제로 게임이 되게 하면 재미있겠다 생각해서 이다.

파이썬으로 그래픽을 만지는 법을 모르기에 오직 텍스트로만 만들었다.

게임판의 토대 만들기

지뢰찾기를 만들기 위해서는 게임판을 먼저 만들어야 한다.

플레이어에게 보이는 게임판과 지뢰의 위치가 그려져있는 게임판을 따로 만들어줬다.

gameBoard = [] 
vGameBoard = [] #visible Game board
mode = 'O' # O : open mode , F : flag mode
 
for raw in range(10):
    gameBoard.append(['.']*10)
 
for raw in range(11):
    vGameBoard.append(['.']*10)

위와 같은 코드로 2차원 list로 10, 10이 되도록 먼저 만들어줬다.

mode는 좌표로 입력받은 타일을 열지 깃발을 세울지를 결정해주는 변수이다.

텍스트로 그래픽 표현하기

그리고 텍스트 만으로 게임을 만들 것이기 때문에 편하게 게임판을 표기하기 위해서

graphic(list[][])함수를 만들었다.

def graphic(table) :
    global mode
 
    print('Current mode = {}'.format(mode))
 
 
    for raw in range(10) :
        for line in range(10) :
            print(table[raw][line], end = ' ')
        print('')

이차원 리스트를 parameter로 입력받아서 그것을 밑에 와 같이 출력해준다.

그리고 현제 모드도 함께 보여준다.

지뢰찾기에서는 제일 먼저 고른 타일에는 지뢰가 설치되지 않기에 먼저 첫 번째 타일 위치를 입력받아야 한다.

graphic(vGameBoard)
 
print('First selection is limited inward.')
com = input('x, y : select (x, y) tile\n')
 
x, y = com.split(", ")
x, y = int(x), int(y)
 

그래서 지뢰의 위치를 정하기 전에 위와 같이 좌표를 입력받는다.

설명 또한 출력해준다.

랜덤으로 지뢰 위치 정하기

첫번째 타일 위치를 입력받았기 때문에 이제 보이지 않는 게임판에 지뢰의 위치와 지뢰가 없는 타일에는 근처에 있는 지뢰의 개수를 숫자를 정해야 한다.

from random import randint
 
def makeBoard(x, y) :
 
    global gameBoard
    global vGameBoard
 
    for raw in range(10) :
        for line in range(10) :
            if randint(1, 100) < 20 : #randint를 이용해서 20%확률로 지뢰(#)를 설치한다. 
                gameBoard[raw][line] = '#'
 
    for raw in range(y - 2, y + 1): #처음 고른 타일의 좌표와 그 주변 8칸을 지뢰를 지운다.
        for line in range(x - 2, x + 1):
            gameBoard[raw][line] = '.'
 
    for raw in range(10) :
        for line in range(10) :
 
            mineNumber = 0
            if gameBoard[raw][line] == '.' : #지뢰가 없는 타일(.)이라면 그 근처 8칸에서 #의 수를 mineNumber에 저장한다.  
                if 0 <= (raw - 1) and (raw - 1) <= 9 and 0 <= (line - 1) and (line - 1) <= 9 :
                    if gameBoard[raw - 1][line - 1] == '#' :
                        mineNumber += 1
                if 0 <= (raw - 1) and (raw - 1) <= 9 and 0 <= (line) and (line) <= 9 :
                    if gameBoard[raw - 1][line] =='#' :
                        mineNumber += 1
                if 0 <= (raw - 1) and (raw - 1) <= 9 and 0 <= (line + 1) and (line + 1) <= 9 :
                    if gameBoard[raw - 1][line + 1] == '#' :
                        mineNumber += 1
                if 0 <= (raw) and (raw) <= 9 and 0 <= (line - 1) and (line - 1) <= 9 :
                    if gameBoard[raw][line - 1] == '#' :
                        mineNumber += 1
                if 0 <= (raw) and (raw) <= 9 and 0 <= (line + 1) and (line + 1) <= 9 :
                    if gameBoard[raw][line + 1] == '#' :
                        mineNumber += 1
                if 0 <= (raw + 1) and (raw + 1) <= 9 and 0 <= (line - 1) and (line - 1) <= 9 :
                    if gameBoard[raw + 1][line - 1] == '#' :
                        mineNumber += 1
                if 0 <= (raw + 1) and (raw + 1) <= 9 and 0 <= (line) and (line) <= 9 :
                    if gameBoard[raw + 1][line] == '#' :
                        mineNumber += 1
                if 0 <= (raw + 1) and (raw + 1) <= 9 and 0 <= (line + 1) and (line + 1) <= 9 :
                    if gameBoard[raw + 1][line + 1] == '#' :
                        mineNumber += 1
 
                gameBoard[raw][line] = str(mineNumber) #mineNumber을 그 칸에 string의 형태로 저장한다. 

이 함수는 처음 고른 타일의 좌표(x, y)를 parameter로 받아서 20%확률로 지뢰(#)위치를 무작위로 정하고 (8~11)

x, y의 타일과 그 주변 8칸의 타일을 (.)로 만들면서 지뢰(#)를 없앤다. (13~15)

그리고 모든 지뢰가 없는 타일에 그 주변에 지뢰가 있는 타일의 수를 세서 저장한다. (17~47)

그렇게 하면 위와 같은 게임판을 랜덤으로 만들 수 있을 것이다.

그리고 이것은 플레이어에게 보이면 안 되기에 안 보이는 게임판에 저장해야한다.

선택한 타일을 열기

그 다음에는 선택한 타일을 여는 open(x, y) 함수를 만들었다.

def open(x, y) :
    global vGameBoard
    global gameBoard
 
    if gameBoard[y-1][x-1] == '#' :
        vGameBoard[y-1][x-1] = gameBoard[y-1][x-1]
        graphic(vGameBoard)
        print('게임에 패배했습니다.')
        exit()
# If player select mine, then player defeat
 
    else :
        openlemma(x, y)
# open selected tile

만약 선택한 타일이 지뢰(#)라면 게임이 패배하고 프로그램이 종료되도록 했다. (5~10)

선택한 타일이 지뢰가 아니라면 연쇄적으로 타일이 열리는 것을 표현하기 위해서 재귀 함수를 사용하기 위해

openlemma(x, y) 함수를 따로 만들었다.

def openlemma(x, y) : # for making function simpl
    global vGameBoard
    global gameBoard
    vGameBoard[y-1][x-1] = gameBoard[y-1][x-1]
    raw = y - 1
    line = x - 1
 
    if gameBoard[raw][line] == '0' :
        if 0 <= (raw - 1) and (raw - 1) <= 9 and 0 <= (line - 1) and (line - 1) <= 9 :
            if gameBoard[raw - 1][line - 1] == '0' and vGameBoard[raw-1][line-1] == '.' :
                openlemma(x-1, y-1)
 
        if 0 <= (raw - 1) and (raw - 1) <= 9 and 0 <= (line) and (line) <= 9 :
            if gameBoard[raw - 1][line] == '0' and vGameBoard[raw-1][line] == '.' :
                openlemma(x, y-1)
 
        if 0 <= (raw - 1) and (raw - 1) <= 9 and 0 <= (line + 1) and (line + 1) <= 9 :
            if gameBoard[raw - 1][line + 1] == '0' and vGameBoard[raw-1][line+1] == '.' :
                openlemma(x+1, y-1)
 
        if 0 <= (raw) and (raw) <= 9 and 0 <= (line - 1) and (line - 1) <= 9 :
            if gameBoard[raw][line - 1] == '0' and vGameBoard[raw][line-1] == '.' :
                openlemma(x-1, y)
 
        if 0 <= (raw) and (raw) <= 9 and 0 <= (line + 1) and (line + 1) <= 9 :
            if gameBoard[raw][line + 1] == '0' and vGameBoard[raw][line+1] == '.' :
                openlemma(x+1, y)
 
        if 0 <= (raw + 1) and (raw + 1) <= 9 and 0 <= (line - 1) and (line - 1) <= 9 :
            if gameBoard[raw + 1][line - 1] == '0' and vGameBoard[raw+1][line-1] == '.' :
                openlemma(x-1, y+1)
 
        if 0 <= (raw + 1) and (raw + 1) <= 9 and 0 <= (line) and (line) <= 9 :
            if gameBoard[raw + 1][line] == '0' and vGameBoard[raw+1][line] == '.' :
                openlemma(x, y+1)
 
        if 0 <= (raw + 1) and (raw + 1) <= 9 and 0 <= (line + 1) and (line + 1) <= 9 :
            if gameBoard[raw + 1][line + 1] == '0' and vGameBoard[raw+1][line+1] == '.' :
                openlemma(x+1, y+1)
# 주의의 0인 타일에 다시 openlemma를 적용
 
 
    if gameBoard[raw][line] == '0' :
        if 0 <= (raw - 1) and (raw - 1) <= 9 and 0 <= (line - 1) and (line - 1) <= 9 :
            vGameBoard[raw-1][line-1] = gameBoard[raw-1][line-1]
 
        if 0 <= (raw - 1) and (raw - 1) <= 9 and 0 <= (line) and (line) <= 9 :
            vGameBoard[raw-1][line] = gameBoard[raw-1][line]
 
        if 0 <= (raw - 1) and (raw - 1) <= 9 and 0 <= (line + 1) and (line + 1) <= 9 :
            vGameBoard[raw-1][line+1] = gameBoard[raw-1][line+1]
 
        if 0 <= (raw) and (raw) <= 9 and 0 <= (line - 1) and (line - 1) <= 9 :
            vGameBoard[raw][line-1] = gameBoard[raw][line-1]
 
        if 0 <= (raw) and (raw) <= 9 and 0 <= (line + 1) and (line + 1) <= 9 :
            vGameBoard[raw][line+1] = gameBoard[raw][line+1]
 
        if 0 <= (raw + 1) and (raw + 1) <= 9 and 0 <= (line - 1) and (line - 1) <= 9 :
            vGameBoard[raw+1][line-1] = gameBoard[raw+1][line-1]
 
        if 0 <= (raw + 1) and (raw + 1) <= 9 and 0 <= (line) and (line) <= 9 :
            vGameBoard[raw+1][line] = gameBoard[raw+1][line]
 
        if 0 <= (raw + 1) and (raw + 1) <= 9 and 0 <= (line + 1) and (line + 1) <= 9 :
            vGameBoard[raw+1][line+1] = gameBoard[raw+1][line+1]
#0인 타일의 주변의 타일을 연다

일단 선택한 타일을 열어준다. (4)

그리고 선택한 타일의 주변에 주변의 지뢰 갯수가 0인 곳이 있다면 다시 openlemma(x, y)를 불러서

그 타일 또한 연쇄적으로 열리도록 했다. (8~40)

그리고 0 주변에 있는 0이 아닌 타일들 또한 열려야 하기 때문에 열어줬다. (43~66)

(보이는 게임판에 안 보이는 게임판에 저장되있는 정보를 가져오는 것을 연다고 표현했다.)

선택한 타일에 깃발 꼽기

def flag(x, y) :
    global vGameBoard
    if vGameBoard[y-1][x-1] == '.' :
        vGameBoard[y-1][x-1] = 'P'
    elif vGameBoard[y-1][x-1] == 'P' :
        vGameBoard[y-1][x-1] = '.'
    else :
        print('error_2')
        exit()
# P(flag) -> not flag / not flag -> flag

flag(x, y)함수도 좌표를 parameter로 받아와서

선택한 타일에 깃발이 있다면 없애고

없다면 만들었다.

(깃발은 비슷하게 생긴 'P'로 했다.)

게임 승리 확인

def check() : #게임의 승리를 확인
    global vGameBoard
 
    gameEnded = 0
 
    for raw in range(10) :
        for line in range(10) :
            if gameBoard[raw][line] != '#' :
                if vGameBoard[raw][line] == gameBoard[raw][line] :
                    gameEnded = 1
 
                else :
                    gameEnded = 0
                    return 0
 
    if gameEnded == 1 :
        print('게임에 승리하였습니다.')
        return 1

지뢰 없는 타일들이 모두 열렸다면 게임에서 승리한 것이기 때문에

이중 if문을 이용해서 만약 모두 열렸다면 1(true)이 반환되고 아니라면 0(false)이 반환되게 했다. (6~18)

Main 함수

위에서 만든 함수들을 종합해서 실제 실행될 부분을 만들 것이다.

def main() :
    global mode
    global vGameBoard
    global gameBoard
 
    graphic(vGameBoard)
 
    print('First selection is limited inward.')
    com = input('x, y : select (x, y) tile\n')
 
    x, y = com.split(", ")
    x, y = int(x), int(y)
 
    makeBoard(x, y)
    open(x, y)
 
    while 1 :
        print('-----------------------------------------------------------')
        graphic(vGameBoard)
        com = input('F : Flag mode\nO : Open mode\nx, y : select (x, y) tile\n')
        if com == 'F' :
            mode = 'F'
            continue
        elif com == 'O' :
            mode = 'O'
            continue
        else :
            x, y = com.split(", ")
            x, y = int(x), int(y)
            if mode == 'F' :
                flag(x, y)
            elif mode == 'O' :
                open(x, y)
            else :
                print('Error_1')
                exit()
 
        if check() == 1:
            exit()

사실 첫 17열 윗부분은 앞에서 먼저 나왔던 부분이다. 특수한 처음 선택을 제외하고는 17열 아래에서 실행된다.

graphic 함수를 이용해서 현재 상황을 보여주고 (18~19)

입력을 받는다. 만약 F나 O를 입력 받는다면 각각 Flag모드와 Open모드로 mode를 변환한다. (21~26)

그 이외에는 좌표일 것이기에 모드에 따라서 flag 함수나 open 함수를 호출해서 선택한 타일에

알맞은 실행을 한다. (27~33)

그래서 check 함수를 이용해서 1이 반환된다면 승리한 것이기 때문에 프로그램을 멈춘다. (38~39)

전체 코드

#-------------------------------------------------------------------------------
# Name:        Minesweeper By python
# Purpose:
#
# Author:      정윤성
#
# Created:     06-05-2021
# Copyright:   (c) jys09 2021
# Licence:     <your licence>
#-------------------------------------------------------------------------------
 
from random import randint
 
gameBoard = []
vGameBoard = [] #visible Game board
mode = 'O' # O : open mode , F : flag mode
 
for raw in range(10):
    gameBoard.append(['.']*10)
 
for raw in range(11):
    vGameBoard.append(['.']*10)
# make foundation for game board
 
#-------------------------------------------------------------------------------
 
def main() :
    global mode
    global vGameBoard
    global gameBoard
 
    graphic(vGameBoard)
 
    print('First selection is limited inward.')
    com = input('x, y : select (x, y) tile\n')
 
    x, y = com.split(", ")
    x, y = int(x), int(y)
 
    makeBoard(x, y)
    open(x, y)
 
    while 1 :
        print('-----------------------------------------------------------')
        graphic(vGameBoard)
        com = input('F : Flag mode\nO : Open mode\nx, y : select (x, y) tile\n')
        if com == 'F' :
            mode = 'F'
            continue
        elif com == 'O' :
            mode = 'O'
            continue
        else :
            x, y = com.split(", ")
            x, y = int(x), int(y)
            if mode == 'F' :
                flag(x, y)
            elif mode == 'O' :
                open(x, y)
            else :
                print('Error_1')
                exit()
 
        if check() == 1:
            exit()
 
#-------------------------------------------------------------------------------
 
def makeBoard(x, y) :
 
    global gameBoard
    global vGameBoard
 
    for raw in range(10) :
        for line in range(10) :
            if randint(1, 100) < 20 :
                gameBoard[raw][line] = '#'
 
    for raw in range(y - 2, y + 1):
        for line in range(x - 2, x + 1):
            gameBoard[raw][line] = '.'
 
    for raw in range(10) :
        for line in range(10) :
 
            mineNumber = 0
            if gameBoard[raw][line] == '.' :
                if 0 <= (raw - 1) and (raw - 1) <= 9 and 0 <= (line - 1) and (line - 1) <= 9 :
                    if gameBoard[raw - 1][line - 1] == '#' :
                        mineNumber += 1
                if 0 <= (raw - 1) and (raw - 1) <= 9 and 0 <= (line) and (line) <= 9 :
                    if gameBoard[raw - 1][line] =='#' :
                        mineNumber += 1
                if 0 <= (raw - 1) and (raw - 1) <= 9 and 0 <= (line + 1) and (line + 1) <= 9 :
                    if gameBoard[raw - 1][line + 1] == '#' :
                        mineNumber += 1
                if 0 <= (raw) and (raw) <= 9 and 0 <= (line - 1) and (line - 1) <= 9 :
                    if gameBoard[raw][line - 1] == '#' :
                        mineNumber += 1
                if 0 <= (raw) and (raw) <= 9 and 0 <= (line + 1) and (line + 1) <= 9 :
                    if gameBoard[raw][line + 1] == '#' :
                        mineNumber += 1
                if 0 <= (raw + 1) and (raw + 1) <= 9 and 0 <= (line - 1) and (line - 1) <= 9 :
                    if gameBoard[raw + 1][line - 1] == '#' :
                        mineNumber += 1
                if 0 <= (raw + 1) and (raw + 1) <= 9 and 0 <= (line) and (line) <= 9 :
                    if gameBoard[raw + 1][line] == '#' :
                        mineNumber += 1
                if 0 <= (raw + 1) and (raw + 1) <= 9 and 0 <= (line + 1) and (line + 1) <= 9 :
                    if gameBoard[raw + 1][line + 1] == '#' :
                        mineNumber += 1
 
                gameBoard[raw][line] = str(mineNumber)
 
 
#-------------------------------------------------------------------------------
 
def flag(x, y) :
    global vGameBoard
    if vGameBoard[y-1][x-1] == '.' :
        vGameBoard[y-1][x-1] = 'P'
    elif vGameBoard[y-1][x-1] == 'P' :
        vGameBoard[y-1][x-1] = '.'
    else :
        print('error_2')
        exit()
# P(flag) -> not flag / not flag -> flag
 
#-------------------------------------------------------------------------------
 
def graphic(table) :
    global mode
 
    print('Current mode = {}'.format(mode))
 
 
    for raw in range(10) :
        for line in range(10) :
            print(table[raw][line], end = ' ')
        print('')
# make gameBoard visible by texts
 
#-------------------------------------------------------------------------------
 
def open(x, y) :
    global vGameBoard
    global gameBoard
 
    if gameBoard[y-1][x-1] == '#' :
        vGameBoard[y-1][x-1] = gameBoard[y-1][x-1]
        graphic(vGameBoard)
        print('게임에 패배했습니다.')
        exit()
# If player select mine, then player defeat
 
    else :
        openlemma(x, y)
# open selected mine
 
#-------------------------------------------------------------------------------
 
def openlemma(x, y) : # for making function simpl
    global vGameBoard
    global gameBoard
    vGameBoard[y-1][x-1] = gameBoard[y-1][x-1]
    raw = y - 1
    line = x - 1
 
    if gameBoard[raw][line] == '0' :
        if 0 <= (raw - 1) and (raw - 1) <= 9 and 0 <= (line - 1) and (line - 1) <= 9 :
            if gameBoard[raw - 1][line - 1] == '0' and vGameBoard[raw-1][line-1] == '.' :
                openlemma(x-1, y-1)
 
        if 0 <= (raw - 1) and (raw - 1) <= 9 and 0 <= (line) and (line) <= 9 :
            if gameBoard[raw - 1][line] == '0' and vGameBoard[raw-1][line] == '.' :
                openlemma(x, y-1)
 
        if 0 <= (raw - 1) and (raw - 1) <= 9 and 0 <= (line + 1) and (line + 1) <= 9 :
            if gameBoard[raw - 1][line + 1] == '0' and vGameBoard[raw-1][line+1] == '.' :
                openlemma(x+1, y-1)
 
        if 0 <= (raw) and (raw) <= 9 and 0 <= (line - 1) and (line - 1) <= 9 :
            if gameBoard[raw][line - 1] == '0' and vGameBoard[raw][line-1] == '.' :
                openlemma(x-1, y)
 
        if 0 <= (raw) and (raw) <= 9 and 0 <= (line + 1) and (line + 1) <= 9 :
            if gameBoard[raw][line + 1] == '0' and vGameBoard[raw][line+1] == '.' :
                openlemma(x+1, y)
 
        if 0 <= (raw + 1) and (raw + 1) <= 9 and 0 <= (line - 1) and (line - 1) <= 9 :
            if gameBoard[raw + 1][line - 1] == '0' and vGameBoard[raw+1][line-1] == '.' :
                openlemma(x-1, y+1)
 
        if 0 <= (raw + 1) and (raw + 1) <= 9 and 0 <= (line) and (line) <= 9 :
            if gameBoard[raw + 1][line] == '0' and vGameBoard[raw+1][line] == '.' :
                openlemma(x, y+1)
 
        if 0 <= (raw + 1) and (raw + 1) <= 9 and 0 <= (line + 1) and (line + 1) <= 9 :
            if gameBoard[raw + 1][line + 1] == '0' and vGameBoard[raw+1][line+1] == '.' :
                openlemma(x+1, y+1)
# 주의의 0인 타일에 다시 openlemma를 적용
 
 
    if gameBoard[raw][line] == '0' :
        if 0 <= (raw - 1) and (raw - 1) <= 9 and 0 <= (line - 1) and (line - 1) <= 9 :
            vGameBoard[raw-1][line-1] = gameBoard[raw-1][line-1]
 
        if 0 <= (raw - 1) and (raw - 1) <= 9 and 0 <= (line) and (line) <= 9 :
            vGameBoard[raw-1][line] = gameBoard[raw-1][line]
 
        if 0 <= (raw - 1) and (raw - 1) <= 9 and 0 <= (line + 1) and (line + 1) <= 9 :
            vGameBoard[raw-1][line+1] = gameBoard[raw-1][line+1]
 
        if 0 <= (raw) and (raw) <= 9 and 0 <= (line - 1) and (line - 1) <= 9 :
            vGameBoard[raw][line-1] = gameBoard[raw][line-1]
 
        if 0 <= (raw) and (raw) <= 9 and 0 <= (line + 1) and (line + 1) <= 9 :
            vGameBoard[raw][line+1] = gameBoard[raw][line+1]
 
        if 0 <= (raw + 1) and (raw + 1) <= 9 and 0 <= (line - 1) and (line - 1) <= 9 :
            vGameBoard[raw+1][line-1] = gameBoard[raw+1][line-1]
 
        if 0 <= (raw + 1) and (raw + 1) <= 9 and 0 <= (line) and (line) <= 9 :
            vGameBoard[raw+1][line] = gameBoard[raw+1][line]
 
        if 0 <= (raw + 1) and (raw + 1) <= 9 and 0 <= (line + 1) and (line + 1) <= 9 :
            vGameBoard[raw+1][line+1] = gameBoard[raw+1][line+1]
#0인 타일의 주변의 타일을 연다
 
 
#-------------------------------------------------------------------------------
 
def check() : #게임의 승리를 확인
    global vGameBoard
 
    gameEnded = 0
 
    for raw in range(10) :
        for line in range(10) :
            if gameBoard[raw][line] != '#' :
                if vGameBoard[raw][line] == gameBoard[raw][line] :
                    gameEnded = 1
 
                else :
                    gameEnded = 0
                    return 0
 
    if gameEnded == 1 :
        print('게임에 승리하였습니다.')
        return 1
 
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
 
main()