2

I am recreating the popular game minesweeper. Bombs are hidden in a grid, and where grids do not contain bombs, contain numbers of how many (if any ) bombs are connected to the grid. In order to achieve this in python, I have created a list of list of integers. this list of list acts as a set of coordinates, the number inside is then used as a key in a dictionary which is used in pygame to display an image. for example, if grid[i][j] = (0, 0) would be used in a dictionary to display the image for an empty box, 50 would be used to display a bomb, 1 would be used to display the number 1 within the box.

in order to display the numbers surrounding the bombs, I have made for loop with if statements to add tuples which correspond to the coordinates. when a bomb location has (14,x) in the first for loop it appends (13,x) 4 times. and I cannot figure out why. I am most curious to find help as to what the bug is, and would be even more appreciative if someone could suggest a more 'pythonic' way of achieving this. Please see the code below.

The code runs and produces a list of tuples. It might have to be run a couple of times to produce the results.

My code:

import random

bomb_count = 15

def set_grid():
    grid = [[0 for x in range(30)] for x in range(15)]
    num_columns = [x for x in range(30)]
    num_rows = [x for x in range(15)]
    counter = 0

    while counter < bomb_count:
        rndnum1 = random.choice(num_rows)
        rndnum2 = random.choice(num_columns)

        grid[rndnum1][rndnum2] = 50
        counter += 1

    return grid


def set_bomb_locations(grid):
    grid_with_bombs = []
    print("grid = ", grid)

    for i in range(len(grid)):
        for j in range(len(grid[i])):
            if grid[i][j] == 50:
                grid_with_bombs.append((i, j))

    return grid_with_bombs


def number_layout(grid_with_bombs):
    numbers_to_add_to_grid = []

    for a, b in grid_with_bombs:
        c = a + b
        print(" this is", a, b)
        if b < 29:  # only if b isnt more than 29
            c = a, b + 1
            print("if statement 1: ", numbers_to_add_to_grid)
            numbers_to_add_to_grid.append(c)
        if b > 0:  # only if b isnt 0
            c = a, b - 1
            print("if statement 2: ", numbers_to_add_to_grid)
            numbers_to_add_to_grid.append(c)
        if a > 0 and b > 0:  # only if a isnt 0 and b isnt 0
            c = a - 1, b - 1
            print("if statement 3: ", numbers_to_add_to_grid)
            numbers_to_add_to_grid.append(c)
        if a > 0:  # only if a isnt 0
            c = a - 1, b
            print("if statement 4: ", numbers_to_add_to_grid)
            numbers_to_add_to_grid.append(c)
        if a > 0 and b < 29:  # only if a isnt 0 and b isnt more than 29
            c = a - 1, b + 1  # error?
            print("if statement 5: ", numbers_to_add_to_grid)
            numbers_to_add_to_grid.append(c)
        if a < 14 and b > 0:  # only if a isnt more than 15 and b isnt 0
            c = a + 1, b - 1
            print("if statement 6: ", numbers_to_add_to_grid)
        numbers_to_add_to_grid.append(c)
        if a < 14:  # only if a isnt more than 15
            c = a + 1, b
            print("if statement 7: ", numbers_to_add_to_grid)
        numbers_to_add_to_grid.append(c)
        if a < 14 and b < 29:  # only if a isnt more than 15 and b isnt more than 29
            c = a + 1, b + 1
            print("if statement 8: ", numbers_to_add_to_grid)
        numbers_to_add_to_grid.append(c)

    for a, b in numbers_to_add_to_grid:

        if grid[a][b] != 50:
            grid[a][b] += 1

    return grid

grid = set_grid()
grid2 = set_bomb_locations(grid)
grid_with_bombs = number_layout(grid2) 
Qiu YU
  • 517
  • 4
  • 20
Pyson
  • 57
  • 7
  • Please supply the expected [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). Show where the intermediate results differ from what you expected. As the posting guidelines tell you "make it easy for others to help you." Your output has labeled `if` statements and a list of 2-tuples, with no particular explanation of where to find the error. – Prune Aug 21 '20 at 19:01
  • I suggest that you first reduce the grid size so we don't get dazzled with the quantity of data. Second, don't tell us to run this a few times: *you* force the situation you want to show us. Then detect the situation, halt the program, and add the deterministic output to your post. Trace a couple of suspect variables related to the problem. Use meaningful variable names. – Prune Aug 21 '20 at 19:03
  • 1
    Since you're not using `elif`, multiple `if` conditions can be satisfied. Is that intentional? – Barmar Aug 21 '20 at 19:14
  • the if statements are intentional. because the tuple for the bomb location is used to map the surrounding coords in the form of a tuple. – Pyson Aug 21 '20 at 20:02

