user1019450
user1019450

Reputation: 41

Python minesweeper game - user chooses grid size and how many mines

I have the following python code but I'm really struggling to get it to work.

It's a basic minesweeper game in terminal. This version is a little different to the others out there in that it's supposed to start by asking the user how big the grid, then how many mines to insert. The rest of it is your good old basic minesweeper.

def display():
    for x in range(SIZE):
        if x == 0:
            print(" ", end="")
            for i in range(SIZE):
                print("   " + str(i), end="")
            print("\n  |-" + "--|-" * (SIZE - 1) + "--|")
            print("0 |", end="")
            for y in range(SIZE):
                print(" " + str(BOARD[x][y].status) + "|", end="")
            print("\n  |-" + "--|-" * (SIZE -1) + "--|")
        elif x == SIZE -1:
            print(str(SIZE-1) + " |", end="")
            for y in range(SIZE):
                print(" " + str(BOARD[x][y].status) + "|", end="")
            print("\n  |-" + "--|-" * (SIZE -1) + "--|")
        else:
            print(str(x) + " |", end="")
            for y in range(SIZE):
                print(" " + str(BOARD[x][y].status) + "|", end="")
            print("\n  |-" + "--|-" * (SIZE-1) + "--|")

class Square:
    def __init__(self, x, y):
        self.status = "- "
        self.row = x
        self.column = y
        self.mine = False
        self.neighbours = []
        self.danger = 0

    def status_get(self):
        return this.status

def first_move():
    # establish mine locations, ignoring stating dig
    print("""Where would you like to start?\n""")
    while True:
        a, b = input("Enter an x and y coodinate, comma separated, to dig the first square: ").split(",")
        x = int(a)
        y = int(b)
        if x >= SIZE or y >= SIZE:
            print("Out of bounds, try again.")
            continue
        else:
            for m in range(MINES):
                place_mines(x, y)
            welfare(BOARD[x][y])
            break

def place_mines(x, y):
    # Recursively place mines, ignoring starting square
    global BOARD
    h = random.randint(0, SIZE - 1)
    w = random.randint(0, SIZE - 1)
    if h == x and w == y:
        place_mines(x, y)
    else:
        if not BOARD[h][w].mine:
            BOARD[h][w].mine = True
            for sqr in BOARD[h][w].neighbours:
                sqr.danger += 1
        else:
            place_mines(x, y)

def next_turn():
    # keeps promting to dig, flag, or end the game
    move = input("What would you like to do? ").lower()
    if move == "!quit":
        print("Thank you, come again.")
        quit(0)
    elif move == "d":
        dig()
        display()
    elif move == "f":
        flag()
        display()
    elif move == "r":
        unflag()
        display()
    elif move == "!help":
        print("!quit = Quit game.")
        print("d = Uncover a square.")
        print("f = Plant a flag marking a square as containing a mine.")
        print("r = Remove a flag.")
    else:
        print("You'll have to speak up... Try again.")

def unflag():
    # Unflags a previously flagged square
    while True:
        a, b = input("Supply an x and y coordinate to unflag: ").split(",")
        x = int(a)
        y = int(b)
        if x >= SIZE or y >= SIZE:
            print("Out of bounds, try again.")
            continue
        else:
            global BOARD
            if BOARD[x][y].status == "F ":
                BOARD[x][y].status = "- "
            break

def flag():
    # flag a square to indicate a mine
    while True:
        a, b = input("Supply an x and y coordinate to flag: ").split(",")
        x = int(a)
        y = int(b)
        if x >= SIZE or y >= SIZE:
            print("Out of bounds, try again.") 
            continue
        else:
            global BOARD
            BOARD[x][y].status = "F "
            global FLAGS
            if BOARD[x][y].mine and BOARD[x][y].status == "F ":
                FLAGS += 1
            break

