Shapes as Data
A single dot has carried you a long way. But a game character is a shape, and
you don't want to write eight grid[...] = ... lines every time it moves.
Two ideas fix this, and they are the last two before you build a real game.
1. A shape is a list of lists
Draw the picture in your code and read it as data:
1 means "paint this square", 0 means "leave it". That's a three-row,
four-column sprite you can actually see in the source — which makes it easy to
change.
2. Write a function that stamps it
top and left say where the sprite's corner goes. Everything inside is
worked out from there.
Now drawing the hero anywhere is one line:
You have written functions before, in Milestone 1. This is the first time one has really earned its keep: the alternative is twelve lines every frame, and twelve chances to make an off-by-one mistake.
len(sprite) and len(sprite[r])
len(sprite) is the number of rows. len(sprite[r]) is the number of columns
in row r.
Writing it that way means the same stamp works for a 3×4 hero, a 5×5 coin, or
a 1×1 dot. Hard-code 3 and 4 and you've written a function that only draws
one thing — which is barely a function at all.
Staying inside the picture
stamp will happily try to paint at row 20, and Python will stop you with an
IndexError. Keep the sprite inside the grid, or check before painting:
That guard is worth adding once you start moving sprites around — which is the very next chapter.