Stage 3 of 6 · Theory, pen ready · about 30 minutes
Pseudocode
Requirements say what; pseudocode sketches how. It is logic written in structured plain language: exact about the order of steps, the decisions and the loops, and deliberately silent about syntax. It is where you find your design's flaws while they still cost pencil lead.
What pseudocode is
Pseudocode is a description of an algorithm precise enough to translate into any programming language and informal enough to write at a whiteboard. It has no official standard, and that is by design: textbooks, exams and teams each fix their own conventions, then apply them consistently. What every convention shares is the structured part: the same three shapes that structured programming showed are sufficient for any algorithm, sequence (one step after another), selection (IF/ELSE), and repetition (loops). That result, one of computer science's foundational ideas, is why the small construct table below covers everything you will ever design; the Wikipedia article on structured programming tells the history, dramatic 1968 letter included.
Two things pseudocode is not:
- Not vague code. "Deal with the button somehow" is not pseudocode; it postpones the only decision that matters. Pseudocode is exact about logic: which condition, checked when, doing what.
- Not Python with the colons removed. If your pseudocode says
button.wait_for_press(), you are writing code and calling it design. Pseudocode says wait until the button is pressed and lets the translation choose the call.
This course's conventions
Used in the hubs, expected on the midterm, recommended for your project documents. Keywords in capitals, indentation for blocks (the habit Python already taught you), one step per line:
| Keyword | Meaning |
|---|---|
SET x TO value | Assignment |
IF condition THEN ... ELSE ... END IF | Selection; ELSE IF chains allowed |
WHILE condition ... END WHILE | Condition-controlled loop; LOOP FOREVER for the deliberate infinite loop |
FOR each item IN collection ... END FOR | Count- or collection-controlled loop |
FUNCTION name(parameters) ... RETURN value | A named recipe |
INPUT ... / OUTPUT ... | Reading from and reporting to the outside world |
WAIT ... / WAIT UNTIL ... | Time passing or an event arriving (this course's addition, for hardware) |
# comment | A note to the reader, ignored by the "machine" |
Other sources use ENDIF, REPEAT ... UNTIL, arrows for assignment, or French keywords; all are fine. The only rule with teeth: pick a convention and hold it, because inconsistent pseudocode cannot be traced.
The construct map
Every keyword above lands on a Week 4 construct, which is why the translation step at the end is nearly mechanical, and why Python was advertised as reading like pseudocode:
| Pseudocode | Python |
|---|---|
SET count TO 0 | count = 0 |
IF temperature > 30 THEN ... ELSE ... | if temperature > 30: ... else: ... |
WHILE count < 5 / LOOP FOREVER | while count < 5: / while True: |
FOR each n IN 0 to 4 | for n in range(5): |
FUNCTION flash(duration) ... RETURN | def flash(duration): ... return |
OUTPUT "blink", n | print(f"blink {n}") |
WAIT 0.5 seconds | sleep(0.5) |
WAIT UNTIL button is pressed | button.wait_for_press() |
SET t0 TO current time | t0 = monotonic() |
Stepwise refinement
Nobody writes twenty exact lines from a blank page. Designs descend through levels, each one checkable before the next:
The coarse level for the timer, checked against the spec: ready → random wait → light → measure press → report → repeat. And immediately the spec earns its keep: R4 is nowhere in those steps. Where do false starts fit? Inside "random wait", which is not a plain wait at all but a watchful one. That discovery, made in five lines of coarse steps, is the design catching a bug that improvised code would have met as a rewrite.
The reaction timer, designed
The detailed level, satisfying R1 to R4 and R6. Read it against the spec table, requirement by requirement:
LOOP FOREVER # R6: rounds continue until Ctrl+C
turn LED off # R1
OUTPUT "Get ready..." # R1
SET delay TO random number between 2.0 and 5.0 # R2
SET waited TO 0
SET false_start TO false
WHILE waited < delay # the watchful wait
IF button is pressed THEN
SET false_start TO true
EXIT WHILE # leave the wait early
END IF
WAIT 0.01 seconds
SET waited TO waited + 0.01
END WHILE
IF false_start THEN # R4
OUTPUT "FALSE START"
WAIT UNTIL button is released
continue to next round
END IF
turn LED on # R2 completed
SET t0 TO current time
WAIT UNTIL button is pressed # R3
SET reaction TO (current time - t0) in ms
turn LED off
OUTPUT "Reaction:", reaction, "ms" # R3
WAIT UNTIL button is released
END LOOP
Notice the design decisions the pseudocode makes visible, exactly the ones worth debating with a partner before coding: the wait polls every 0.01 s (a design answer to R5's accuracy); a flag variable (false_start) carries a fact out of the loop to the decision after it, a pattern you will reuse constantly; and both exits wait for the button's release, without which one long press would bleed into the next round, an edge case R6's "new round" wording forces you to confront on paper.
Trace tables: run it on paper
A trace table (also called a desk check or dry run) executes a design by hand: one row per step actually taken, one column per variable, plus condition results and output. It is the standard exam technique for a reason: it separates what the text says from what you meant, and the bug always lives in that gap. Trace the false-start path, pretending the button is pressed at 0.02 s into a 3.0 s delay:
| Step | Line | delay | waited | false_start | Condition? | Output |
|---|---|---|---|---|---|---|
| 1 | turn LED off · OUTPUT | – | – | – | – | Get ready... |
| 2 | SET delay / waited / false_start | 3.0 | 0 | false | – | – |
| 3 | WHILE waited < delay | 3.0 | 0 | false | 0 < 3.0 → true, enter | – |
| 4 | IF button is pressed | 3.0 | 0 | false | false → skip | – |
| 5 | WAIT · SET waited | 3.0 | 0.01 | false | – | – |
| 6 | loop again · IF button is pressed | 3.0 | 0.01 | false | true (pressed at 0.02 s) | – |
| 7 | SET false_start · EXIT WHILE | 3.0 | 0.01 | true | – | – |
| 8 | IF false_start | 3.0 | 0.01 | true | true → enter | FALSE START |
| 9 | WAIT UNTIL released · next round | – | – | – | – | Get ready... |
Nine rows, and R4 is proven, on paper, before any Python exists: no time was reported, the message appeared, a new round began. Now trace the version without the flag, where the false-start OUTPUT sits inside the WHILE: the table shows the loop resuming after the press, the LED still lighting, the round not restarting. The trace table catches in two minutes what the bench would have turned into twenty of confused rewiring. On the midterm, expect to trace given pseudocode exactly like this; practice material is on the Resources page.
Pseudocode smells
| Smell | Example | The fix |
|---|---|---|
| Hand-waving | "handle the button correctly" | Name the condition and the action: which press, detected when, doing what |
| Secret Python | Button(27, bounce_time=0.05) | Back up one level: "wait until button is pressed (debounced)"; the call belongs in the translation |
| Level mixing | "initialize hardware" next to "SET waited TO waited + 0.01" | Refine evenly; a coarse line among detailed ones hides exactly the logic you needed to check |
| Untraceable loop | WHILE with a condition no line ever changes | Every loop needs a line that moves it toward exit (or an explicit EXIT); the trace table exposes this in three rows |
| Orphan logic | A branch no requirement asked for | Either a requirement is missing (add it) or the branch is (delete it); spec and design must trace to each other |
Checklist for this stage
Check yourself
Why is pseudocode kept free of real syntax like wait_for_press()?
What are the three structured-programming shapes, and why do they matter?
What job does the false_start flag do that nothing else in the design does?
In a trace table, what exactly goes in each row?
Your WHILE loop traces forever. Which smell is this and what is the repair?
SET waited TO waited + 0.01, or an explicit exit on an event.