def dig():
    # Uncover a square. If it's a bomb it's game over. Otherwise, reveal nearby squares
    while True:
        a, b = input("Supply an x and y coordinate to dig: ").split(",")
        x = int(a)
        y = int(b)
        if x >= SIZE or y >= SIZE:
            print("Out of bounds, try again.")
            continue
        else:
            if BOARD[x][y].mine is True:
                print("BANG! You have died!")
                for x in range(SIZE):
                    for y in range(SIZE):
                        sqr = BOARD[x][y]
                        if sqr.mine:
                            sqr.status = "@"
                display()
                quit(0)
            else:
                BOARD[x][y].status = "x"
                welfare(BOARD[x][y])
                break


def welfare(sqr, history=[]):
    # update square state after each dig to reveal mine status
    if sqr.danger == 0:
        sqr.status = "x "
        for box in sqr.neighbours:
            if box not in history:
                history.append(box)
                welfare(box, history)
    else:
        if not sqr.mine:
            sqr.status = str(sqr.danger) + " "
        #for box in sqr.neighbours:
        #    if not box.mine:
        #        if box not in history:
        #            history.append(box)
        #            welfare(box, history)

def win_state():
    # is it over, can I go home now?
    global FLAGS
    global MINES
    global BOARD
    correct = 0
    if FLAGS == MINES:
        for x in range(SIZE):
            for y in range(SIZE):
                sqr = BOARD[x][y]
                if sqr.mine and sqr.status == "F ":
                    correct += 1
                    if correct == MINES:
                        print("Congratulations!\n")
                        print("You found " + str(MINES) + " mines and didn't die.")
                        exit(0)
            



def startup():
    # set game parameters, give all the squares their neighbours
    print("It's Minesweeper, how wide do you want the board?")
    global SIZE
    while True:
        SIZE = int(input("Give me a size, between 3 and 10 inclusive "))
        if SIZE not in range(3, 11):
            print("Did I stutter? Try again. ")
            continue
        else:
            global MINES
            while True:
                MINES = int(input("How many mines do you want? "))
                if MINES >= SIZE ** 2:
                    print("Whoa! That's too many. Try Again. ")
                    continue
                else:
                    break
            print("OKAY")
            # initialise an empty board
            global BOARD
            BOARD = [[None for _ in range(SIZE)] for _ in range(SIZE)]
            
            # fill that board with square objects
            for s in range(SIZE):
                for d in range(SIZE):
                    BOARD[s][d] = Square(s, d)
            # link each square to it's neighbour
            
            for a in range(SIZE):
                for b in range(SIZE):
                    sqr = BOARD[a][b]
                    if a == 0:
                        sqr.neighbours.append(BOARD[a + 1][b])
                        if b != SIZE - 1:
                            sqr.neighbours.append(BOARD[a + 1][b + 1])
                            sqr.neighbours.append(BOARD[a][b + 1])
                        if b != 0:
                            sqr.neighbours.append(BOARD[a][b - 1])
                            sqr.neighbours.append(BOARD[a + 1][b - 1])
                    elif a == SIZE - 1:
                        sqr.neighbours.append(BOARD[a - 1][b])
                        if b != SIZE - 1:
                            sqr.neighbours.append(BOARD[a][b + 1])
                            sqr.neighbours.append(BOARD[a - 1][b + 1])
                        if b != 0:
                            sqr.neighbours.append(BOARD[a - 1][b - 1])
                            sqr.neighbours.append(BOARD[a][b - 1])
                    else:
                        sqr.neighbours.append(BOARD[a + 1][b])
                        sqr.neighbours.append(BOARD[a - 1][b])
                        if b != SIZE - 1:
                            sqr.neighbours.append(BOARD[a - 1][b + 1])
                            sqr.neighbours.append(BOARD[a][b + 1])
                            sqr.neighbours.append(BOARD[a + 1][b + 1])
                        if b != 0:
                            sqr.neighbours.append(BOARD[a - 1][b - 1])
                            sqr.neighbours.append(BOARD[a][b - 1])
                            sqr.neighbours.append(BOARD[a + 1][b - 1]) 
                      
                        


