Search code examples
pythonnumpyminesweeper

Python - Minesweeper using Numpy: How do I count bombs around each tile?


I was trying to make a simple minesweeper board but when I execute the following code:

import random
import tkinter as tk
import numpy as np

WIDTH = 4
HEIGHT = 4

class Board:
def __init__(self, width, height):
    self.width = width
    self.height = height
    number_of_mines = (width-1)*(height-1)
    self.number_of_mines = number_of_mines
    """# added 'padding' to avoid IndexOutOfRange errors, so iterate statring at index 1 and iterate to index whatever-1"""
    board = np.array([[0 for i in range(width+2)] for j in range(height+2)], dtype = str)
    self.board = board
    for i in range(number_of_mines):
        # generate random co-ords for bombs
        while True:
            x_pos, y_pos = random.randint(1, width), random.randint(1,height)
            if board[y_pos, x_pos] != 'b':
                board[y_pos, x_pos] = 'b'
                break

    # iterate over each square of the board
    for y_pos in range(1, height):
        for x_pos in range(1, width):
            # if the square in question is a bomb then skip it
            if board[y_pos, x_pos] == 'b':
                continue
            adj_squares = board[(y_pos-1):3, (x_pos-1):3]
            count = 0
            for square in np.nditer(adj_squares):
                if square == 'b':
                    count+=1
            board[y_pos, x_pos]=str(count)


test = Board(WIDTH, HEIGHT)
print(test.board)

I get an output like this:

[['0' '0' '0' '0' '0' '0']
['0' 'b' 'b' 'b' 'b' '0']
['0' '2' '2' 'b' '0' '0']
['0' 'b' '0' '0' '0' '0']
['0' '0' 'b' 'b' 'b' '0']
['0' '0' '0' '0' '0' '0']]

Going bigger than a 4x4 board breaks my code entirely giving me a ValueError.

Any help would be much appreciated :)


Solution

  • I see multiple issues with your code.

    First off, you don't update self.board, you update board, meaning your final result will not return any numbers other than the '0' and 'b'.

    You also do not update the numbers in the first row, I presume to prevent an indexing error? I would change your ranges from starting with 1 to starting with 0.

    I have changed this line code as following to take care of the wrong indexing:

    adj_squares = board[(y_pos-1):3, (x_pos-1):3]
    

    To this line:

    adj_squares = self.board[max((y_pos-1),0):min(height, y_pos + 2), max((x_pos-1),0):min(width, x_pos + 2)]
    

    This prevents the array being indexed in non existent coordinates (in your code it goes wrong when you check the last column).

    This makes the final result:

    import random
    import numpy as np
    
    WIDTH = 5
    HEIGHT = 5
    
    class Board:
        def __init__(self, width, height):
            self.width = width
            self.height = height
            number_of_mines = (width-1)*(height-1)
            self.number_of_mines = number_of_mines
            """# added 'padding' to avoid IndexOutOfRange errors, so iterate statring at index 1 and iterate to index whatever-1"""
            board = np.array([[0 for i in range(width+2)] for j in range(height+2)], dtype = str)
            self.board = board
            for i in range(number_of_mines):
                # generate random co-ords for bombs
                while True:
                    x_pos, y_pos = random.randint(1, width), random.randint(1,height)
                    if self.board[y_pos, x_pos] != 'b':
                        self.board[y_pos, x_pos] = 'b'
                        break
    
            # iterate over each square of the board
        for y_pos in range(0, height + 2):
            for x_pos in range(0, width + 2):
                # if the square in question is a bomb then skip it
                if self.board[y_pos, x_pos] == 'b':
                    continue
                adj_squares = self.board[max((y_pos-1),0):min(height + 2, y_pos + 2), max((x_pos-1),0):min(width + 2, x_pos + 2)]                    print(adj_squares)
                    count = 0
                    for square in np.nditer(adj_squares):
                        if square == 'b':
                            count+=1
                    print(count)
    
                    self.board[y_pos, x_pos]=str(count)
    
    
    test = Board(WIDTH, HEIGHT)
    print(test.board)