This page contains exercise answers and teaching guidance for
Step 1 — Variables, Tuples & Constants.
Not linked from the student pages.
Students: please go back and try the activities first.
Snake Game Python Tutorial · Facilitator Notes
ALL_CAPS names signal constants that should not change during the game.BLACK is still a tuple; the name does not make it a colour in Python's eyes.| Mistake | Behaviour | Severity |
|---|---|---|
CELL_SIZE = (30) thinking it is a tuple |
Works but is an int — parentheses alone do not create a tuple |
Low |
Forgets pygame.init() |
RuntimeError or blank window |
Low — immediate error |
WINDOW_W = COLS + CELL_SIZE instead of * |
Window is only 50 pixels wide (adds instead of multiplies) | Obvious visually |
Step 1 has three embedded interactive demos. Preview the student page (step-1.html) before your session so you know what students will encounter. Use the notes below to guide discussion at each demo.
A clickable/hoverable grid that shows pixel coordinates as students move the cursor. Helps make the abstract idea of (col, row) positions concrete before writing code.
Shows the pygame window being "built" step by step — grid lines appear, then the border, then the caption. A replay button lets students watch again. Previews what their program will produce.
Three sliders (R, G, B) that update a live colour swatch and display the Python tuple syntax in real time. Preset buttons load the exact colours used in the Snake game.
Step 1 does not have a Common Mistakes or Quick Check section on the student page — those start from Step 3 onwards. Use the Mistakes table in this guide for real-time diagnosis instead.
The complete Step 1 boilerplate students should produce. All constants are defined at the top — a deliberate structure they will carry into every subsequent step.
import pygame
pygame.init()
# Grid constants
GRID_COLS = 20
GRID_ROWS = 15
CELL_SIZE = 30
# Colour constants (R, G, B) tuples
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 200, 80)
RED = (220, 50, 50)
BG = (30, 30, 30)
# Window dimensions (derived from grid constants — not magic numbers)
WINDOW_W = GRID_COLS * CELL_SIZE # 20 × 30 = 600
WINDOW_H = GRID_ROWS * CELL_SIZE # 15 × 30 = 450
screen = pygame.display.set_mode((WINDOW_W, WINDOW_H))
pygame.display.set_caption("Snake")
WINDOW_W = GRID_COLS * CELL_SIZE?Emphasise that this is decomposition in action: change one constant (CELL_SIZE = 40) and the whole window scales automatically. Students who hardcode 600 will struggle at Step 3 when they need to change the grid size.
Exercise prompt: "Add a SNAKE_SPEED constant and a SNAKE_COLOUR tuple. Run the code — the window should open without errors."
SNAKE_SPEED = 10
SNAKE_COLOUR = (0, 200, 80)
# Run the code — the window should appear and stay open until closed.
ALL_CAPS (convention for constants).SNAKE_COLOUR written as a tuple with parentheses, not a list with brackets.The activity uses a 4-tier progressive structure with calibration and reflection gates. Below are the accepted answers for each tier.
| Blank | Context in code | Accepted answer |
|---|---|---|
| b1 | GRID_COLS = ___ | 20 |
| b2 | GRID_ROWS = ___ | 15 |
| b3 | CELL_SIZE = ___ | 30 |
| b4 | WHITE = ___ | (255, 255, 255) |
| b5 | RED = ___ | (220, 50, 50) |
| b6 | WINDOW_W = ___ * CELL_SIZE | GRID_COLS |
| b7 | WINDOW_H = GRID_ROWS * ___ | CELL_SIZE |
The auto-checker is case-insensitive and ignores whitespace, so grid_cols and GRID_COLS both pass for b6/b7.
| Bug | Faulty line | Root cause | Correct line |
|---|---|---|---|
| Bug 1 | CELL_SIZE = "30" |
Value is a string, not an integer. Multiplying a string by an int repeats characters rather than scaling pixels — causes a TypeError when computing window size. |
CELL_SIZE = 30 |
| Bug 2 | GREEN = [0, 200, 80] |
Square brackets create a mutable list, not an immutable tuple. Colour constants should be tuples. While pygame accepts both, using a list violates the intent and could allow accidental mutation. | GREEN = (0, 200, 80) |
| Bug 3 | WINDOW_W = GRID_COLS + CELL_SIZE |
Addition gives 20 + 30 = 50 pixels wide instead of 20 × 30 = 600. The window will be tiny — students can see this immediately when they run the code. | WINDOW_W = GRID_COLS * CELL_SIZE |
The checker accepts any "why" explanation of 10+ characters and checks the fix text for the key marker (cell_size=30, green=(0,200,80), *). Short explanations get rejected — encourage students to write a full sentence.
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 # 600
WINDOW_H = GRID_ROWS * CELL_SIZE # 450
screen = pygame.display.set_mode((WINDOW_W, WINDOW_H))
pygame.display.set_caption("Snake")
screen.fill(BG)
pygame.display.flip()
pygame.time.wait(3000)
pygame.quit()
The checker requires 16 of 18 checks to pass, so minor variations in constant values (e.g. different SNAKE_SPEED) are tolerated.
Students add GOLD = (255, 200, 0) and a BORDER_COLOUR of their choice, then print the type of every colour constant. Any BORDER_COLOUR tuple is accepted.
# Model answer — BORDER_COLOUR value is the student's own choice
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)
GOLD = (255, 200, 0)
BORDER_COLOUR = (100, 100, 100) # any valid RGB tuple is accepted
WINDOW_W = GRID_COLS * CELL_SIZE
WINDOW_H = GRID_ROWS * CELL_SIZE
print("WHITE:", type(WHITE))
print("GREEN:", type(GREEN))
print("DKGREEN:", type(DKGREEN))
print("RED:", type(RED))
print("BG:", type(BG))
print("GOLD:", type(GOLD))
print("BORDER_COLOUR:", type(BORDER_COLOUR))
screen = pygame.display.set_mode((WINDOW_W, WINDOW_H))
pygame.display.set_caption("Snake")
screen.fill(BG)
pygame.display.flip()
pygame.time.wait(3000)
pygame.quit()
Expected terminal output (every line should show <class 'tuple'>):
WHITE: <class 'tuple'>
GREEN: <class 'tuple'>
DKGREEN: <class 'tuple'>
RED: <class 'tuple'>
BG: <class 'tuple'>
GOLD: <class 'tuple'>
BORDER_COLOUR: <class 'tuple'>
If a student's output shows <class 'list'> for any line, they used square brackets — redirect to Bug 2 in Tier 2 as a reference.
The journal is open-ended and graded on length (15+ words per prompt). These are reference responses — not the only valid answers.
| Prompt | Key ideas a strong answer contains |
|---|---|
| 1. Most confusing part of Step 1? | Common answers: why WINDOW_W = GRID_COLS * CELL_SIZE rather than a hard number; the difference between tuple and list syntax. A good answer names the concept and explains what resolved the confusion. |
| 2. Why UPPER_CASE for constants? | It is a Python convention (not enforced by the language). It signals to every reader: "do not change this value." Good answer mentions PEP 8 or convention vs. rule. |
| 3. Tuple vs list for colours? | Tuples are immutable — a colour like GREEN = (0, 200, 80) should never change mid-game. Using a tuple communicates that intent and prevents accidental mutation. Lists can be changed; tuples cannot. |
| 4. Change CELL_SIZE 30 → 20? | Because WINDOW_W = GRID_COLS * CELL_SIZE, the window would shrink from 600×450 to 400×300. All derived values update automatically — this is the power of decomposition / named constants. |
| 5. Explain Step 1 to a classmate? | Should cover: variable = named box in memory; tuple = a fixed group of numbers in (); constant = a variable you agree not to change; pygame.display.set_mode() opens the game window using the calculated pixel dimensions. |
| What they did | What they see | What to say |
|---|---|---|
Named colour tuples in lowercase (green = (0,200,80)) |
Code runs but Python convention is broken — future collaborators will not recognise it as a constant | "Why do professional codebases write colours in caps? What's the convention signal?" |
WINDOW_W = GRID_COLS + CELL_SIZE |
Tiny 50-pixel window (20 + 30 instead of 20 × 30) | "What does + mean versus *? Draw the grid on paper and count the pixels across." |
Used a list [255, 0, 0] instead of tuple (255, 0, 0) |
Works — pygame accepts both — but intent is wrong | "Colours do not change during the game. Use a tuple (immutable) to show that in code." |
Wrote CELL_SIZE = (30) thinking it creates a tuple |
Works as an int, not a tuple. print(type(CELL_SIZE)) shows <class 'int'> |
"Parentheses alone don't make a tuple. A single-element tuple needs a trailing comma: (30,) — but for a plain number we just write 30." |
Forgets pygame.init() |
Immediate RuntimeError or blank/frozen window |
"Think of pygame.init() as starting the engine before driving the car. What happens if you skip it?" |
A variable is a labelled box. A tuple is a box that holds exactly N things and refuses to be opened and changed. A constant is a box with tape over the lid — Python won't stop you, but the name tells every developer "do not change this."
Python does not enforce immutability on constants — it is a human convention. Some students will test this by reassigning BLACK = (255,0,0) and seeing it "work." That is expected. The lesson is that the convention communicates intent, not a language rule.
Even an empty dark window is exciting for beginners. Celebrate it. Students who see their window appear are motivated to keep going. Students who hit an error at this point need extra support — isolate their code before Step 2.
Steps 2–9 all build on this file. If a student has the wrong CELL_SIZE or WINDOW_W, every subsequent step will produce unexpected results. Do a quick scan of the room before moving on — students can display their window on screen for a visual "pass/fail" check.
"If we want to change the grid from 20×15 to 30×20, which lines need to change? Why is it only 2?" — This surfaces decomposition: because the window size is derived from the constants, not hardcoded.
Step 1 should take 15–20 minutes for most students. If a student finishes in under 10 minutes, ask them to: (1) try the RGB mixer and find the colour of their hair in Python tuple form, and (2) add a FPS = 10 constant and explain where it will be used later.
These 5 questions appear in the activity page after Tier 4 (post-calibration gate). Pass mark is 4 of 5 (80%). Students who fail may retry; the system records attempts and final score in Google Sheets.
| Question (displayed to student) | Correct Answer |
|---|---|
| Q1: Which of these is a string (text) value in Python? | ✓ "snake" |
| Q2: What does WIN_WIDTH = 600 create? | ✓ A named constant storing 600 |
| Q3: What will this print? x = 3 / x = x + 1 / print(x) | ✓ 4 |
| Q4: Which line correctly imports the pygame library? | ✓ import pygame |
| Q5: What does pygame.init() do? | ✓ Initialises all pygame subsystems (sound, display, etc.) |
Recorded in Google Sheet (Act_1 tab):
concept_q1–concept_q5 (student’s 0-based answer index),
concept_score_pct, concept_passed (1 = pass, 0 = fail),
concept_attempts (retry count).
Every submission to this step writes one row to the Act_1 tab in the research spreadsheet. All 13 tabs (Student_Reg, Pre_Test, Post_Test, Act_1–Act_10) share the same student identity columns.
| Column | Description |
|---|---|
| STUDENT IDENTITY (10 fields) | |
matric | Matric / student ID |
name | Full name |
gender | Gender (Female / Male / Other) |
age | Age in years |
mykid | MyKid / IC number |
home_state | Home state in Malaysia |
class | Class or cohort code |
school_code | School or programme code |
phone | Phone number |
email | Email address |
| SUBMISSION | |
step | Step number (1) |
submitted_iso | KL timestamp (UTC+8, ISO 8601) |
| PRE-CALIBRATION | |
cal_confidence | Self-confidence before activity (1–5 scale) |
cal_predicted | Predicted score before activity (%) |
cal_reflection | Free-text: what will be hard? |
| ACTIVITY TIERS | |
t1_score_pct | Tier 1 Fill-in-Blanks score (%) |
t2_attempts | Tier 2 Debug — number of attempts |
refl2_text | Tier 2 reflection free text |
t3_attempts | Tier 3 Complete-Code attempts |
t4_attempts | Tier 4 New Task attempts |
| CONCEPT CHECK | |
concept_q1–concept_q5 | Student answer index (0-based) per question |
concept_score_pct | Percentage correct (0–100) |
concept_passed | 1 = passed (≥80%), 0 = failed |
concept_attempts | Total retries |
| REFLECTIVE JOURNAL | |
jr1–jr5 | Journal prompts 1–5 free-text responses |
| POST-CALIBRATION | |
post_confidence | Confidence rating after activity (1–5) |
post_actual | Self-reported actual score (%) |
post_r1 | Reflection: how accurate was the prediction? |
post_r2 | Reflection: what would you do differently? |
calibration_index | post_actual − cal_predicted (negative = overconfident) |