startup()
while True:
    next_turn()
    win_state()

Upvotes: 0

Views: 509

Answers (1)

Tim Roberts
Tim Roberts

Reputation: 54645

Here's a slightly reorganized version of your code that works. You forgot to call first_move. You were inconsistent in whether status had to include the trailing blank (I'm not using it). If changes all your while loops so they break on success; that avoids the confusion. Notice there is only one global here.

import random

def display():
    for x in range(SIZE):
        if x == 0:
            print(" ", end="")
            for i in range(SIZE):
                print("   " + str(i), end="")
            print("\n  |-" + "--|-" * (SIZE - 1) + "--|")
            print("0 |", end="")
            for y in range(SIZE):
                print(" " + str(BOARD[x][y].status) + " |", end="")
            print("\n  |-" + "--|-" * (SIZE -1) + "--|")
        elif x == SIZE -1:
            print(str(SIZE-1) + " |", end="")
            for y in range(SIZE):
                print(" " + str(BOARD[x][y].status) + " |", end="")
            print("\n  |-" + "--|-" * (SIZE -1) + "--|")
        else:
            print(str(x) + " |", end="")
            for y in range(SIZE):
                print(" " + str(BOARD[x][y].status) + " |", end="")
            print("\n  |-" + "--|-" * (SIZE-1) + "--|")

class Square:
    def __init__(self, x, y):
        self.status = "-"
        self.row = x
        self.column = y
        self.mine = False
        self.neighbours = []
        self.danger = 0

    def status_get(self):
        return this.status

def first_move():
    # establish mine locations, ignoring stating dig
    print("""Where would you like to start?\n""")
    while True:
        a, b = input("Enter an x and y coodinate, comma separated, to dig the first square: ").split(",")
        x = int(a)
        y = int(b)
        if x < SIZE and y < SIZE:
            break
        print("Out of bounds, try again.")
    for m in range(MINES):
        place_mines(x, y)
    welfare(BOARD[x][y])

def place_mines(x, y):
    # Recursively place mines, ignoring starting square
    h = random.randint(0, SIZE - 1)
    w = random.randint(0, SIZE - 1)
    if h == x and w == y:
        place_mines(x, y)
    else:
        if not BOARD[h][w].mine:
            BOARD[h][w].mine = True
            for sqr in BOARD[h][w].neighbours:
                sqr.danger += 1
        else:
            place_mines(x, y)

def next_turn():
    # keeps promting to dig, flag, or end the game
    display()
    move = input("What would you like to do? ").lower()
    if move == "!quit":
        print("Thank you, come again.")
        quit(0)
    elif move == "d":
        dig()
    elif move == "f":
        flag()
    elif move == "r":
        unflag()
    elif move == "!help":
        print("!quit = Quit game.")
        print("d = Uncover a square.")
        print("f = Plant a flag marking a square as containing a mine.")
        print("r = Remove a flag.")
    else:
        print("You'll have to speak up... Try again.")

def unflag():
    # Unflags a previously flagged square
    while True:
        a, b = input("Supply an x and y coordinate to unflag: ").split(",")
        x = int(a)
        y = int(b)
        if x < SIZE and y < SIZE:
            break
        print("Out of bounds, try again.")
    if BOARD[x][y].status == "F":
        BOARD[x][y].status = "-"

def flag():
    # flag a square to indicate a mine
    while True:
        a, b = input("Supply an x and y coordinate to flag: ").split(",")
        x = int(a)
        y = int(b)
        if x < SIZE and y < SIZE:
            break
        print("Out of bounds, try again.") 
    BOARD[x][y].status = "F"
    global FLAGS
    if BOARD[x][y].mine and BOARD[x][y].status == "F":
        FLAGS += 1

