Search code examples
pythonlistconditional-statementscountingdirections

Count string elements on a map based on where strings are and fill grid with the counts


I have a list called my_map that contains two different kinds of string values '.' and '&'. Now, for each value [x][y] that is a '.' I want to count the number of times an '&' was found in any of the eight directions next to the '.'

I created a grid to store the counts but I am just not able to formulate my conditions correctly. I can not use numpy arrays.

Note: 'S' and 'E' are treated like '.'

 my_map = ['................' '....&...........' '..........E.....'
 '&&..&...........' '....&&&.........' '......&&&&..&&..'
 '................' '.......&........' '.....&.&........'
 '....S...........' '.......&.&&.....']

def create_grid(my_map):
    grid = [[0]*(len(my_map[0])) for x in range(len(my_map))]
    return grid

grid = create_grid(my_map)

for x, y in [(x,y) for x in range(len(my_map)) for y in range(len(my_map[0]))]:
    #any '&' north ?
    if my_map[x][y+1]== '&' and my_map[x][y]=='.': 
        grid[x][y]+= 1
        #any '&' west ?
        if my_map[x-1][y]== '&' and my_map[x][y]=='.': 
            grid[x][y]+=1
            #any '&' south ?
            if my_map[x][y-1]== '&'and my_map[x][y]=='.': 
                grid[x][y]+=1
                #any '&' east ?
                if my_map[x+1][y]== '&'and my_map[x][y]=='.': 
                    grid[x][y]+=1
                    #any '&' north-east ?
                    if my_map[x+1][y+1] == '&'and my_map[x][y]=='.': 
                        grid[x][y]+=1
                        #any '&' south-west ?
                        if my_map[x-1][y-1] == '&'and my_map[x][y]=='.': 
                            grid[x][y]+=1
                            #any '&' south-east ?
                            if my_map[x+1][y-1]== '&'and my_map[x][y]=='.': 
                                grid[x][y]+=1
                                #any '&' north-west?
                                if my_map[x-1][y+1]== '&'and my_map[x][y]=='.': 
                                    grid[x][y]+=1

 #desired output for first 3 rows
grid = [[0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0],[2,2,1,1,2,1,0,0,0,0,0,0,0,0,0,0]]

At the moment, I get an 'IndexError: string index out of range'. I dont know how to limit the range so it will still be correct.The only thing I managed so far was a grid displaying 1s for all '.' and 0s for all '&'.


Solution

  • I don't think the nested conditionals are appropriate here; each outer conditional must be true for the inner ones to be evaluated. They should be independent of each other and sequential.

    It's also a lot of work and error-prone to enumerate every conditional by hand. For each cell, there are up to 8 directions in which a neighbor might live, and we do the exact same check on each direction. A loop is the appropriate construct for doing this; each loop iteration checks one neighboring cell, determining whether it's in bounds and of the appropriate character.

    Furthermore, since your grid has few &, it makes sense to only perform neighbor checks for & characters. For each one, increment counts for neighboring .s. Do the opposite if the grid is predominantly & characters.

    my_map = [
        '................', 
        '....&...........',
        '..........E.....',
        '&&..&...........', 
        '....&&&.........', 
        '......&&&&..&&..',
        '................', 
        '.......&........', 
        '.....&.&........',
        '....S...........', 
        '.......&.&&.....'
    ]
    
    grid = [[0] * len(x) for x in my_map]
    directions = [
        [-1, 0], [1, 0], [0, 1], [0, -1], 
        [-1, -1], [1, 1], [1, -1], [-1, 1]
    ]
    
    for row in range(len(my_map)):
        for col in range(len(my_map[row])):
            if my_map[row][col] == "&":
                for x, y in directions:
                    y += row
                    x += col
    
                    if y < len(my_map) and y >= 0 and \
                       x < len(my_map[y]) and x >= 0 and \
                       my_map[y][x] != "&":
                        grid[y][x] += 1
    
    for row in grid:
        print(row)
    

    Output:

    [0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    [2, 2, 1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    [0, 0, 1, 2, 0, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0]
    [2, 2, 1, 2, 0, 0, 0, 4, 3, 2, 1, 1, 2, 2, 1, 0]
    [0, 0, 0, 1, 2, 4, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0]
    [0, 0, 0, 0, 0, 1, 3, 4, 4, 2, 1, 1, 2, 2, 1, 0]
    [0, 0, 0, 0, 1, 1, 3, 0, 2, 0, 0, 0, 0, 0, 0, 0]
    [0, 0, 0, 0, 1, 0, 3, 0, 2, 0, 0, 0, 0, 0, 0, 0]
    [0, 0, 0, 0, 1, 1, 3, 2, 3, 2, 2, 1, 0, 0, 0, 0]
    [0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 1, 0, 0, 0, 0]
    

    And a version that overlays counts with the original map Minesweeper-style:

    0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0
    0 0 0 1 & 1 0 0 0 0 0 0 0 0 0 0
    2 2 1 2 2 2 0 0 0 0 0 0 0 0 0 0
    & & 1 2 & 4 2 1 0 0 0 0 0 0 0 0
    2 2 1 2 & & & 4 3 2 1 1 2 2 1 0
    0 0 0 1 2 4 & & & & 1 1 & & 1 0
    0 0 0 0 0 1 3 4 4 2 1 1 2 2 1 0
    0 0 0 0 1 1 3 & 2 0 0 0 0 0 0 0
    0 0 0 0 1 & 3 & 2 0 0 0 0 0 0 0
    0 0 0 0 1 1 3 2 3 2 2 1 0 0 0 0
    0 0 0 0 0 0 1 & 2 & & 1 0 0 0 0
    

    Try it!