Strange bug. For loop code works fine for most values, but then appends 4 times with certain values

Viewed 108

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) 
2 Answers

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))

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
Related