Session 9: Boss Battle

The boss is waiting for you. It moves back and forth, shoots bullets and gets more dangerous as its HP drops. You shoot back, keep track of its HP on a bar, and survive three phases. This is the hardest session, and the most satisfying one if you make it through.

What you’ll learn today

  • Drawing an HP bar on the screen
  • A state machine: the boss behaves differently in each phase
  • Making the boss shoot with a bullet timer
  • Activating phases at HP thresholds

Step 0: Setup

  1. Open Thonny.
  2. Download the starter via this link, unpack the ZIP and open main.py in Thonny.
  3. Click Run. You see the boss at the top, your ship at the bottom. No movement, no bullets yet.
  4. Problems? Ask a coach.

Step 1: Making the boss move

The boss moves back and forth across the screen. When it hits the edge, it turns around.

Find # STEP 1 and add:

boss_rect.x += boss_speed

# Turn around at the edge
if boss_rect.right >= WIDTH or boss_rect.left <= 0:
    boss_speed = -boss_speed

boss_speed = 3 is already at the top. Click Run: the boss thunders from left to right!

Moving the player and shooting (find # PLAYER):

keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and player_rect.left > 0:
    player_rect.x -= PLAYER_SPEED
if keys[pygame.K_RIGHT] and player_rect.right < WIDTH:
    player_rect.x += PLAYER_SPEED

# Shoot (find # SHOOT in the event loop):
player_bullets.append(pygame.Rect(player_rect.centerx - 3, player_rect.top - 15, 6, 15))

Step 2: ✅ Basic: hitting the boss and the HP bar

Collision player bullet with boss:

Find # STEP 2:

for b in player_bullets[:]:
    b.y -= 8
    if b.y < 0:
        player_bullets.remove(b)
        continue
    pygame.draw.rect(screen, YELLOW, b)
    if b.colliderect(boss_rect):
        player_bullets.remove(b)
        boss_hp -= 1
        if boss_hp <= 0:
            running = False  # player wins!

Drawing the HP bar (find # HP BAR):

# Background of the bar
pygame.draw.rect(screen, DARK_RED, (10, 10, 300, 20))
# Filled bar based on the current HP
breedte = int(300 * boss_hp / BOSS_MAX_HP)
pygame.draw.rect(screen, RED, (10, 10, breedte, 20))
# Label
hp_surf = font.render(f"Boss HP: {boss_hp}", True, WHITE)
screen.blit(hp_surf, (320, 10))

Click Run. Shoot at the boss: the HP bar shrinks!

Step 3: ⭐ Stretch: the boss shoots back

The boss fires a bullet downwards every N frames.

Find # STEP 3:

boss_shoot_timer += 1
if boss_shoot_timer >= boss_shoot_interval:
    boss_shoot_timer = 0
    boss_bullets.append(pygame.Rect(boss_rect.centerx - 4, boss_rect.bottom, 8, 16))

Updating boss bullets:

for b in boss_bullets[:]:
    b.y += 6
    if b.y > HEIGHT:
        boss_bullets.remove(b)
        continue
    pygame.draw.rect(screen, ORANGE, b)
    if b.colliderect(player_rect):
        boss_bullets.remove(b)
        player_lives -= 1
        if player_lives <= 0:
            running = False

Draw player_lives as hearts or red blocks at the bottom of the screen.

Step 4: 🔥 Expert: three phases

Now the boss reacts to its HP:

  • Phase 1 (HP 100 to 50): normal behaviour
  • Phase 2 (HP 50 to 25): move faster, shoot more often
  • Phase 3 (HP < 25): maximum speed, double bullets

Find # STEP 4:

if boss_hp > 50:
    boss_phase = 1
    boss_speed_abs = 3
    boss_shoot_interval = 60
elif boss_hp > 25:
    boss_phase = 2
    boss_speed_abs = 5
    boss_shoot_interval = 35
else:
    boss_phase = 3
    boss_speed_abs = 7
    boss_shoot_interval = 20

# Adjust the speed without changing direction:
boss_speed = boss_speed_abs * (1 if boss_speed > 0 else -1)

Phase 3: double bullets (add this in the shoot code):

if boss_phase == 3:
    boss_bullets.append(pygame.Rect(boss_rect.left + 10, boss_rect.bottom, 8, 16))
    boss_bullets.append(pygame.Rect(boss_rect.right - 18, boss_rect.bottom, 8, 16))
else:
    boss_bullets.append(pygame.Rect(boss_rect.centerx - 4, boss_rect.bottom, 8, 16))

Win screen at boss_hp <= 0:

screen.fill(BLACK)
win = font.render("BOSS DEFEATED!", True, YELLOW)
screen.blit(win, (WIDTH // 2 - win.get_width() // 2, HEIGHT // 2))
pygame.display.flip()
pygame.time.wait(3000)

Showcase

Show your game to a coach and a buddy. Who reaches phase 3 and survives?

See you next time!

“Next time: your own game, from scratch, completely your own idea.”

Take it home

Try one of these extensions at home:

  1. Easy: change the colour of the boss per phase (green → orange → red).
  2. Medium: add a score: the faster you beat the boss, the more points.
  3. Hard: let the boss shoot diagonal bullets in phase 2 too (slanted left and right).
  4. Very hard: add a second player who can join in with WASD keys.

Stuck? Ask a coach at the next dojo, or just try something else. Programming is doing.

View the cheatsheet for this session