Stage 4: Spawn Aliens
Keep building in your saved Python Arcade project.
This stage is part of the same game you started in Setup. Do the work in your own remixed Trinket — it’s the workspace on the right of this page.
enemy turtles that appear at random positions
how to reuse a pattern you already know with a brand-new trigger
aliens spawning near the top of the screen
The big idea
Last stage you built the most reusable pattern in the whole course: a list of game objects + a game loop that walks the list. Today we use it again. Aliens are temporary game objects, just like lasers — they appear, move, and get cleaned up.
What changes is the trigger. Lasers spawn because a player presses a key. Aliens have no one pressing a key for them. They need to spawn on their own, at a steady rhythm, in different places, so the player can't predict where the next threat is coming from.
Stage 3 — Lasers: Stage 4 — Aliens:
player presses space ─→ random chance per frame
│ │
▼ ▼
fire_laser() spawn_alien()
│ │
▼ ▼
lasers.append(...) aliens.append(...)
The trigger is the only thing that changes. The list, the append, the cleanup — all reused.
The new tool today is randomness. Random means unpredictable on purpose. Python's random library gives us two things we need: pick a random number in a range (for the alien's X position) and "flip a weighted coin" (to decide whether to spawn this frame at all).
A tiny chance, checked many times per second, becomes a steady stream. If we spawn with a 3% chance each frame and the game runs at 30 frames per second, we get about one alien per second on average — sometimes two close together, sometimes a quiet beat. That rhythm is what makes the game feel alive.
Your cannon should move, and pressing space should fire lasers.
Build it
Step 1 — Bring in Python's random library
The random library is part of Python — we don't install it, we just import it so this file can use it.
At the top of the file, with your other imports, add:
import random
Step 2 — Decide the alien rules
Just like the laser cap, we make the alien numbers easy to tune. ALIEN_SPAWN_CHANCE is the heartbeat. ALIEN_START_Y is the line they spawn on.
Add these near your other constants:
ALIEN_SPAWN_CHANCE = 0.03
ALIEN_START_Y = TOP - 40
aliens = []
aliens = [] is the empty list, exactly like lasers = [] from Stage 3. The shape of the pattern is already familiar.
Step 3 — Write the spawn function
This function builds one alien — a new Turtle, colored and shaped, dropped at a random X position near the top — and puts it in the list. From that moment on, the game owns the alien through the list.
Add this above the game loop:
def spawn_alien():
alien = turtle.Turtle()
alien.penup()
alien.color("lime")
alien.shape("circle")
alien.turtlesize(1.2)
x = random.randint(int(LEFT + 40), int(RIGHT - 40))
alien.setposition(x, ALIEN_START_Y)
aliens.append(alien)
The random.randint(a, b) call returns a whole number from a up through b. We wrap LEFT + 40 and RIGHT - 40 in int(...) because those values are fractions (Python divided the window width by 2), and randint only deals in whole numbers.
The 40 margins on each side are an invisible-walls move, same idea as Stage 2: we don't want aliens spawning on the screen edge where the cannon can't reach them.
Step 4 — Decide whether to spawn each frame
Now we wire the spawn into the game loop. Every frame, we flip a coin — and if the coin says "spawn," we do.
At the top of your while True: loop, add:
if random.random() < ALIEN_SPAWN_CHANCE:
spawn_alien()
random.random() (no arguments) returns a fresh number from 0 to just-under-1 every time you call it. If that number lands below 0.03, we spawn. That's our 3% chance, checked every frame.
Run the game. Aliens should drop in near the top, spaced apart, in unpredictable spots.
Understand it
The whole stage is a lesson in pattern reuse. Look at your code side-by-side: aliens is to lasers what spawn_alien is to fire_laser. The trigger changed, the appearance changed, the position rules changed — but the shape of the code is identical. Recognizing this is a real coding superpower. Next time you need an extra system in a game, ask: "is this a list of things that appear and disappear?" If yes, you already know the bones.
Probability-per-frame is worth pausing on. We could have written code that spawns an alien exactly every 30 frames — a perfect metronome. Instead we used randomness. The reason is feel: a perfect metronome teaches the player the pattern, and once they know it the game is solved. A small random chance is slightly different every game, which keeps the pressure honest. Designed unpredictability is what separates a game from a worksheet.
We deliberately set ALIEN_SPAWN_CHANCE = 0.03 and not 0.3. The difference is huge: a 3% chance per frame gives about one alien per second; a 30% chance fills the screen in two seconds. Game tuning happens in small numbers.
Try this
Try this
Three short experiments. Predict before you run, then test your guess.
Change ALIEN_SPAWN_CHANCE = 0.03 to ALIEN_SPAWN_CHANCE = 0.5. Before you run, predict what will happen. Now run it. Was it what you imagined, or worse? What does that tell you about probability times framerate?
Comment out spawn_alien inside the game loop and instead bind it to a key like you did with fire_laser (screen.onkeypress(spawn_alien, "a")). Now you spawn an alien every time you press A. Which version feels more like an arcade — random rhythm or one-per-keypress? Why?
Stage 5 makes the aliens move. Look at your Stage 3 laser game-loop code — specifically the for laser in lasers[:]: block. What needs to change for aliens? What stays exactly the same?
Test your stage
- Aliens appear without anyone pressing a key.
- Aliens appear at different X positions, not always the same column.
- The cannon and lasers still work.
- The screen doesn't instantly flood with enemies.
- Design check. Watch the game for thirty seconds without playing. Does the alien rhythm feel like the game is breathing, or like the metronome is broken?
If it breaks
- No aliens appear. Temporarily set
ALIEN_SPAWN_CHANCE = 0.2to confirm the spawn function works at all. If aliens appear at 0.2 but not at 0.03, the chance is just low — be patient or pick a slightly higher value. - Python says
randomis not defined. Theimport randomline is missing or sitting below code that uses it. Imports go at the top of the file. - Aliens appear outside the screen. Look at
random.randint(int(LEFT + 40), int(RIGHT - 40)). The margins (+ 40and- 40) keep them inside. If you removed theint(...)wrappers, Python will complain thatrandintneeds whole numbers. - Aliens spawn but the rest of the game freezes. Check that
spawn_alien()is not in an infinite loop by mistake (like inside its ownwhile True:). It should be one line: build alien, append, done.