SeeThinkExploreMarvel

UMPSA STEM LAB · Step 3 of 10

Step 3 — Direction & Movement

Map arrow keys to directions, guard against impossible reversals, and move the snake head one cell per frame — your first visible game action.

if / elifConditionals Boolean ANDStringCoordinates
Step 3 / 10

What you'll build in Step 3

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.

🎯 Learning goals

Understand if/elif chains, the Boolean AND operator, how string values store state, and how (col, row) coordinates change with direction.

🐍 Snake game progress

The direction variable and the hx, hy head position introduced here are used unchanged in every later step.

CT skills this step

📋 Algorithm Design 🔍 Evaluation / Logic 🔄 Pattern Recognition 💡 Abstraction

Try It — Control the Snake Head

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.

Loading…
D-pad
🔴 red flash = reverse blocked
by the direction guard

New Variables: direction, hx, hy

Three 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, 7

Two 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.

How direction maps to coordinate changes

directionMeaninghx changehy changeWhy?
"RIGHT"Move →+10Column increases leftward
"LEFT"Move ←−10Column decreases
"UP"Move ↑0−1y=0 is the TOP — up = row decreases
"DOWN"Move ↓0+1y increases downward
Remember: y is flipped! In Pygame (like most screens), the top-left is (0, 0) and y increases downward. Moving "UP" on screen means subtracting from hy. This trips up almost every beginner — don't worry if it feels backwards at first.

See the coordinate change — click to move

Click the canvas to step through a movement sequence.

if / elif — Conditional Chains

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.

📋 Algorithm Design — decision making

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.

Decision tree — click an arrow key to trace it

if K_UP and direction != "DOWN"→direction = "UP"
elif K_DOWN and direction != "UP"→direction = "DOWN"
elif K_LEFT and direction != "RIGHT"→direction = "LEFT"
elif K_RIGHT and direction != "LEFT"→direction = "RIGHT"

Boolean Logic — and & !=

Each condition in the if/elif uses two operators you need to understand: and and !=.

🔍 Evaluation / Logic

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.

The operators

!=not equal direction != "DOWN"→ True when direction is "RIGHT", "LEFT" or "UP"
False only when direction is exactly "DOWN"
andlogical AND A and B→ True only when BOTH A and B are True

Truth table — K_UP pressed and direction != "DOWN"

K_UP pressed?direction != "DOWN"?ResultWhat happens
✅ True✅ True (direction="RIGHT")Truedirection = "UP" ← allowed
✅ True❌ False (direction="DOWN")FalseBlocked — reverse guard fires 🔴
❌ False✅ TrueFalseK_UP not pressed — skip
❌ False❌ FalseFalseSkip

The Reverse Guard — why it matters

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.

✅

Allowed turn

Moving RIGHT, press UP
"RIGHT" != "DOWN" → True
direction changes to "UP"

🔴

Blocked reverse

Moving DOWN, press UP
"DOWN" != "DOWN" → False
direction stays "DOWN"

💀

Without the guard

Head moves into its own neck — instant collision — game over on first press!

🔄 Pattern Recognition — input validation

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.

Drawing the Head — pygame.draw.rect()

Each frame the head is drawn as a rounded rectangle using the pixel-coordinate formula from Step 1.

💡 Abstraction — draw.rect hides pixel math

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.

🎲 pygame.draw.rect() — Live Playground

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.


    

The Code — Step 3

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)

Try It — Exercises

Predict the result before you run each change.

1

Remove one guard 🔍 Logic

Delete and direction != "DOWN" from the UP check. Play the game — press UP while moving DOWN. What happens? Does the snake die immediately? Why?

2

WASD alternative controls 🔄 Pattern

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?

3

Change start direction 💡 Abstraction

Change direction = "RIGHT" to direction = "DOWN". Where does the head start moving? Does this break anything else in the code?

4

Print the coordinates 📋 Algorithm

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?

5

Edge-of-screen bug 📋 Algorithm

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.

⚠️ Common Mistakes — Spot These Before You Start

These are the errors beginners make most often in Step 3. Read them now so you can recognise them in your own code.

✗ Mistake 1: Pressing the opposite direction reverses into yourself
if event.key == pygame.K_LEFT:
    direction = (-1, 0)   # fine if going right... but:
🐞 What happens: If the snake is heading RIGHT (1,0) and you press LEFT (-1,0), the head moves into the second segment. Instant game over.
if event.key == pygame.K_LEFT and direction != (1, 0):
    direction = (-1, 0)   # guard: ignore if already going right
✗ Mistake 2: Using if/if instead of if/elif
if event.key == pygame.K_UP:    direction = (0, -1)
if event.key == pygame.K_DOWN:  direction = (0, 1)   # runs even if UP matched!
🐞 What happens: If somehow both fire in one frame, the last 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)
⚡ Mistake 3: Forgetting the snake moves every frame, not just on keypress
# Student thinks direction only applies when a key is held down
🐞 The direction variable is read every frame. Once set, it stays until changed. The snake keeps moving in that direction even with no key pressed — that's intentional!
# direction persists between frames — it remembers the last pressed key
# (In Steps 3-9 you update it only when a new key is pressed)

✅ Quick Check — Are You Ready?

Three questions — not graded. They help you spot gaps before the activities.

1 Why must we prevent pressing the opposite direction?
2 Why use elif for direction keys instead of separate if statements?
3 What happens to direction between key presses?

You've covered all the concepts for Step 3. Time to apply them.

Start Activities → Tier 1 → 2 → 3 → 4
Previous← Loop & Events