pythonpython-3.xalgorithmspiral

Spiral matrix generator skips rows and columns


I wrote a code that is supposed to generate a spiral pattern of characters. It does generate a spiral, but for some reason, it skips the outermost column, the bottommost row, and the leftmost column (I'll leave an example output below). I can't figure out why this is happening on my own. I do check whether the cell two steps ahead is filled. Only if it is, we should make a turn, essentially creating the spiral.

def asterisk_spiral(size):
    matrix = [[' ' for _ in range(size)] for _ in range(size)]

    row, col = 0, 0
    directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]
    direction = 0

    for i in range(1, size * size // 2):
        matrix[row][col] = '*'

        next_row, next_col = row + directions[direction][0], col + directions[direction][1]
        next_next_row, next_next_col = row + 2 * directions[direction][0], col + 2 * directions[direction][1]
        
        if (
            0 <= next_row < size
            and 0 <= next_col < size
            and 0 <= next_next_row < size
            and 0 <= next_next_col < size
            and matrix[next_row][next_col] == ' '
            and matrix[next_next_row][next_next_col] != '*'
        ):
            row, col = next_row, next_col
        else:
            # turn
            direction = (direction + 1) % 4
            row, col = row + directions[direction][0], col + directions[direction][1]

    return matrix

def print_spiral(matrix):
    for row in matrix:
        print(' '.join(row))

size = 9

spiral_matrix = asterisk_spiral(size)
print_spiral(spiral_matrix)

Result:

[['*', '*', '*', '*', '*', '*', '*', '*', ' '],
 [' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' '],
 [' ', '*', '*', '*', '*', '*', ' ', '*', ' '],
 [' ', '*', ' ', ' ', ' ', '*', ' ', '*', ' '],
 [' ', '*', ' ', '*', '*', '*', ' ', '*', ' '],
 [' ', '*', ' ', '*', '*', '*', ' ', '*', ' '],
 [' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' '],
 [' ', '*', '*', '*', '*', '*', '*', '*', ' '],
 [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']]

whereas expected matrix is:

[['*', '*', '*', '*', '*', '*', '*', '*', '*'],
 [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*'],
 ['*', '*', '*', '*', '*', '*', '*', ' ', '*'],
 ['*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*'],
 ['*', ' ', '*', '*', '*', ' ', '*', ' ', '*'],
 ['*', ' ', '*', ' ', ' ', ' ', '*', ' ', '*'],
 ['*', ' ', '*', '*', '*', '*', '*', ' ', '*'],
 ['*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*'],
 ['*', '*', '*', '*', '*', '*', '*', '*', '*']]

Solution

  • When the if condition 0 <= next_row < size is true, but 0 <= next_next_row < size is not true, your logic will trigger a turn, but that means that for the first few lines the turn is made before hitting the boundary of the square, i.e. one step too early.

    Instead you should have this logic:

    "When the next cell in the current direction exists, and it is not so that one step further there is an asterisk, then go on in the same direction".

    That means the if condition could look like this:

            if (
                0 <= next_row < size
                and 0 <= next_col < size
                and not (0 <= next_next_row < size
                         and 0 <= next_next_col < size
                         and matrix[next_next_row][next_next_col] == '*')
            ):
    

    Once you have corrected this, you'll notice another issue: the loop doesn't make enough iterations. The for statement should be corrected to this:

        for i in range(size + size * size // 2):
    

    Depending how you want the output to look for when size is an even number, you may want to reduce that by one iteration. If so, change it to this:

        for i in range(1 - size % 2, size + size * size // 2):
    

    Choose whichever of the two suits your needs for the case where size is even.