Years ago, the two of us — college roommates with matching Computer Science degrees — found ourselves cross-legged in the children's section of a Seattle bookstore, tea in hand, in front of titles like Rosie Revere, Engineer. And we got to wondering. The ideas in coding had always felt simple, even beautiful — it was the typing that got in the way. What if a kid could meet the ideas first, in a story, before a computer ever entered the room?
Since then, computers have learned to write their own code — a kid can ask for a game and have it running before their tea goes cold. You'd think that would make a book like this pointless. We think it's the opposite: the typing was never the part we loved anyway. The ideas were, and the ideas are the part that's left. So this is not a book that teaches kids to type code. It's a book that shows them how code thinks — with, we hope, a little fun along the way.
— Robin & Sonya
It's a story and a puzzle book — Sonny and Ro run an ice cream parlour and build machines to help. Five things to know, then we're off:
Machine instructions. Writing these is coding — that's the whole secret. The little () means "do this action!"
Under the Lid. Lift the flap whenever you want to see how an idea really works.
Puzzles. Type your answer first, then peek at ours — the book remembers what you write. (Ours isn't the only right one.)
The crew. Sonny designs. Ro engineers. Jerry tries his best.
The meter up top. Flip pages with the ‹ › arrows, keys, or a swipe — and earn a scoop for every chapter you finish.
You don't need to read fast. You need to read thinky. Ready? Let's open the shop.
Meet Sonny. Sonny is a designer!
She knows how things should look, and feel.
Sonny daydreams of sliding down clouds of pink ice cream with cherries on top. Sonny works hard and comes up with a plan!
Meet Ro. Ro is an engineer!
She puts the "pro" in problem-solving.
Ro daydreams of the biggest, baddest slide that ice cream can make. How would it work? How many swirls and twirls would it have?
One hot summer day, Ro gets a letter from Sonny.
"Today marks May 1st and, soon, it will be summer. I've saved up my pocket change and started an ice cream parlour!
It has walls with red stripes and a counter of our favorite flavors: chocolate, cotton-candy, raspberry-ripple, and more. There are a few things I'll need help with, so let's join forces — you're all that I'm looking for!"
"Yippeeee!" screamed Ro, as she danced all the way home. "I'll show up bright and early tomorrow, and see what needs to get done."
The next morning, the two friends stood behind the counter, aprons on. By noon, Sonny's arm ached from scooping, Ro had counted forty customers — forty-one if you count the pigeon — and the line was out the door.
"This is wonderful," puffed Sonny, "and also a disaster. We can't do every single thing by hand all day. I want to spend my time dreaming up flavors and talking to guests — not doing the same chore a hundred times."
Ro's eyes lit up. "Then let's not do them by hand. Let's build machines to do the boring, repeating parts. I'll build the machines — but a machine only knows what we tell it. So we'll write each one a set of instructions."
"Instructions for a machine," said Sonny slowly. "Like a recipe it follows exactly."
"Exactly like that," grinned Ro. "And here's the secret: writing those instructions is coding. Come on — I'll show you. Bring me your problems, one at a time, and we'll solve each one with a machine."
Before Ro builds anything, she pulls out her toolbox. "Every machine needs two simple things," she says. "A way to remember things, and a way to compare things."
How does a shop remember things? It writes them down! A variable is a little chalkboard on the machine's wall. The board's name is painted on the frame, and its value is chalked on the slate — one board, one value.
number_of_flavors = 12
Read the = sign as: erase the board, chalk on the new value. That's called assignment. Whatever the board said before? Gone.
When a new flavor arrives in the fall, we update the board:
number_of_flavors = number_of_flavors + 1
The machine reads what the board says right now (12), adds one, and chalks the answer back on: 13.
A chalkboard can hold anything you can write. To start, machines write three kinds of things: a number, a word, or an answer:
number_of_flavors = 12 ← a number
flavor_of_the_day = "vanilla" ← a word
customer_is_hungry = true ← an answer (true or false)
Spot the quotation marks around "vanilla"? They matter — a lot:
Quotes mean the thing itself. "vanilla" is the actual word vanilla, chalked out letter by letter.
No quotes means the name of a board. flavor_of_the_day is painted on a frame — to find out what it means, you have to walk over and read that board.
So flavor_of_the_day = "vanilla" just means: wipe that board, chalk the word "vanilla" on it. Quotes = the thing. No quotes = the name of a board.
And the third board holds an answer: true while the customer's hungry, false once they're full. Boards like that one are about to run every machine in the shop.
(One more board hangs by the freezer: days_since_a_mess = 3. Keep half an eye on that one.)
Drag a value chip onto any board. The machine wipes the slate, chalks your value on — and shows you the exact instruction you just wrote. The name never moves. The value is yours to change.
drag a value chip onto any board ↑ — the name stays put; the value gets re-chalked.
An operator is a little tool for working with values. The machine uses these constantly, and they come in two kinds.
Math tools make a new value:
| Operator | Means | Example | What you get |
|---|---|---|---|
+ | add | 2 + 3 | 5 |
- | take away | 5 - 1 | 4 |
Question tools ask about two values — and the answer is always true or false. Let's say our boards currently say flavor = "chocolate" and scoops = 5. Then:
| Operator | Asks | Example | The answer |
|---|---|---|---|
== | is it the same as...? | flavor == "vanilla" | false — the board says "chocolate" |
!= | is it different from...? | flavor != "vanilla" | true — chocolate isn't vanilla |
> | is it more than...? | scoops > 3 | true — 5 is more than 3 |
< | is it less than...? | scoops < 3 | false — 5 isn't less than 3 |
>= | is it at least...? | scoops >= 5 | true — "at least" means this many or more |
Watch the machine work out flavor == "vanilla": it sees flavor has no quotes, so that's a board's name — it walks over, reads the board, and finds "chocolate". Then it compares that to "vanilla" — quotes, so that's the actual word. Chocolate and vanilla aren't the same word → the answer is false.
And a true-or-false answer is a value like any other, so it fits on a board — that's exactly what boards like customer_is_hungry are holding.
Pick a board to peek at, a question tool, and a value to test against — the machine stamps the answer. It is always true or false.
tap chips to build a question ↑
= chalks. == asks.= → erase the board, write the value. One board, one value.== → "are these two the same?" The answer is always true or false.Those true/false answers steer every machine in this book.
Why we start here: a loop is the whole reason machines are magic. They never get bored of repeating.
"Ro," said Sonny, rubbing her sore arm the next morning, "the guests are thrilled — but I've been scooping all day and my arm is done. Every single customer, I do the exact same thing: scoop, hand it over, scoop, hand it over. Over and over and over."
"Over and over and over," repeated Ro, with a slow smile. "That, my friend, is a job for a loop. Machines love doing the same thing over and over. Let me build the Super Scooper 4000."
It took a few tries, but Ro finally got it. Here are the first instructions she gave the machine:
while customer_is_hungry:
scoop_ice_cream()
"Look at the shape of it," said Ro. "customer_is_hungry is one of our true-or-false boards. When a customer walks up and orders, the machine asks once — hungry? — and chalks true on it. The : at the end of the line means 'here's what to do about it' — and the step tucked underneath, nudged to the right, is the step that belongs to the while.
"So here's what the machine does: it peeks at the board. If it says true, it does the step — scoop! Then it walks right back and peeks again. Still true? Scoop again. Around and around, for as long as the board says true. The moment a peek finds false, it stops. Simple!"
The next day, it was time to test it. A regular named Scott came in with his pup. "I'd like a peanut-butter-cup, please," he said. "And vanilla for my pup."
"You got it," said Sonny, and pressed start.
One scoop, two scoops, three scoops... four...?
"Scott, are you still hungry?" asked Sonny, a little nervous.
"Sure am! Can I start eating?" said Scott.
"Not yet — the machine isn't done... Ro?! When does it stop?!"
It didn't take long for things to go wrong. Soon Scott's cone had twenty scoops, and the Super Scooper 4000 was still going!
"ICE CREAM SCOOPS CATAPULTING — TURN IT OFFFFFFF!!"
"We've made an infinite loop," panted Ro, wiping cream off her glasses. "Look at our instructions again:
while customer_is_hungry:
scoop_ice_cream()
The machine keeps scooping while the board customer_is_hungry says true. But the Scooper grips the cone tight in its claw until the job is done — so poor Scott can't take a single bite until the machine stops, and he stays hungry the whole time! Every single peek at the board finds true, so the machine scoops again. And nothing in our instructions ever re-chalks that board to false. So the machine never, ever stops."
"That's the trap," she went on. "A while loop peeks, does its step, peeks again — around and around. If nothing can change what's on the board, the loop runs forever."
"So we need something inside the loop that can change the answer," said Sonny.
"Exactly!" said Ro. "Let's add a step that re-chalks the board every time around:"
while customer_wants_another_scoop:
scoop_ice_cream()
customer_wants_another_scoop = ask("Another scoop?")
"When a guest orders, true goes on the board — of course they want their first scoop! Then each time around, the machine scoops, asks, and chalks the guest's fresh answer right onto the board. Their words change what's on the board — and what's on the board decides whether the machine goes around again."
Sonny pressed start. One scoop in:
"Another scoop, Scott?"
"No way — twenty was plenty yesterday. One's perfect today!"
The machine stopped. Sonny cheered.
"Want to see what just happened inside?" grinned Ro, and sketched it on her notepad:
| Peek | What the board says | What the machine does |
|---|---|---|
| 1st peek | true (Scott ordered!) | scoop! → "Another scoop?" → "No way!" → false gets chalked on the board |
| 2nd peek | false | the loop stops. Enjoy, Scott! |
Then Ro walked to the freezer wall and, without a word, wiped a board clean and chalked: days_since_a_mess = 0.
Not a big deal, by the way — building machines and software is all about build it, test it, watch it break, and fix it. Breaking things is part of the job.
The machine only knows its instructions: while the customer wants another scoop, keep scooping. Your two buttons are the ask() — watch them re-chalk the board on the wall. And watch what happens if nobody ever says no...
while customer_wants_another_scoop: true → keep scooping! scoops served: 0
A condition is any check that comes out true or false — a peek at a board, or a question tool. The machine only cares which answer comes out.
while — when you don't know how many times.
while tub_has_ice_cream:
scoop_ice_cream()Peek → true? Do the step, peek again. false? Stop. This one stops on its own: every scoop empties the tub a little, until the answer flips.
⚠ If nothing can change the answer, the loop never stops. That's an infinite loop — you've met one.
for — when you do know how many times.
for scoop in range(3):
scoop_ice_cream()range(3) means "do this 3 times." The counter board scoop says 0, then 1, then 2 — machines start counting at 0. (That trick pays off in Arrays.)
And an if? Same peek — but only once. We'll meet it next.
"That's better," said Sonny. "But for Scott's pup, I always know it's exactly one scoop of vanilla. And Jenny down the street? She gets exactly five every time." (Jenny is seven. Jenny dreams big.)
"Then use a for loop and skip the questions," said Ro:
for scoop in range(5):
scoop_ice_cream()
"Aha," said Sonny. "That's the stuff."
Puzzle 1. How would you tell the machine to scoop three scoops for a customer who already knows exactly what they want?
for scoop in range(3):
scoop_ice_cream()
Puzzle 2a (the other direction). Read the code and say it in plain English:
while wants_more_sprinkles:
add_sprinkles()
While the guest wants more sprinkles, keep adding sprinkles.
Puzzle 2b. And this one?
while customer_in_line:
take_order()
While there's still a customer in line, take their order.
Puzzle 2c. One more:
for guest in range(10):
give_free_scoop(guest)
For each of ten guests, give that guest a free scoop.
Puzzle 3 (now you write it!). In plain English: "Keep waving hello while the door is open." Can you turn that into instructions? (Hint: do you know how many times? If not, which loop?)
Our answer is waiting in the back of the book.
The Super Scooper was a hit. "Our guests love trying every flavor," said Sonny. "But I want to offer free samples — let them taste, and only sell them a scoop if they actually like it. Sometimes they like it, sometimes they don't. The machine needs to decide."
"Decisions!" said Ro. "That's exactly what an if statement is for — one instruction that picks a path. Watch:"
if likes_sample:
serve_ice_cream()
else:
give_next_sample()
"In plain words," said Ro, "if the guest likes the sample, serve them a scoop. Otherwise (else), hand them the next sample to try."
"likes_sample is another true-or-false board," she added. "The guest tastes, their verdict gets chalked on it, and the machine peeks: true → the top path, false → the else path. It's the same peek a while does — but an if peeks once, picks a path, and moves on. No going around again."
One sundae per order. Chalk the board with a button, and the machine peeks once: true → one burst of sprinkles; false → served plain. Then it's done — an if never pours forever. Send a few orders!
if wants_sprinkles: chalk the board — the next sundae is waiting!
if likes_sample: ← condition
serve_ice_cream() ← body
else is the optional "otherwise" path. No else? When the condition is false, the machine simply moves on.
One habit: ask positive questions — likes_sample, not sample_rejected. Brains and machines both read them better.
Puzzle 1. A guest asks, "Could I get sprinkles on top?" Write the instruction that adds sprinkles when they want them. Do you need an else?
if wants_sprinkles:
add_sprinkles()
No else needed here — if they don't want sprinkles, there's simply nothing to do!
Puzzle 2a. Translate into plain English:
if ordered_banana_split:
make_banana_split()
else:
serve_ice_cream()
If they ordered a banana split, make one. Otherwise, serve a regular scoop.
Puzzle 2b. A trickier one — ifs inside ifs!
if wants_toppings:
if wants_sprinkles:
add_sprinkles()
if wants_fruit:
add_strawberries()
add_bananas()
If they want toppings: add sprinkles if they want sprinkles; and if they want fruit, add strawberries and bananas.
"That machine is awesome, Ro! My arm feels brand new," said Sonny. "Business is booming — so I hired Jerry to run the cash register. One problem: he's new, and he keeps giving people the wrong change."
"Then let's write Jerry a clear, repeatable job," said Ro. "I'll bundle all the change-making steps into one function so he (and the register) can do it the same way every time."
Steps on the card, a name across the top. Say the name — the whole job happens. Ten times a day, if you like.
scoop_ice_cream():
remove_lid(ice_cream_tub)
scooper = get_scooper()
cone = get_cone()
scoop_one_scoop(scooper, cone)
:, and the steps underneath.scoop_ice_cream(). (That's why it's been written that way all book!)(). remove_lid(ice_cream_tub) needs to know which tub.get_scooper() ends with a scooper in your hand — caught on a board: scooper = get_scooper(). (And ask("Another scoop?")? Its answer is true or false — straight onto the board.)A function is a named bundle of steps. Press the card’s name and watch every step run, in order. Press it again — same job, every time.
press the card’s name ↑ — one press runs the whole job, every step, every time.
"Okay Jerry," said Ro, "here's your function for the register. Its input is how many scoops the guest ordered, and its answer is the correct change."
operate_cash_register(num_scoops):
total_cost = price_of(num_scoops)
money_given = take_payment(total_cost)
change = calculate_change(money_given, total_cost)
put_in_drawer(money_given)
return change
"Step by step," said Ro:
1. Figure out the total_cost for that many scoops.
2. take_payment from the guest and remember how much they handed over.
3. Work out the change.
4. Put their money in the drawer.
5. Hand back (return) the change.
"And how does calculate_change actually work?" asked Sonny.
"Subtract what they owe from what they paid," said Ro:
calculate_change(money_given, total_cost):
change = money_given - total_cost
if change >= 0:
return change
else:
ask_for_more_money()
"If money_given covers the cost, change is zero or more, and we hand it back — handing back 0 is perfectly fine, it just means exact change! If they gave too little, change would be negative — there's no answer to hand back yet — so we politely ask for more money first."
"And when the change is exactly zero," said Jerry, squinting at the card, "I hand them... nothing?"
"You hand them their ice cream and a smile," said Ro. "Smiles are free."
With the Scooper and Jerry's register humming, business boomed. "We're selling out so fast our flavors run dry before closing!" said Sonny. "Can we build something to refill any tub that runs low?"
"Meet the Refiller 1.0," said Ro, lifting a red velvet cover off a small brass machine. "It pulls fresh tubs from the fridge, tops up the one in the case, and wheels the big tub back before it melts."
"Why 1.0?" asked Sonny.
"Because it hasn't broken yet," said Ro. "Give it time."
"But we have lots of flavors," said Sonny. "How does it know to check them all?"
"Because I keep all the flavor tubs together in an array — a row I can march straight down. Here are its instructions:"
refill_flavors():
for tub in ice_cream_tubs:
while has_room(tub):
add_to_tub(tub)
"For each tub in the row, while that tub still has room, add a scoop. Then move on to the next tub. (has_room(tub) is a question-function: hand it a tub, and its answer is true or false — and every scoop we add fills the tub a little more, so the answer flips to false on its own. No infinite loops in this shop!)"
"Brilliant! Can it refill toppings too?"
"We don't even need a new machine — just point the same idea at the toppings row," said Ro.
Puzzle. Write refill_toppings() so it refills every tub in topping_tubs instead.
refill_toppings():
for tub in topping_tubs:
while has_room(tub):
add_to_tub(tub)
It's a data structure — fancy words for "a tidy place to keep your stuff." (A sock drawer. A fridge. A pencil case.)
The special part is the order: every spot has a number — an index — and machines count from 0. First book: books_array[0]. Fourth: books_array[3].
The machine finds things by index, and it counts from 0. Tap a number — and yes, try 5.
shelf[2] → "CHOC" — the third tub! (Machines count from 0.)
"Refilling runs like clockwork," said Sonny, eyeing the four flavors behind the counter. "So smoothly that we're moving to a bigger shop on Polk Street — gumdrop lights, tall ceilings, room for twelve more flavors!"
"Twelve more!" said Ro. "We'd better have a tidy way to describe a flavor, then."
"Funny you say that," said Sonny. "I design every flavor the same way — same list of properties each time. It's like a blueprint. Here it is:"
class Ice_Cream:
name
color
water # percent
cream # percent
sugar # percent
fluff # low / medium / high
add_ins
"The blueprint doesn't say which flavor — it just lists what every flavor needs. Then I fill it in, once per flavor. Here's vanilla:"
vanilla = Ice_Cream(
name = "vanilla",
color = "beige",
water = 60,
cream = 30,
sugar = 10,
fluff = "medium",
add_ins = "rainbow sprinkles",
)
(See the little # notes? That's a comment — a reminder for humans, written right next to the instructions. The machine politely ignores everything after a #. It's how programmers leave notes for each other, and for themselves later!)
class Student:
name
age
student_id
ro = Student(name = "Ro", age = 7, student_id = 1234)The dot reaches in: ro.age → 7. (Keep that dot; it's back next chapter.)
And a board can hold a whole built thing: ro = Student(...) chalks one name that means the entire student, properties and all.
Why bother? Same reason as functions: talk about the whole thing without listing every nut and bolt.
Fill each blank — the machine builds your ice cream and writes the exact instruction underneath. One blueprint, endless flavors.
tap options to fill the blueprint ↑
Puzzle 1a. Write a class (just the property names!) for a dog 🐶. What describes a dog?
class Dog:
name
age
num_legs
favorite_toy
Puzzle 1b. Now a cake 🎂:
class Cake:
cake_flavor
icing_flavor
num_layers
Puzzle 1c. And a book 📖 (like this one!):
class Book:
title
author
num_pages
genre
Puzzle 2. Invent a brand-new ice cream flavor by filling in the blueprint. Go wild!
my_flavor = Ice_Cream(
name = "____________",
color = "____________",
water = ____, # percent
cream = ____, # percent
sugar = ____, # percent
fluff = "____________", # low / medium / high
add_ins = "____________",
)
(Spot which blanks have quotes waiting for you? Words wear quotes; numbers don't!)
raspberry_ripple = Ice_Cream(
name = "raspberry ripple", color = "red",
water = 30, cream = 60, sugar = 10,
fluff = "high", add_ins = "cherries",
)
(The blueprint works for terrible ideas too — we once filled it in as garlic_swirl. It did not sell.)
The new shop had a big walk-in fridge, and the most-used ingredient by far was milk. Sonny and Ro lined the milk cartons up in a row.
"I keep buying milk, but the fridge smells sour — some of it's spoiling before we use it!" said Sonny. "We buy one carton a day and use one a day. How is any going bad?"
"Show me how you store it," said Ro.
"Easy — when I buy a new carton, I put it right at the front, where I can reach it. Then I grab from the front to make ice cream."
Ro nodded slowly. "Ah. You're using a stack when you want a queue."
"Here's the problem," said Ro. "You always put the newest carton at the front, and you always grab from the front. So you only ever use the newest milk — and the older cartons get shoved to the back and never used. They sit there until they spoil! That's a stack: the last thing in is the first thing out."
"Stacks are perfect for some things," she added. "Like clean plates — you set each washed plate on top and grab from the top. Newest plate, used first. No problem there. Though nobody has seen the bottom plate since May."
"But for milk," said Sonny, catching on, "I want to use the oldest carton first, before it goes bad!"
"Exactly. So flip it around. Put each new carton at the back, and keep grabbing from the front. Now the oldest carton is always the one you use next. That's a queue: the first thing in is the first thing out — just like a line of people."
Sonny rearranged the fridge that afternoon. No more sour smell. 🥛✨
(Out front, even the pigeon has started waiting in line. Everyone is very proud of him.)
Same two moves — put one in, take one out — but the plates hand back the newest one first, and the milk line serves the oldest first.
A stack's three moves:
| Move | What it does |
|---|---|
push("abc") | put a value on top |
top() | peek at the top value (without removing it) |
pop() | remove the top value |
Push "a", "b", "c" — then peek and pop. (print(top()): top() hands back the top value, and that answer becomes print's input.)
| Line of code | Stack afterward (top first) | Printed so far |
|---|---|---|
push("a") | a | |
push("b") | b, a | |
push("c") | c, b, a | |
print(top()) | c, b, a | c |
pop() | b, a | c |
print(top()) | b, a | c b |
pop() | a | c b |
print(top()) | a | c b a |
Out comes c b a — backwards! Last in, first out.
A queue's three moves — same idea, but you peek at the front():
| Move | What it does |
|---|---|
push("strawberry") | add a value at the back |
front() | peek at the front value |
pop() | remove the front value |
The very same "a", "b", "c":
| Line of code | Queue afterward (front first) | Printed so far |
|---|---|---|
push("a") | a | |
push("b") | a, b | |
push("c") | a, b, c | |
print(front()) | a, b, c | a |
pop() | b, c | a |
print(front()) | b, c | a b |
pop() | c | a b |
print(front()) | c | a b c |
Out comes a b c — the same order in and out. That's the whole idea.
The same three moves, on both at once. Push a few letters, peek & print, pop — and watch the two printed lines come out different.
same moves on both — push a few letters, then peek & pop.
Puzzle 1. First, s = Stack() builds a brand-new empty stack and gives it the name s — Stack is a blueprint, just like Ice_Cream! And there's last chapter's dot: s.push("Sonny") means tell s to push "Sonny". So... what gets printed? (Hint: draw the stack after each line!)
s = Stack()
s.push("Sonny")
print(s.top())
s.pop()
s.push("Ro")
s.push("and")
print(s.top())
s.pop()
print(s.top())
Sonny … and … Ro
Puzzle 2. And this queue?
q = Queue()
q.push("Rocky")
q.push("Road")
q.push("Ice Cream")
for n in range(3):
print(q.front())
q.pop()
Rocky … Road … Ice Cream
Challenge: Using either a stack or a queue, can you write code that prints "I scream for ice cream"? There are many right answers — compare with a friend! (One of ours is in the back of the book.)
A few days later, the milk queue was working well — until Sonny crossed Polk Street to the corner store and grabbed a discounted carton that, she realized too late, expired today. She put it at the back of the queue like always... and it spoiled before its turn ever came.
"The queue isn't quite enough," reasoned Ro. "We don't really want first-in-first-out. We want soonest-to-expire, first out — no matter when we bought it."
"So I should slot each carton in by its expiration date," said Sonny, "with the soonest at the front!"
Here's the difference:
Plain queue (by when you bought it):
FRONT (grab here!) BACK (new cartons join here)
[ Jun 12 ] [ Jun 13 ] [ Jun 14 ] [ Jun 9 ← just bought, on sale! ]
Priority queue (by expiration date):
FRONT (grab here!) BACK
[ Jun 9 ] [ Jun 12 ] [ Jun 13 ] [ Jun 14 ]
In the plain queue, that on-sale carton sits at the back and spoils. In the priority queue, it jumps to the front because it expires first — so it gets used in time. "Foolproof," said Sonny.
A priority queue in your hands: new cartons slot in by expiry date — and "use milk" always takes the front. Watch a soon-to-expire carton jump the whole line.
buy a carton — it slots in by expiry date, not by when you bought it.
Stacks and queues only care when things went in. A priority queue sends the highest-priority thing out first — always, no matter when it arrived.
You already do this. Thirty minutes, messy room, five chores:
You do them in priority order — not list order. That's a priority queue.
This one's a little fancy — but you already do it without thinking. Watch.
Business was so good that Sonny spent a whole night writing twenty-six new recipe cards, one for each letter, sorted alphabetically in a neat stack: Almond, Banana, Coconut... all the way to Zucchini-bread (don't knock it till you try it).
At the counter, a woman with a red ponytail said, "I'd like Peppermint chocolate, please!"
Sonny started flipping from the top. A... B... C... D... "This'll take forever," she groaned. "And I'm adding a hundred cards a week — soon that's thousands to flip through!"
"You don't have to look at them one by one," said Ro. "They're in order — so let's be clever."
"Watch," said Ro. "Split the pile in half and peek at the middle letter."
A B C D E F G H I J K L M | N O P Q R S T U V W X Y Z
"P comes after M, so the whole first half — A through M — can be thrown out in one move. Now split what's left and peek again:"
N O P Q R S T | U V W X Y Z → keep the left half (P is in here)
N O P Q | R S T → keep the left half
N O | P Q → keep the right half
P | Q → there it is!
"That's binary search," said Ro, blowing on her fingertips like a gunslinger. "Every peek throws away half of what's left. To find Peppermint, we looked at about 5 cards instead of flipping past 16. With a thousand cards, you'd find any recipe in only about ten peeks. Time is money!"
Every peek at the middle throws away half the cards. How fast can you corner the letter P?
Looking for Peppermint in 26 cards. Peek in the middle!
Checking one at a time is a linear search — if the thing you want is last, you check everything. 26 cards → up to 26 peeks. A thousand → a thousand peeks.
Binary search: peek in the middle, keep the half your answer must be in, toss the rest. Repeat. Halving shrinks piles absurdly fast:
| Number of cards | Linear search (worst case) | Binary search (worst case) |
|---|---|---|
| 26 | 26 peeks | ~5 peeks |
| 1,000 | 1,000 peeks | ~10 peeks |
| 1,000,000 | 1,000,000 peeks | ~20 peeks |
The one rule: it only works when things are sorted. (You already do this in a dictionary — nobody starts at page one.)
It was nearly the end of August — almost back-to-school time. The shop ran beautifully, but every single day, something odd happened. Sonny was forever calling Ro for help.
"Instead of treating each weird thing as a brand-new mystery," said Ro, "let's sort our problems into types. Once you know what kind of problem it is, you usually know how to handle it."
So Ro started a cheat sheet of the three problems that came up again and again.
"A guest asked for Mango today," said Sonny, "and the Scooper went hunting for a Mango tub — but we don't have Mango! There's no such tub on the shelf. It scraped at the empty air where a tub should have been, and made the saddest little sound. Skrrrk."
"That's a null problem," said Ro. "Null means the thing you expected isn't there at all. Not an empty tub — no tub. The fix is to check before you act:"
while wants_scoop:
if shelf_has_tub(flavor):
scoop_ice_cream()
else:
offer_another_flavor()
"Always make sure the tub is there before you reach for it. An empty tub is null's gentler cousin — the tub exists, it's just run dry — but the medicine is the same: peek first, act second. Refill it, or kindly suggest another flavor. Never just scrape at nothing."
"I taught the register a shortcut," said Sonny. "A guest asks for the third flavor on the shelf, Jerry presses 3, and the machine grabs shelf[3]. But it keeps handing over the fourth flavor! It's driving Jerry bananas."
"Classic off-by-one!" said Ro. "Remember how computers start counting at 0? On the machine's shelf, the first tub is shelf[0] — so the third tub is shelf[2], and shelf[3] is the fourth. People count from 1, machines count from 0, and whenever you forget which is which, you land exactly one spot away from where you meant — the neighbor flavor. The fix is to double-check the exact spot before you scoop: count the way the machine counts."
"Mrs. Smitten's two little ones drop their cones on the floor every single time," sighed Sonny. "It's so sad — and we clean it up each time."
"That's a user error," said Ro, gently. "It's not the machine breaking — it's a person doing something we didn't plan for. And here's the rule: never let a user error crash the whole shop. Plan for it and handle it kindly. Keep a stack of napkins ready, and offer a free re-scoop. The show goes on."
Programmers call surprises edge cases, and handling them gracefully error handling. Nobody avoids problems — the trick is knowing which kind you've got:
| Kind | What it means | The fix |
|---|---|---|
| Null / empty | The thing you expected isn't there | Check that it exists before you use it |
| Off-by-one | You're one spot away from where you meant to be | Double-check the exact position |
| User error | A person did something unexpected | Expect it; handle it kindly; don't crash |
Good machines (and good shops) don't fall apart at the first surprise. Notice it, sort it, handle it.
Categorize each one: is it null, off-by-one, or user error? Type your guess, then peek!
1. A guest wants cherry topping, but the cherry tub is empty.
Null / empty
2. A guest gets chocolate syrup all over their fingers.
User error (hand them a napkin!)
3. Someone asks for the 3rd flavor and the machine grabs the 4th.
Off-by-one
Bonus brain-bender: One afternoon, Gus the shop dog knocks over the entire soda fountain. 🐶 Which type of error is that?
Trick question — sometimes the world throws something nobody could have planned for. Programmers call those the truly unexpected events, and the best you can do is clean up safely and carry on. Even Gus.
August faded, and school crept closer. Sonny and Ro stood behind the counter of the busiest, best-run ice cream parlour on the block — built not just from ice cream, but from loops and ifs, functions and arrays, queues and searches, and a whole lot of fixing-what-broke.
"We didn't just make ice cream," said Sonny, watching the evening light spill through the window. "We taught the machines how to think."
"And we got pretty good at thinking, ourselves," said Ro.
We hope we got you thinking about the exciting problems you could solve. It doesn't have to be an ice cream shop! You won't have much fun building something you don't care about. So here's our real question for you:
What kind of problem would you like to solve?
Sonny and Ro started an ice cream parlour. What will you build? A pizza place? A pet-grooming robot? A treehouse club? Pick something you love, and try writing the instructions for one machine that would help run it. Use any idea from this book — a loop, an if, a function.
Your very own machine:
Show a grown-up what you came up with — we'd love to see it too!
When we learned to code, getting it wrong was most of the experience. A missing comma. A stray bracket. You typed, it broke, you fixed it, it broke again — and then, finally, it worked, and something clicked into place that you could actually feel. It was slow and frustrating and, looking back, kind of wonderful.
Kids today get to skip nearly all of that. A kid can describe a game — a cat catching falling stars, say — and a computer will build it, politely and almost instantly, without a single word about brackets. Which means one day your child will look up from a machine that just did all this and ask you a perfectly reasonable question: if the computer can do it, why should I learn any of it?
They deserve a better answer than the usual one — that they should learn it so they can check the computer's work. The computers are getting alarmingly good at checking their own work, so that answer shrinks a little every year. Besides, no child in history has ever been inspired by the promise that they'd grow up to be a supervisor.
We think the real answer starts with calculators. Calculators have out-multiplied every human on Earth since about 1972, and we still teach children arithmetic — not out of nostalgia, and not because the times-table industry has a powerful lobby. We do it because a kid who never learns arithmetic never develops number sense: the quiet feel for quantity that notices the bill is wrong, the deal is bad, the "50% more!" is somehow less. A calculator can do all the computing for you. It cannot do the noticing. This book is after the same kind of sense, one floor up — a feel for how processes work: what repeats, what depends on what, what has to happen in order, and what to do when it doesn't.
The bigger reason is this: you can only ask for what you can think of, and you think with the ideas you've got. A machine that will build anything you can describe doesn't make imagination less important — it makes imagination just about the whole job. Every idea in this book is a new thought your child becomes able to think. A kid who knows what a loop is starts spotting loops everywhere: brushing teeth, setting the table, the chorus of every song on the radio. A kid who's met a queue suddenly understands the lunch line. And nobody has ever asked a machine for a priority queue without first knowing that a priority queue was a thing they could want. The ideas come first. The asking comes after.
None of these ideas were ever really about computers, either. Milk spoils at the back of the fridge because somebody used a stack where they needed a queue — true in 1950, true now, and still true when today's computers look like steam engines. Bakeries ran first-in-first-out long before anyone thought to write it down. Programming languages come and go like pop songs, but the ideas underneath are more like arithmetic: small, old, and quietly running everything. So this isn't job training — we have no idea what the jobs will be. It's clear thinking, learned through a subject that happens to run the world at the moment.
There's also a feeling we're trying to protect. The click of figuring something out yourself is one of the best feelings a person gets to have, and a kid who only ever orders finished things from a machine never gets it — a customer is all they get to be. A kid who understands gets the discovery that the machines humming all around them were never magic: they're ideas, stacked up, that some person once thought all the way through. And a world made of ideas is a world you're allowed to have ideas about.
That's why there is no computer anywhere in this book. The most advanced piece of technology your child will need is a pencil. (We recommend one with an eraser. The infinite-loop chapter explains why.) Pencil and paper strip away the brackets and the error messages and leave just the ideas — which, it turns out, were the good part all along.
If we did this right, your child won't be able to type a single line of real code by the last page. They'll have something better: a head full of ideas that were true before computers, will be true after them, and are excellent company in the meantime — and a growing suspicion that they could think up a few of their own.
That's worth a pencil and an afternoon. We think it's worth a lot more.
— Robin & Sonya
12), a word in quotes ("vanilla"), or an answer (true/false). Quotes mean the thing itself; no quotes means the name of a board.+ - == != > < >=).else covers the other case.Loops — Puzzle 3
while door_is_open:
wave_hello()
You don't know how many times, so it's a while loop!
Stacks & Queues — Challenge
q = Queue()
q.push("I")
q.push("scream")
q.push("for")
q.push("ice")
q.push("cream")
for n in range(5):
print(q.front())
q.pop()
First in, first out — so it prints I scream for ice cream, in exactly the order we pushed it.
Original concept & draft (2017): Robin Mehta & Sonya Kotov · Revised master (2026)
Inspired by the Bubble Sort Zines and Linda Liukas's Hello Ruby
Illustrations to come — art plates throughout mark each planned picture 🍒