For Loops in Python: build a wave from one rule
Every arcade wave you've ever dodged — a row of invaders sliding into view — is a for loop. Spawn one enemy, then let the loop place the rest.
The big idea
A for loop runs the same block once for each value in a sequence, handing your code that value through a loop variable.
See it in code
At its core, a for loop runs its body once for each value in a sequence. range(4) yields 0, 1, 2, 3 — four values, starting at zero:
for i in range(4):
print(i)0 1 2 3
Four values in, four lines out. The loop variable i holds each one as it goes — that's the hook everything else hangs on.
The real power is computing with the loop variable. Here we turn each index into an evenly spaced x-position — the exact formula we're about to place a wave with:
for column in range(6):
x = 55 + column * 74
print("column", column, "-> x", x)column 0 -> x 55 column 1 -> x 129 column 2 -> x 203 column 3 -> x 277 column 4 -> x 351 column 5 -> x 425
55 + column * 74 maps 0…5 to 55, 129, 203 … — six positions, evenly spaced, from one line of math.
Now feed those positions to real game objects. In the game package each Enemy(x, y) draws itself — so one loop builds the whole fleet with that same formula, and a second loop draws it:
from game import Stage, Enemy
screen = Stage.create()
Stage.clear(screen)
wave = []
for column in range(6):
x = 55 + column * 74
wave.append(Enemy(x, 90))
for enemy in wave:
enemy.draw(screen)
Two loops, two jobs: the first builds the fleet with range(); the second draws it by iterating over the list. Bump range(6) to range(10) and the same formula spaces ten enemies just as evenly.
Nothing here is really about enemies. Swap Enemy for anything and the loop is identical — iterate a list of sprites each frame, walk the rows of a dataset, or step through the codons of a DNA strand. range() gives you the count; the body does the work. It's the same skill in the art, game, biotech, and quant courses alike.
Try it yourself
Change range(6) to range(10) and shrink the spacing (* 74 → * 44) so ten fit. Then add a second row: wrap the build in another loop over range(2) and offset y by the row number — that's your first nested loop. Reach for this pattern in any loop activity.
The common mistake
The precise gotcha is an off-by-one from misreading range. range(6) produces 0, 1, 2, 3, 4, 5 — six values, and 6 is not included. Reach for range(1, 6) expecting six enemies and you'll get five. When you want positions 0 to n − 1, range(n) is exactly right.
What it unlocks
Looping is the backbone of everything that repeats: iterate a list of entities every frame, nest loops to build grids, and reach for range() whenever you need precise counts. When you don't need the index, loop directly over the collection like the draw loop above.