Changing one row of a 2D list changes them all
[[0] * cols] * rows repeats a reference to one inner list. Every row is the same object, so one assignment shows up everywhere.
What it means
Multiplying a list copies references, not values. For numbers that is harmless, because numbers cannot be changed in place, but for an inner list it means grid[0] and grid[1] are one list seen through two names.
Common causes
1. Building a grid with *
The inner list is created once and shared.
Breaks
grid = [[0] * 3] * 3
grid[0][0] = 1 # every row changesWorks
grid = [[0] * 3 for _ in range(3)]2. Repeating a dict or list default
Same trap with any mutable value.
Breaks
rows = [{}] * 5Works
rows = [{} for _ in range(5)]3. Copying a grid with a shallow copy
copy() and [:] copy the outer list only.
Breaks
backup = grid[:]Works
import copy
backup = copy.deepcopy(grid)How to find it in your own code
Build each inner list separately with a comprehension. When a change appears in several places at once, print id() of the parts: matching ids mean shared objects.
Still not sure why yours breaks?
Paste it into the visualizer and watch it run line by line, with every variable at every step. Free, and it runs in your browser.