35. Score and text — Homework solutions
Problem 1 — Score display
Problem. A number on screen that the Up/Down arrows change.
How to think about it. One integer variable, two
KEYDOWN checks. Re-render the text surface each frame.
Worked solution.
import pygame
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Score Display")
clock = pygame.time.Clock()
font = pygame.font.SysFont("arial", 48)
value = 0
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
value += 1
if event.key == pygame.K_DOWN:
value = max(0, value - 1)
screen.fill((20, 20, 40))
surf = font.render(str(value), True, (255, 255, 255))
screen.blit(surf, (10, 10))
pygame.display.flip()
clock.tick(60)
pygame.quit()max(0, value - 1) prevents the value from going below 0
without needing a separate if check.
Common mistakes.
- Using
get_pressedfor the arrow keys here.get_pressedfires every frame while held, so the number would race upward rapidly.KEYDOWNfires once per press, giving per-press control.
Problem 2 — Countdown timer
Problem. Count down from 10 to 0 and show
"Time's up!" at the end.
How to think about it. Add 1/60 to an
accumulator each frame. The displayed integer is
10 - int(accumulator), floored at 0. When the accumulator
reaches 10 the countdown is done.
Worked solution.
import pygame
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Countdown")
clock = pygame.time.Clock()
font = pygame.font.SysFont("arial", 64)
small = pygame.font.SysFont("arial", 36)
elapsed = 0.0
DURATION = 10.0
done = False
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if not done:
elapsed += 1 / 60
if elapsed >= DURATION:
elapsed = DURATION
done = True
remaining = int(DURATION - elapsed)
screen.fill((20, 20, 40))
if done:
surf = small.render("Time's up!", True, (255, 100, 100))
screen.blit(surf, ((800 - surf.get_width()) // 2, 260))
else:
surf = font.render(str(remaining), True, (255, 255, 255))
screen.blit(surf, ((800 - surf.get_width()) // 2, 230))
pygame.display.flip()
clock.tick(60)
pygame.quit()clock.tick(60) returns the number of milliseconds since
the last call — a more accurate alternative for timing is
clock.tick(60) / 1000.0 as the delta time. For this
exercise, assuming exactly 60 fps with 1/60 is
sufficient.
Common mistakes.
- Computing
remainingasDURATION - elapsedas a float and displaying it. The timer shows fractional seconds, which looks unpolished.int(...)truncates to whole seconds.
Problem 3 — Collect and score
Problem. Arrow-key player, repositioning target on touch, score displayed at top.
How to think about it. Combine the collect pattern
from chapter 34 with font.render /
screen.blit.
Worked solution.
import pygame
import random
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Collect")
clock = pygame.time.Clock()
font = pygame.font.SysFont("arial", 28)
W, H = 800, 600
player = pygame.Rect(370, 270, 50, 50)
target = pygame.Rect(random.randint(0, W-40), random.randint(0, H-40), 40, 40)
score = 0
speed = 5
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
player.x -= speed
if keys[pygame.K_RIGHT]:
player.x += speed
if keys[pygame.K_UP]:
player.y -= speed
if keys[pygame.K_DOWN]:
player.y += speed
player.clamp_ip(pygame.Rect(0, 0, W, H))
if player.colliderect(target):
score += 1
target.x = random.randint(0, W - target.width)
target.y = random.randint(0, H - target.height)
screen.fill((20, 20, 40))
pygame.draw.circle(screen, (255, 220, 0), target.center, target.width // 2)
pygame.draw.rect(screen, (100, 200, 255), player)
score_surf = font.render(f"Score: {score}", True, (255, 255, 255))
screen.blit(score_surf, (10, 10))
pygame.display.flip()
clock.tick(60)
pygame.quit()Challenge — Three lives
Problem. Collect coins (score), avoid red squares (lives). Display both. Game over at 0 lives.
How to think about it. Keep a list of red squares. Each frame, move each square a fixed amount toward the player (or in a fixed direction). On collision with the player, decrement lives and reposition the square. On collision with a coin, increment score and reposition the coin.
Worked solution.
import pygame
import random
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Three Lives")
clock = pygame.time.Clock()
font = pygame.font.SysFont("arial", 28)
large = pygame.font.SysFont("arial", 64)
W, H = 800, 600
player = pygame.Rect(370, 270, 50, 50)
coin = pygame.Rect(random.randint(0, W-30), random.randint(0, H-30), 30, 30)
enemies = [pygame.Rect(random.randint(0, W-40), random.randint(0, H-40), 40, 40)
for _ in range(3)]
score = 0
lives = 3
speed = 4
enemy_speed = 2
game_over = False
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
if not game_over:
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
player.x -= speed
if keys[pygame.K_RIGHT]:
player.x += speed
if keys[pygame.K_UP]:
player.y -= speed
if keys[pygame.K_DOWN]:
player.y += speed
player.clamp_ip(pygame.Rect(0, 0, W, H))
for enemy in enemies:
if enemy.centerx < player.centerx:
enemy.x += enemy_speed
elif enemy.centerx > player.centerx:
enemy.x -= enemy_speed
if enemy.centery < player.centery:
enemy.y += enemy_speed
elif enemy.centery > player.centery:
enemy.y -= enemy_speed
if player.colliderect(enemy):
lives -= 1
enemy.x = random.randint(0, W - enemy.width)
enemy.y = random.randint(0, H - enemy.height)
if lives <= 0:
game_over = True
break
if not game_over and player.colliderect(coin):
score += 1
coin.x = random.randint(0, W - coin.width)
coin.y = random.randint(0, H - coin.height)
screen.fill((20, 20, 40))
pygame.draw.circle(screen, (255, 220, 0), coin.center, coin.width // 2)
for enemy in enemies:
pygame.draw.rect(screen, (220, 50, 50), enemy)
pygame.draw.rect(screen, (100, 200, 255), player)
screen.blit(font.render(f"Score: {score}", True, (255, 255, 255)), (10, 10))
screen.blit(font.render(f"Lives: {lives}", True, (255, 200, 200)), (10, 44))
if game_over:
surf = large.render("Game Over", True, (255, 80, 80))
screen.blit(surf, ((W - surf.get_width()) // 2, (H - surf.get_height()) // 2))
pygame.display.flip()
clock.tick(60)
pygame.quit()The enemies move one step toward the player each frame. When an enemy
collides with the player, it respawns at a random position. The
break after detecting a lethal hit exits the enemy loop
early so only one life is lost per frame even if multiple enemies
overlap the player.
Common mistakes.
- Modifying a list while iterating over it. The solution here only
modifies list items' positions, not the list structure itself, so
iterating with
for enemy in enemiesis safe.
Done?
Part 7 ends with two mini-projects — Coin Collector and Collect-All-Coins — that combine the game loop, drawing, movement, and score into complete, playable programs.