Minesweeper gameboard generator: bug while generating board

72 Views Asked by At

Here is my code for generating minesweeper gameboard.

import random

objects = []

class Minesweeper_gameboard():
    def __init__(self, width, height, num_mines):
        self.width = width
        self.height = height
        self.num_mines = num_mines
        objects.append(self)
    def game_board(self):
        
        board = [[0 for i in range(self.height)] for j in range(self.width)]
        number_of_mines = 0
        
        while number_of_mines < self.num_mines:
            x = random.randint(0, self.width - 1)
            y = random.randint(0, self.height - 1)
            
            #Create conditions, when test is true, adopt range: a_True; when test is false, adopt range: a_False
            a_test = (x - 1) >= 0
            b_test = (x + 1) < self.width
            c_test = (y - 1) >= 0
            d_test = (y + 1) < self.height
            #when (x-1) is more or equal to 0, use (x-1) as the starting point of the range, vice versa
            a_True = x-1 
            b_True = x+2
            c_True = y-1
            d_True = y+2
            #when (x-1) is less than 0, x locate at the corner, use x as the starting point of the range, vice versa
            a_False = x 
            b_False = x+1
            c_False = y
            d_False = y+1
            
            condition_list = [[a_test, a_True, a_False], [b_test, b_True, b_False],
                                [c_test, c_True, c_False], [d_test, d_True, d_False]]
            #Delete the condition that is not satisfied
            for i in condition_list:
                if i[0] == True:
                    del i[0]
                    del i[1]
                else:
                    del i[0]
                    del i[0]
            #Flatten the list with a nested loop
            condition_list = [a for b in condition_list for a in b]
            #Create Rules to illustrate mine locations            
            if board[x][y] != 'X':
                board[x][y] = 'X'
                number_of_mines += 1
            try:
            #call out the flatten condition_list as the range to illustrate the number of mines nearby
                for i in range(condition_list[0], condition_list[1]):
                    for j in range(condition_list[2], condition_list[3]):
                        if board[i][j] != 'X':
                            board[i][j] += 1
            except:
                continue
            else:
                x = random.randint(0, self.width-1)
                y = random.randint(0, self.height-1)
        return board
    
    def player_board(self):
        player_board = [['-' for i in range(self.height)] for j in range(self.width)]
    
    def display_board(self, board):
        for row in board:
            print(" ".join(str(cell) for cell in row))
            print("")
            
#testing function
mine = Minesweeper_gameboard(10,10,10)
for object in objects:
    object.game_board()
    object.display_board(object.game_board())`

When I tried to generate:

mine = Minesweeper_gameboard(10, 10, 10)

the gameboard generator generates the correct gameboard:

X 2 0 0 1 1 1 0 0 0

X 2 0 0 1 X 2 1 0 0

1 1 0 0 1 2 X 1 0 0

0 1 2 2 1 1 1 1 0 0

1 2 X X 1 0 0 1 2 2

X 2 2 2 1 0 0 1 X X

1 1 0 0 0 0 0 1 2 2

0 0 0 0 0 0 0 0 0 0

0 0 0 0 0 1 1 1 0 0

0 0 0 0 0 1 X 1 0 0

But when I tried to generate:

mine = Minesweeper_gameboard(10, 15, 30)

there's an error in the gameboard output:

0 0 0 0 1 X 2 X X X 3 1 X 1 0

0 1 1 2 2 2 3 4 7 X 3 1 1 1 0

2 **4** X 3 X 1 1 X X 3 2 0 1 1 1

X **5** X 4 2 2 2 2 2 1 0 0 1 X 1

3 5 X 3 2 X 1 0 1 1 2 1 2 1 1

X 2 1 2 X 2 2 2 4 X 3 X 1 0 0

1 2 1 2 1 1 1 X X X 4 1 2 1 1

0 1 X 2 1 0 1 3 X X 2 0 1 X 1

0 1 2 X 3 2 2 2 3 3 1 0 2 2 2

0 0 1 1 3 X 2 1 X 1 0 0 1 X 1

The numering of gameboard in the bold grid is incorrect. Number 4 should be 3, and numebr 5 should be 4 instead.

Can anyone figure out why when i tried to generate a board with 10*15 grids, 30 mines, number of mines nearby cannot be generated as expected? Thanks. You can copy my code and run in your devices as well.

1

There are 1 best solutions below

1
SIGHUP On

Here's a different approach.

The board has a number of rows and columns. There needs to be a randomly placed number of mines. Cells that are not mines need to indicate the number of mines that are "nearby". A mine is considered to be in the centre of a 3x3 matrix. When considered the extremes of the matrix we have ensure that we don't go outside the bounds of the board.

Therefore:

from random import sample

EMPTY = 0
MINE = 'X'

ADJ = [
    (-1, -1),
    (-1, 0),
    (-1, 1),
    (0, -1),
    (0, 1),
    (1, -1),
    (1, 1),
    (1, 0)
]
class Minesweeper_gameboard:
    def __init__(self, rows, cols, num_mines):
        self._rows = rows
        self._cols = cols
        self._num_mines = num_mines
        self.reset()
    def initialise(self):
        self.board = [[EMPTY for _ in range(self._cols)] for _ in range(self._rows)]
    def add_mines(self):
        for v in sample(range(self._rows*self._cols), k=self._num_mines):
            row, col = divmod(v, self._cols)
            self.board[row][col] = MINE # type: ignore
    def nearby(self):
        for row in range(self._rows):
            for col in range(self._cols):
                if self.board[row][col] != MINE:
                    for ra, ca in ADJ:
                        if (r := row+ra) >= 0 and r < self._rows and (c := col + ca) >= 0 and c < self._cols:
                            if self.board[r][c] == MINE:
                                self.board[row][col] += 1 # type: ignore
    def reset(self, new_mines=True):
        if new_mines:
            self.initialise()
            self.add_mines()
        else:
            for row in self._rows:
                for col in self._cols:
                    if self.board[row][col] != MINE:
                        self.board[row][col] = 0
        self.nearby()

for row in Minesweeper_gameboard(10, 15, 30).board:
    print(*row)

Output (example):

0 1 X 1 1 X 2 1 1 1 1 1 1 X X
1 2 1 1 1 1 2 X 2 2 X 1 1 2 2
X 2 1 0 0 0 1 2 X 3 3 4 2 1 0
2 X 1 1 1 1 0 1 1 2 X X X 3 2
1 1 2 3 X 2 0 0 0 1 2 4 4 X X
0 0 2 X X 4 1 2 1 1 1 2 X 3 2
1 1 4 X X 4 X 3 X 2 2 X 2 1 0
1 X 3 X 4 4 X 3 1 2 X 3 2 1 0
1 1 2 2 X 2 2 2 1 1 1 2 X 1 0
0 0 0 1 1 1 1 X 1 0 0 1 1 1 0