2 Answers2

1

I realize these should be posted as comments, but I don't have enough reputation to comment.

[x for x in range(30)] can be changed to [*range(30)]

But that can be avoided by using random.randint() instead of random.choice.(), and the while loop isn't necessary, you can use a for loop since it's a set number:

from random import randint

for i in range(bomb_count):
    grid[randint(0, 30)][randint(0, 15)] = 50

Instead of using len() and range() to iterate through the grid, just iterate through it:

#INSTEAD OF:
    for i in range(len(grid)):
        for j in range(len(grid[i])):
            if grid[i][j] == 50:
                grid_with_bombs.append((i, j))
#USE THIS:
    for c_index, column in enumerate(grid):
        for r_index, row in enumerate(column):
            if row == 50:
                grid_with_bombs.append((c_index, r_index))
Elan-R
  • 484
  • 3
  • 6
  • I would help more, but I have no clue what you're trying to achieve with the mass of `if` statements – Elan-R Aug 21 '20 at 19:14
  • Also, in the second suggestion I used the variable `row`. Though this is inaccurate as it's technically a point, `row` makes it easier to understand what is represents. – Elan-R Aug 21 '20 at 20:11
  • Hi Elan-R thanks for your answer however some of it is incorrect. for column in grid DOES NOT return the same as for i in range in this example, the first appends tuple locations, the second does not. also grid[randint(0,30][randint(0,15)] = 50 not as you have wrote it, however thanks for the suggestions. – Pyson Aug 22 '20 at 15:43
  • Oops my mistake. I just edited it. I changed the second code to accomplish your goal as well. – Elan-R Aug 22 '20 at 15:49
  • thanks very much Elan-R that works now, what about the [*range 30] ? im interested how you would use this approach to achieve the same as grid = [[0 for x in range(30)] for x in range(15)] as [*range 30] does not produce the same thing – Pyson Aug 22 '20 at 17:06
  • You forgot to put the parentheses. `*` unpacks an iterable and `[]` generates a list. So `[*range(30)]` unpacks the iterable `range(30)` into the list constructor. – Elan-R Aug 22 '20 at 17:14
  • And I'm not replacing `[0 for x in range(30)]` I'm replacing `[x for x in range(30)]`. But if you wanted to improve `[0 for x in range(30)]`, just do `[0] * 30`. – Elan-R Aug 22 '20 at 17:17
0

i fixed it, by re writing the code to fit a "logic by exception" rule. to over come my problem. i simply added every tuple to a temporary list, and then removed the tuples that were not in the required parameters. I think the lesson here is, the more complex the logic, the more likely and, the harder fixing any bugs become. Below is the code that now works. sometimes a way to fix a bug, is to rewrite the logic. free to add further improvements. on a side note, i have found a grid created this way is very useful in creating games in pygame like snake, minsweeper, tetris, etc etc, i learned this approach from someone else and is really helping me learn how useful lists are in python, feel free to use and share.

my code below

from random import randint
def set_grid():
grid_with_bombs = []
grid_with_bombs_and_numbers = []

# create grid with all 0
grid = [[0 for x in range(30)] for x in range(15)]

# choose 15 random locations for bombs
for i in range(bomb_count):
    grid[randint(0, 14)][randint(0, 29)] = 50

# create a list of bomb locations as tuples
for c_index, column in enumerate(grid):
    for r_index, row in enumerate(column):
        if row == 50:
            grid_with_bombs.append((c_index, r_index))

# use tuples from above to mark out any grid cell as next to a bomb
# and create a temp list of tuples with those locations
temp_tuple_list = []
for a, b in grid_with_bombs:

    c = a, b + 1
    d = a, b - 1
    e = a - 1, b - 1
    f = a - 1, b
    g = a - 1, b + 1
    h = a + 1, b - 1
    i = a + 1, b
    j = a + 1, b + 1

    temp_list = [c,d,e,f,g,h,i,j]
    for i in temp_list:
        temp_tuple_list.append(i)

# now remove tuples that correspond to a grid reference that does not exist. e.g (-1,29)
for a,b in temp_tuple_list:

    c = a,b
    if a == -1 or a >= 15:
        continue
    if b == -1 or b >= 30:
        continue
    else:
        grid_with_bombs_and_numbers.append(c)


# every tuple that isnt 50( a bomb ) or 0 ( nothing ) is a number square,
# each time a tuple is in the list add 1 this will give us a value to display
for a,b in grid_with_bombs_and_numbers:
    if grid[a][b] != 50:
        grid[a][b] += 1


return grid
Pyson
  • 57
  • 7