Skip to main content

Stage 7: Add Lives and Game Over

Course progressStage 7 of 10
~40 min
One game, one Trinket

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.

Build

lives, a penalty for missed aliens, and a game-over screen

Learn

how stakes turn a toy into a game

Ship

a game that can actually be lost

The big idea

Until this stage, you could not lose. Missing a laser was free. Letting an alien drift past the cannon was free. The game was, technically, a toy — a thing you played with rather than a thing you played. Today we add stakes.

The new tool is a piece of game state called a Boolean. A Boolean is a value that is either True or False — nothing else. We use one to ask a yes/no question on every frame: "is the game still running?" When the answer becomes False, the loop exits.

Stages 3–6: while True:
run forever (nothing can stop us)

Stage 7: game_running = True
while game_running:
run only while game_running is True

When lives reach 0:
game_running = False
→ loop exits → GAME OVER shows

This is the first time the game has two modes: playing and ended. The loop only runs in playing mode. The screen we show after the loop exits is its own thing — a tiny second program that prints the result.

We also tighten the alien cleanup. In Stage 5 we removed aliens when they passed BOTTOM — anywhere off the bottom of the screen counted. Today we move the line up to FLOOR_LEVEL — the cannon's row. An alien that reaches FLOOR_LEVEL has reached the player. That's the moment a life comes off.

Before you start

Your game should score points when lasers hit aliens.

Build it

Step 1 — Add the lives counter and the on/off switch

lives is just a number that ticks down. game_running is the Boolean that controls the loop. We also create a second writer Turtle so lives can live in the top-right corner, opposite the score.

Add these near score:

lives = 3
game_running = True

Then add a second writer before the game loop:

status_writer = turtle.Turtle()
status_writer.hideturtle()
status_writer.penup()
status_writer.color("white")
status_writer.setposition(RIGHT - 140, TOP - 40)

RIGHT - 140 slides the writer in from the right edge by a fixed amount — enough room for the word "Lives:" plus a number.

Step 2 — Draw the lives count

Same pattern as draw_score() from Stage 6 — clear, then write. Reusing the pattern means a future reader sees one shape for all on-screen text.

Add this function:

def draw_status():
status_writer.clear()
status_writer.write(f"Lives: {lives}", font=("Arial", 16, "bold"))

Call it before the game loop:

draw_status()

Step 3 — Take a life when an alien reaches the cannon

This is the change that makes misses hurt. Find the alien cleanup block inside your game loop. It currently checks if alien.ycor() < BOTTOM:. Change it to FLOOR_LEVEL, and add the life-loss logic.

Replace the alien cleanup block with:

if alien.ycor() < FLOOR_LEVEL:
alien.hideturtle()
aliens.remove(alien)
lives -= 1
draw_status()

if lives <= 0:
game_running = False

Reading top-to-bottom: the alien crossed the player's line, so we hide it, drop it from the list, subtract a life, redraw the lives text — and if lives have hit zero, we flip the switch that ends the game.

Note the threshold: < FLOOR_LEVEL, not < BOTTOM. We want the player to lose lives at the cannon's row, not when aliens leave the screen entirely.

Step 4 — Let the loop know how to stop

Right now your game loop says while True:. That's "run forever." Change it to:

while game_running:

That's "run while game_running is True." The moment Step 3's code sets game_running = False, this line evaluates to False and the loop ends.

After the loop, add the game-over screen. We reuse the score_writer from Stage 6 — it already exists, so we just move it and have it draw one more thing.

score_writer.setposition(0, 0)
score_writer.write("GAME OVER", align="center", font=("Arial", 28, "bold"))
screen.update()
turtle.done()

Run the game. Try to lose on purpose — stand still and let aliens come down. Lives should tick down, and when they hit zero you should see GAME OVER in the middle of the screen.

Understand it

The Boolean switch is small but huge. Until now, your game has been a thing the computer runs until you close the window. Today the game runs until it decides to stop on its own. That's a real grown-up loop pattern. Most apps you use every day — the camera, the music player, the browser tab — work this way: they have a state variable, the main loop checks it every cycle, and it can end gracefully when needed.

We deliberately wrote if lives <= 0: and not if lives == 0:. Using <= is defensive — if some future change to the game subtracted two lives at once (say, a powerful alien), == 0 would skip right past zero into -1 and never trigger game-over. <= 0 says "if we ever get to zero or below, end it." Code that survives changes you haven't made yet is good code.

Game-over is the first place we let one writer Turtle have two jobs: drawing the score during the game and announcing the result after. We could have made a third writer just for "GAME OVER," but reusing what we already have is cheap and clean. The Turtle moves, prints, and that's it — its work is done.

Try this

Learning beat

Try this

Three short experiments. Predict before you run, then test your guess.

Predict first

Set lives = 1. Predict before you run: how does the whole feel of the game change with one-hit-and-done? Now play it. Is it more intense, or just frustrating? Is there a fair version of one-life mode?

Compare

Change if lives <= 0: to if lives == 0: and run. Now imagine a future stretch where a single alien costs two lives. Walk through what happens with that change. Why does <= survive that future change and == not?

Connect

Stage 8 adds a win state — survive long enough and you win. The loop needs to end for winning the same way it currently ends for losing. Look at your while game_running: line. What does Stage 8 need to add?

Test your stage

  • Aliens that reach FLOOR_LEVEL are removed and take a life.
  • The lives text in the top-right corner updates every time.
  • When lives reach zero, the game stops.
  • GAME OVER appears centered on the screen.
  • Aliens that the player shoots down don't cost a life (only aliens that get through the cannon line should).
  • Design check. Play three full rounds. Did losing feel earned, or did the game cheat you? If it cheated, what would you change — alien speed, spawn rate, threshold, life count?

If it breaks

  • Python says lives is referenced before assignment. The life-changing code probably ended up inside a function. Either move it back into the main loop (where this stage puts it) or add global lives and global game_running as the first lines of the function.
  • The game never ends — even at zero lives. Two suspects. First, check that the loop now says while game_running: and not while True:. Second, make sure the if lives <= 0: block is inside the alien cleanup block, where it can actually run.
  • GAME OVER appears behind the score text. The score_writer is being moved to (0, 0) but the old text from the score is still sitting in its old position. Add score_writer.clear() before the new position, or call draw_score() once more before the game-over screen.
  • Lives count never appears. The draw_status() call before the game loop is missing, or the writer position is off the screen. Run with print(lives) after each change to confirm the value is moving.
  • The game ends the moment it starts. Check that lives = 3 (not 0) and game_running = True (not False). Defaults matter.