def dig():
    # Uncover a square. If it's a bomb it's game over. Otherwise, reveal nearby squares
    while True:
        a, b = input("Supply an x and y coordinate to dig: ").split(",")
        x = int(a)
        y = int(b)
        if x < SIZE and y < SIZE:
            break
        print("Out of bounds, try again.")
    if BOARD[x][y].mine:
        print("BANG! You have died!")
        for x in range(SIZE):
            for y in range(SIZE):
                sqr = BOARD[x][y]
                if sqr.mine:
                    sqr.status = "@"
        display()
        quit(0)
    else:
        BOARD[x][y].status = "x"
        welfare(BOARD[x][y])


def welfare(sqr, history=[]):
    # update square state after each dig to reveal mine status
    if sqr.danger == 0:
        sqr.status = "x"
        for box in sqr.neighbours:
            if box not in history:
                history.append(box)
                welfare(box, history)
    else:
        if not sqr.mine:
            sqr.status = str(sqr.danger)
        #for box in sqr.neighbours:
        #    if not box.mine:
        #        if box not in history:
        #            history.append(box)
        #            welfare(box, history)

def win_state():
    # is it over, can I go home now?
    correct = 0
    if FLAGS == MINES:
        for x in range(SIZE):
            for y in range(SIZE):
                sqr = BOARD[x][y]
                if sqr.mine and sqr.status == "F":
                    correct += 1
                    if correct == MINES:
                        print("Congratulations!\n")
                        print("You found " + str(MINES) + " mines and didn't die.")
                        exit(0)
            



def startup():
    # set game parameters, give all the squares their neighbours
    print("It's Minesweeper, how wide do you want the board?")
    while True:
        SIZE = int(input("Give me a size, between 3 and 10 inclusive "))
        if SIZE in range(3, 11):
            break
        print("Did I stutter? Try again. ")

    while True:
        MINES = int(input("How many mines do you want? "))
        if MINES < SIZE ** 2:
            break
        print("Whoa! That's too many. Try Again. ")
    print("OKAY")
    # initialise an empty board
    BOARD = [[None for _ in range(SIZE)] for _ in range(SIZE)]
    
    # fill that board with square objects
    BOARD = []
    for s in range(SIZE):
        row = []
        for d in range(SIZE):
            row.append( Square(s, d) )
        BOARD.append(row)
    # link each square to it's neighbour
    
    for a in range(SIZE):
        for b in range(SIZE):
            sqr = BOARD[a][b]
            if a == 0:
                sqr.neighbours.append(BOARD[a + 1][b])
                if b != SIZE - 1:
                    sqr.neighbours.append(BOARD[a + 1][b + 1])
                    sqr.neighbours.append(BOARD[a][b + 1])
                if b != 0:
                    sqr.neighbours.append(BOARD[a][b - 1])
                    sqr.neighbours.append(BOARD[a + 1][b - 1])
            elif a == SIZE - 1:
                sqr.neighbours.append(BOARD[a - 1][b])
                if b != SIZE - 1:
                    sqr.neighbours.append(BOARD[a][b + 1])
                    sqr.neighbours.append(BOARD[a - 1][b + 1])
                if b != 0:
                    sqr.neighbours.append(BOARD[a - 1][b - 1])
                    sqr.neighbours.append(BOARD[a][b - 1])
            else:
                sqr.neighbours.append(BOARD[a + 1][b])
                sqr.neighbours.append(BOARD[a - 1][b])
                if b != SIZE - 1:
                    sqr.neighbours.append(BOARD[a - 1][b + 1])
                    sqr.neighbours.append(BOARD[a][b + 1])
                    sqr.neighbours.append(BOARD[a + 1][b + 1])
                if b != 0:
                    sqr.neighbours.append(BOARD[a - 1][b - 1])
                    sqr.neighbours.append(BOARD[a][b - 1])
                    sqr.neighbours.append(BOARD[a + 1][b - 1]) 
    return SIZE, MINES, BOARD
              
                
FLAGS = 0

SIZE, MINES, BOARD = startup()
first_move()
while True:
    next_turn()
    win_state()

Upvotes: 1

Related Questions