UMPSA STEM LAB · Step 3 of 10
Map arrow keys to directions, guard against impossible reversals, and move the snake head one cell per frame — your first visible game action.
For the first time the player can control something on screen. A single dark-green square — the snake head — moves across the grid, changing direction when arrow keys are pressed.
Understand if/elif chains, the Boolean AND operator, how string values store state, and how (col, row) coordinates change with direction.
The direction variable and the hx, hy head position introduced here are used unchanged in every later step.
Use the D-pad buttons (or arrow keys on your keyboard while focused here) to move the head around the grid. Watch how hx, hy and direction change — and notice what happens when you try to reverse direction.
direction, hx, hyThree variables carry the state of the moving head from one frame to the next.
direction = "RIGHT"A string storing one of four values: "RIGHT", "LEFT", "UP", "DOWN". It starts as "RIGHT" — the snake moves right at game start. Changed by the arrow-key event handler.
hx, hy = 10, 7Two integers assigned in one line (tuple unpacking). hx = head column, hy = head row. Starting at (10, 7) puts the head in the centre of the 20×15 grid.
| direction | Meaning | hx change | hy change | Why? |
|---|---|---|---|---|
"RIGHT" | Move → | +1 | 0 | Column increases leftward |
"LEFT" | Move ← | −1 | 0 | Column decreases |
"UP" | Move ↑ | 0 | −1 | y=0 is the TOP — up = row decreases |
"DOWN" | Move ↓ | 0 | +1 | y increases downward |
hy. This trips up almost every beginner — don't worry if it feels backwards at first.An if/elif chain checks conditions one by one from top to bottom. The first branch that is True runs; the rest are skipped entirely.
Conditionals are how algorithms make decisions. The if/elif chain here is a decision tree: given an arrow key, which direction should the snake go? This same logic structure appears in navigation apps, spam filters, and game AI.
and & !=Each condition in the if/elif uses two operators you need to understand: and and !=.
Boolean logic is how computers make choices. The and operator creates a compound condition — BOTH parts must be True for the whole expression to be True. This is the same logic used in database queries, search filters, and access controls.
K_UP pressed and direction != "DOWN"| K_UP pressed? | direction != "DOWN"? | Result | What happens |
|---|---|---|---|
| ✅ True | ✅ True (direction="RIGHT") | True | direction = "UP" ← allowed |
| ✅ True | ❌ False (direction="DOWN") | False | Blocked — reverse guard fires 🔴 |
| ❌ False | ✅ True | False | K_UP not pressed — skip |
| ❌ False | ❌ False | False | Skip |
Without the guard, pressing the opposite arrow key would make the snake instantly collide with itself — killing the game on the first keypress. The guard is a small piece of logic that prevents a physically impossible move.
Moving RIGHT, press UP"RIGHT" != "DOWN" → True
direction changes to "UP"
Moving DOWN, press UP"DOWN" != "DOWN" → False
direction stays "DOWN"
Head moves into its own neck — instant collision — game over on first press!
The reverse guard is a form of input validation — checking that user input makes sense before acting on it. You'll see this pattern everywhere: form fields that reject empty inputs, ATMs that reject negative withdrawals, login systems that check passwords before granting access.
pygame.draw.rect()Each frame the head is drawn as a rounded rectangle using the pixel-coordinate formula from Step 1.
pygame.draw.rect() abstracts away all the pixel-by-pixel drawing. You just say "draw a rectangle at this position with this colour" and Pygame handles thousands of pixel operations for you.
# Step 3-4: Convert grid (col, row) to pixel (x, y) and draw
pygame.draw.rect(
screen, # which surface to draw on
DKGREEN, # colour tuple from Step 1
pygame.Rect(
hx * CELL_SIZE, # pixel x = col × 30
hy * CELL_SIZE, # pixel y = row × 30
CELL_SIZE - 2, # width: 28px (2px gap makes grid visible)
CELL_SIZE - 2 # height: 28px
),
border_radius=4 # rounded corners — cosmetic touch
)
The -2 on width and height leaves a 1-pixel gap between cells — this makes the grid look neat and helps the player see individual segments.
Drag the sliders to change the rectangle's position, size, and corners. Watch the canvas and the code update live — this is exactly what happens 30× per second when your snake moves.
Run this. Use arrow keys to steer the head. Press Escape to quit. (The head will vanish off-screen — Step 6 fixes that with wrap-around.)
# Step 3-1: Starting position (centre of 20×15 grid) and direction
direction = "RIGHT"
hx, hy = 10, 7 # head column, head row
# ── inside the for-event loop ────────────────────────────────────
# Step 3-2: Arrow keys → change direction (with reverse guard)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP and direction != "DOWN":
direction = "UP"
elif event.key == pygame.K_DOWN and direction != "UP":
direction = "DOWN"
elif event.key == pygame.K_LEFT and direction != "RIGHT":
direction = "LEFT"
elif event.key == pygame.K_RIGHT and direction != "LEFT":
direction = "RIGHT"
# ── after event loop, before draw ────────────────────────────────
# Step 3-3: Move head one grid cell in the current direction
if direction == "RIGHT": hx += 1
elif direction == "LEFT": hx -= 1
elif direction == "UP": hy -= 1
elif direction == "DOWN": hy += 1
# Step 3-4: Draw head as a dark-green rounded rectangle
pygame.draw.rect(screen, DKGREEN,
pygame.Rect(hx * CELL_SIZE, hy * CELL_SIZE,
CELL_SIZE - 2, CELL_SIZE - 2),
border_radius=4)
import pygame
pygame.init()
GRID_COLS = 20
GRID_ROWS = 15
CELL_SIZE = 30
SNAKE_SPEED = 8
WHITE = (255, 255, 255)
GREEN = ( 0, 200, 80)
DKGREEN = ( 0, 160, 50)
RED = (220, 50, 50)
BG = ( 30, 30, 30)
WINDOW_W = GRID_COLS * CELL_SIZE
WINDOW_H = GRID_ROWS * CELL_SIZE
screen = pygame.display.set_mode((WINDOW_W, WINDOW_H))
pygame.display.set_caption("Snake")
clock = pygame.time.Clock()
running = True
direction = "RIGHT" # Step 3-1
hx, hy = 10, 7 # Step 3-1: start in centre
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
# Step 3-2: arrow keys change direction (with guard)
if event.key == pygame.K_UP and direction != "DOWN": direction = "UP"
elif event.key == pygame.K_DOWN and direction != "UP": direction = "DOWN"
elif event.key == pygame.K_LEFT and direction != "RIGHT": direction = "LEFT"
elif event.key == pygame.K_RIGHT and direction != "LEFT": direction = "RIGHT"
# Step 3-3: move head one cell
if direction == "RIGHT": hx += 1
elif direction == "LEFT": hx -= 1
elif direction == "UP": hy -= 1
elif direction == "DOWN": hy += 1
# Step 3-4: draw
screen.fill(BG)
pygame.draw.rect(screen, DKGREEN,
pygame.Rect(hx * CELL_SIZE, hy * CELL_SIZE,
CELL_SIZE - 2, CELL_SIZE - 2),
border_radius=4)
pygame.display.flip()
clock.tick(SNAKE_SPEED)
pygame.quit()
direction = "RIGHT"A string variable holding the current movement direction. Strings are immutable text in quotes. We use ALL-CAPS strings ("RIGHT" not "right") as a self-documenting convention — it's clear these are fixed labels, not user-generated text.
hx, hy = 10, 7Python lets you assign multiple variables in one line using tuple unpacking. Equivalent to hx = 10; hy = 7. Position (10, 7) is roughly the centre of the 20×15 grid — head starts with room to move in any direction.
Python evaluates each condition top-to-bottom. The first True branch runs and the rest are skipped — this is why we use elif (not if) for the second, third and fourth cases. Using all ifs would allow multiple branches to run.
and direction != "DOWN"The compound condition short-circuits: if the key is NOT K_UP (first part is False), Python doesn't even check the second part. This is called short-circuit evaluation — efficient and safe.
hx += 1, hy -= 1, etc.+= is shorthand for hx = hx + 1. Each frame this runs once, moving exactly one cell. Because the game loop runs 8 times/second, the snake moves 8 cells/second across the grid.
pygame.Rect(hx*CELL_SIZE, hy*CELL_SIZE, ...)A Rect object defines a rectangle by (x, y, width, height). We multiply grid coords by CELL_SIZE to get pixel coords. CELL_SIZE-2 leaves a thin gap between cells, making the grid pattern visible.
Predict the result before you run each change.
Delete and direction != "DOWN" from the UP check. Play the game — press UP while moving DOWN. What happens? Does the snake die immediately? Why?
Add W, A, S, D as alternative direction keys (pygame.K_w, pygame.K_a, etc.). Notice the pattern — the elif chain looks almost identical to the arrow-key block. Could you combine them?
Change direction = "RIGHT" to direction = "DOWN". Where does the head start moving? Does this break anything else in the code?
Add print(f"hx={hx} hy={hy} direction={direction}") after the movement lines. Run the game and move the head. Can you predict what the numbers will be before they print?
Let the head move off the right edge (past column 19). What happens on screen? Note this down — Step 6 fixes it with the modulo operator.
These are the errors beginners make most often in Step 3. Read them now so you can recognise them in your own code.
if event.key == pygame.K_LEFT:
direction = (-1, 0) # fine if going right... but:
if event.key == pygame.K_LEFT and direction != (1, 0):
direction = (-1, 0) # guard: ignore if already going right
if/if instead of if/elifif event.key == pygame.K_UP: direction = (0, -1) if event.key == pygame.K_DOWN: direction = (0, 1) # runs even if UP matched!
if wins. Using elif short-circuits: only the first true branch runs.if event.key == pygame.K_UP: direction = (0, -1) elif event.key == pygame.K_DOWN: direction = (0, 1) elif event.key == pygame.K_LEFT: direction = (-1, 0) elif event.key == pygame.K_RIGHT: direction = (1, 0)
# Student thinks direction only applies when a key is held down
# direction persists between frames — it remembers the last pressed key # (In Steps 3-9 you update it only when a new key is pressed)
Three questions — not graded. They help you spot gaps before the activities.
elif for direction keys instead of separate if statements?direction between key presses?You've covered all the concepts for Step 3. Time to apply them.
Start Activities → Tier 1 → 2 → 3 → 4