Challenge 1
Add a fifth mode. Add an option to the list and a rule to the runner. The wrap-round should need no change at all, because it asks the list how long it is. If you had to edit it, look at how you wrote it.
EV3 Robotics›Level 3 · Advanced›Lesson 20
Level 3 · Lesson 20 · EV3-L03-2060 minutes · Ages 9–16 · Model: M134 Minigun Plus
The Minigun Plus: the handheld from Lesson 1, grown up. The barrel cluster still spins on a Large Motor, but now a Medium Motor elevates it — and the Brick’s own buttons choose what the trigger does.
This is the same model you built twenty lessons ago. Then, its whole lesson was giving one routine a name. Now it has several routines and a person has to pick one before firing — which needs an interface, not another block.
By the end of the lesson your machine will show a menu, let somebody scroll it with their thumb, and do what they chose — built from a state variable, a list, and four buttons you already have.
A microwave has a handful of buttons and a one-line display, and from those it offers dozens of behaviours. A washing machine has a dial and a small screen. An older iPod had a wheel and four buttons, and people navigated thousands of songs with it happily.

The trick in every case is the same: the screen shows where you are and the buttons move you around. Nothing else is needed, and adding more buttons usually makes it worse.
Because physical controls are expensive and fragile, and every one you add is another thing to label, waterproof and explain. A screen with a few buttons gives unlimited options from a fixed amount of hardware.
The important design rule is that the user must always know where they are. A menu that does not show the current selection is a machine you operate by memory and luck.
The alternative is a separate program per behaviour, and somebody at a laptop downloading a different one each time. That is fine in a classroom and useless everywhere else — nobody carries a computer to change their microwave from defrost to reheat.
A machine that can only be reconfigured by its programmer is a prototype. One anybody can operate is a product.
A menu is three things you already have: a list of options, a variable holding which one is selected, and a screen showing it. The buttons only ever change that one number.
| Button | Does | From lesson |
|---|---|---|
| Up | change selection by −1 | Variables, Level 2 Lesson 26 |
| Down | change selection by 1 | Same |
| Centre | Run the selected option | Broadcast, Lesson 3 |
| The screen | item (selection) of [options] | Lists, Lesson 9 |
Nothing here is new except the button blocks. A menu is a state machine whose state happens to be a number, and a list walked by hand instead of by a loop. Recognising that you already have the parts is more valuable than the menu itself.
when program starts :: events hat
delete all of [options v] :: list
add [SHORT BURST] to [options v] :: list
add [LONG BURST] to [options v] :: list
add [THREE ROUND] to [options v] :: list
add [SWEEP] to [options v] :: list
set [selection v] to (1) :: variables
forever
clear display :: display
write [CHOOSE:] at line (1) :: display
write (item (selection) of [options v]) at line (3) :: display
wait until <<brick [up v] button pressed? :: sensors> or <brick [down v] button pressed? :: sensors>> :: control
if <brick [up v] button pressed? :: sensors> then
change [selection v] by (-1) :: variables
else
change [selection v] by (1) :: variables
end
wait (0.25) seconds :: control
endPress Up on the first item and selection becomes 0 — a position that does not exist, and item 0 returns nothing. Either stop at the ends, or wrap:
if <(selection) < (1)> then set [selection v] to (length of [options v]) :: variables end if <(selection) > (length of [options v])> then set [selection v] to (1) :: variables end
length of, so adding a fifth option needs no change here.A finger holds a button for a tenth of a second or more, and the loop runs hundreds of times in that period — so one press scrolls the menu twenty items. A short wait after each press, or waiting for the release, fixes it.
The Brick has five buttons of its own — up, down, left, right and centre. They need no sensor, no cable and no port, which makes them the easiest way to let a person tell the robot to do something.
| Block | What it does |
|---|---|
when [center v] button [pressed v] :: events hat | Starts a whole stack when that button is pressed. |
wait until <is [center v] button pressed? :: sensors> | Holds an existing program until the button is pressed. |
A program sitting on a wait block looks exactly like a program that has crashed. Watch one wait — including through two presses of the wrong button.
the other way to do it
A program parked on a wait block looks identical to a crashed one. This is why the status light or a line on the screen is worth setting before you wait.
The wait is watching one button. Up and right change the (button) reading, and the program does not care. That reading is a number rather than a name: 0 for nothing, then 1 left, 2 centre, 3 right, 4 up, 5 down.
Nearly every machine has a start button that is deliberately separate from switching the power on. A robot that begins moving the instant it is powered up is hard to set down and hard to test — one that waits for a press can be positioned first.
A variable holds one number. A list holds many, in order, under one name — so a robot can remember every reading it took rather than only the most recent.
If a variable is a piece of paper with one number on it, a list is a row of lockers. Each locker has a number on the door, and each one holds something of its own. They all share a name — snacks, colours — and you tell them apart by the number, not the name.
The doors matter. You cannot see what is in the whole row at a glance: to find out what is in locker 3, you have to open locker 3. And if you want to change what is in there, you open it, take out what is inside, and put something else in. Try it — click a door.
Three lockers are in use. You cannot see inside any of them until you open one — click a door.
Every list block is one of those physical actions, and the block for whatever you just did appears underneath the lockers:
| Block | The locker version |
|---|---|
add [banana] to [snacks v] | Put a banana in the next free locker. Always the end of the row. |
(item (3) of [snacks v]) | Open locker 3 and tell me what is inside. One locker, one look. |
replace item (3) of [snacks v] with [candy] | Open 3, take out what is there, put candy in. Nothing else moves. |
delete (3) of [snacks v] | Empty locker 3 and close up the gap — everything after it shuffles down one. What was in 4 is now in 3. |
(length of [snacks v]) | How many lockers are in use. |
delete all of [snacks v] | Empty the whole row. This belongs at the top of a program. |
Deleting is the one that catches people. A locker does not stay empty — the row closes up. Delete item 2 of a five-item list and you have a four-item list, with everything after position 2 now one number lower than it was. Any position you wrote down before the delete is wrong afterwards.
Lists are not only for numbers. Anything a block can report can go in one:
| Block | What it does |
|---|---|
add [thing] to [List v] | Puts a new value on the end. |
(item (1) of [List v]) | Reads the value at a position. Positions start at 1. |
(length of [List v]) | Reports how many values are stored. |
delete all of [List v] | Empties it. This belongs at the top of the program, for the same reason a variable is set to zero there. |
The natural shape is a loop that takes a reading and adds it. Watch the same four readings go into a variable and into a list.
into a variable
into a list
Watch the variable box, not the list. Every reading it shows is correct; it is the ones it has already forgotten that matter.
Notice that the variable is never wrong. Every number it shows is a real reading, correctly taken, moments ago. It simply has room for one, so each new reading pushes the last one out — and by the fourth stop three readings have quietly ceased to exist.
Afterwards the list can be walked to find the largest, the smallest, or the average — none of which is possible if you only ever kept the latest value.
Positions count from 1, not 0. A loop that starts its counter at 0 reads a position that does not exist and misses the first entry.
Storing readings is only half of it. To use a list you walk it, and that needs one more idea: a variable that holds a position rather than a value. Call it i. Set it to 1, read item i, then change i by 1 — and the next pass round the loop looks at the next locker.
Two things make this work without anybody counting. The loop repeats length of colours, so it runs once per item however many there are; and i doubles as the screen line, so each item lands on its own row.
when program starts :: events hat delete all of [colours v] add [red] to [colours v] add [blue] to [colours v] add [green] to [colours v] add [yellow] to [colours v] clear display :: display set [i v] to (1) repeat (length of [colours v]) write (item (i) of [colours v]) at line (i) :: display change [i v] by (1) end
Watch i rather than the blocks. It is the only thing that changes between one pass and the next, and it is what makes each pass look at a different locker.
Nowhere does that program mention the number four. Add a fifth colour and it writes five lines, unchanged — which is the whole reason to ask a list its length rather than typing a number you will have to remember to update.
Once a robot can walk a list it can do real work on one. Both of these are the same trick — go along comparing two items at a time — and they differ only in what they do about it. Sorting swaps the pair; finding the biggest just remembers the winner.
when program starts :: events hat
set [i v] to (1)
repeat ((length of [nums v]) - (1))
set [j v] to (1)
repeat ((length of [nums v]) - (i))
if <(item (j) of [nums v]) > (item ((j) + (1)) of [nums v])> then
set [temp v] to (item (j) of [nums v])
replace item (j) of [nums v] with (item ((j) + (1)) of [nums v])
replace item ((j) + (1)) of [nums v] with (temp)
end
change [j v] by (1)
end
change [i v] by (1)
endOnly ever two numbers are being compared at a time. A sort looks complicated because it repeats, not because any one step is hard.
A bubble sort looks hard because it is a loop inside a loop, but no single step is: compare two neighbours, swap them if they are the wrong way round, move along one. Each full pass floats the biggest remaining number to the end — which is where the name comes from — so after as many passes as there are items, the list is in order.
Finding the biggest needs no swapping at all. Assume the first item is the winner, walk the rest, and whenever you meet something bigger, remember that instead. One pass, one variable. Swap the > for a < and the same program finds the smallest — which is how a line-following robot works out its black and its white before choosing a threshold between them.
This is data collection — a robot driving a course while recording distances, then reporting what it found. It is the difference between a machine that reacts and one that measures.
The Brick’s screen is a small black-and-white whiteboard. You can wipe it, draw one of the built-in pictures on it, or write your own words at a spot you choose. It is how the robot tells a person what it is doing.
| Block | What it does |
|---|---|
clear display :: display | Wipes the screen blank, ready for something new. |
display [Eyes / Neutral v] :: display | Draws a built-in picture chosen from a list — eyes, faces, arrows, symbols. |
display [Eyes / Neutral v] for (2) seconds :: display | Draws the picture, holds the program for that long, and then carries on. Handy when a face should be seen before anything else happens. |
write [Hello] at line (1) :: display | Writes your own text on one of the screen’s eight lines. |
write [Hello] at x: (10) y: (40) with font [normal black v] :: display | Writes text at an exact spot instead of a line, and lets you choose the size. Use it when a message has to line up with something else on the screen. |
The screen has no memory of whose writing is whose. It keeps every mark until something wipes it — including the marks left by the last run. Watch the same program with and without its clear display block.
with clear display
without it
Let it loop two or three times. The left screen still reads cleanly; the right one is the same program with one block missing.
Both Bricks are told exactly the same thing. The left one wipes the screen first, so line 7 holds one message and reads correctly. The right one never wipes, so each new message is drawn over the last and the words turn into a smudge. This is why a program that seems to display nonsense is usually displaying the truth — several times over.
There are two blocks for writing text, and they describe where in two completely different ways. Watch one message move around the screen under both of them.
the screen is 178 pixels across and 128 down
The arrows are the two numbers. The one along the top is x; the one down the side is y, and it counts downwards from the top edge.
write [EV3] at line (1) is the simple one. You give it a line number and it puts the text there, starting hard against the left edge — you do not choose how far across, only how far down.
For most robots this is all you need: a status word on line 1, a reading on line 3, a warning on line 5. Pick your lines at the start and keep each one for one job, the way you would keep one colour of status light for one meaning.
write [EV3] at x: () y: () with font [] treats the screen as a grid of pixels — 178 across and 128 down — and lets you put the text anywhere on it.
The block ends with a font dropdown, and normal black is the usual choice. A larger font makes the writing easier to read from across the room but takes more room across the screen, so fewer characters fit before the text runs off the edge.
The text slot of either block will take a reporter, so a sensor value can be shown directly: drop (4 distance in cm) into the slot and the screen shows the reading. That is the EV3’s version of a print statement, and it is the fastest way to find out what a robot actually thinks it is seeing.
A bare number on its own is hard to read, though. To show DIST: 23 rather than 23, use the operator block join [DIST: ] () to glue the label to the value, and put the join into the write block’s text slot.
when program starts :: events hat clear display :: display forever write (join [DIST: ] ([4 v] distance in [cm v] :: sensors)) at line (1) :: display end
Note the forever: a value written once is a value from the start of the program. To watch a reading change, the write block has to be inside a loop.
Once one value is on the screen the rest follows, and the useful trick throughout is join: it glues two pieces of text together and hands the result to the write block. Anything that reports a value can go in either slot — a variable, a sensor, or another join.
Counting is the classic case. A variable counts how many times the Touch Sensor has been pressed, and a bare 5 on the screen tells nobody anything — PRESSED: 5 tells them everything:
when program starts :: events hat clear display :: display set [count v] to (0) :: variables forever wait until <[1 v] is pressed? :: sensors> change [count v] by (1) :: variables write (join [PRESSED: ] (count)) at line (1) :: display wait until <not <[1 v] is pressed? :: sensors>> end
The two wait until blocks are what make it count presses rather than counting as fast as the loop runs while your finger is down. That is the Touch Sensor’s own lesson, but it shows up here because a counter on the screen is where you first notice it going wrong.
Give every reading its own line and keep it. A line that changes meaning halfway through a program is unreadable at a glance, which is the only speed a screen on a moving robot gets read at.
when program starts :: events hat clear display :: display forever write (join [DIST: ] ([4 v] distance in [cm v] :: sensors)) at line (2) :: display write (join [DEG: ] ([A v] degrees counted :: sensors)) at line (5) :: display end
degrees counted is worth putting on the screen the first time you use it — it is the fastest way to find out whether a motor is turning as far as you think it is, and it is the block behind every “why did it stop early” question.
Sensors report numbers, and people read words. The Colour Sensor reports 5; the person watching wants RED. A chain of if … then … else blocks does the translation, and it is worth doing once into a variable rather than in every write block:
if <([3 v] color :: sensors) = (5)> then
set [name v] to [RED] :: variables
else
if <([3 v] color :: sensors) = (4)> then
set [name v] to [YELLOW] :: variables
else
set [name v] to [OTHER] :: variables
end
end
write (join [COLOR: ] (name)) at line (4) :: displayThe same shape turns a distance into a warning. Here the screen carries the number and what the number means, which is what lets somebody across the room tell whether the robot is about to hit something:
if <([4 v] distance in [cm v] :: sensors) < (10)> then
set [status v] to [TOO CLOSE!] :: variables
else
if <([4 v] distance in [cm v] :: sensors) < (30)> then
set [status v] to [OBSTACLE FOUND] :: variables
else
set [status v] to [NOTHING FOUND] :: variables
end
end
write (status) at line (3) :: displayDrive all four of those at once below. The screen is the real thing — five write blocks, five lines, and every one of them a join.
Drive the sensors and read the screen. Every line is one write block with a join in its text slot.
| Line | What goes in the write block | On the screen now |
|---|---|---|
| 1 | join "PRESSED: " (count) | PRESSED: 3 |
| 2 | join "DIST: " (4 distance in cm) | DIST: 24 |
| 3 | (status) — set by the if chain | OBSTACLE FOUND |
| 4 | join "COLOR: " (name) | COLOR: RED |
| 5 | join "DEG: " (A degrees counted) | DEG: 180 |
Line 4 says COLOR: RED, but the sensor only ever said 5. The if chain in between is what turns a number into a word.
The moment a reading goes inside a loop, one of two things goes wrong, and which one depends on where the clear display block went.
Almost everybody tries clear inside the loop first. It gives the right text — and a screen that blinks several times a second, because between the clear and the write there is genuinely nothing on the screen and the loop goes round faster than your eye.
So the clear gets moved to the start of the program, the blinking stops, and the words go wrong. Writing text paints only the characters it has and leaves everything past them exactly where it was. Write RED over YELLOW and the LOW is still there: the screen says REDLOW. Go the other way — yellow first, then red — and it looks fine, which is why this bug takes so long to pin down.
when program starts :: events hat clear display :: display forever write (name) at line (3) :: display end
Watch the bottom three rows rather than the Brick. They are what the screen is actually doing: old characters, new characters, and whatever survives.
The fix is to make the new text at least as long as the old one, and join already does that: pad it with spaces. RED plus three spaces is six characters — exactly enough to paint over YELLOW. Nothing is left behind, and nothing is ever blank.
write (join (name) [ ]) at line (3) :: display
Pad to the length of the longest thing that line can ever show. Six characters covers RED, BLUE and GREEN but not NOTHING FOUND — count the longest message, and add spaces to match. The same applies to numbers: DIST: 5 after DIST: 40 leaves a stray 0 on the end, so a line that shows a two-digit reading needs a trailing space.
Use clear-inside-the-loop when the whole screen changes at once and a flicker does not matter; use the padding trick whenever a value is being watched.
Once several values need showing, the line number becomes a counter and the values come out of a list: write item 1 at line 1, item 2 at line 2, and so on inside a repeat. That needs the list blocks rather than the display blocks, so it lives with them — Lists covers building one and reading it back item by item, and the loop that walks it is the same loop that fills the screen.
Machines in the real world tell you what they are doing: a microwave counts down, a lift shows its floor, a car dashboard warns you. A robot that shows WAITING and then OPEN is far easier to understand — and far easier to debug — than one that moves silently.
Show where you are, let the buttons move you, and act only on the confirm.
The Brick has five buttons of its own — up, down, left, right and centre. They need no sensor, no cable and no port, which makes them the easiest way to let a person tell the robot to do something.
| Block | What it does |
|---|---|
when [center v] button [pressed v] :: events hat | Starts a whole stack when that button is pressed. |
wait until <is [center v] button pressed? :: sensors> | Holds an existing program until the button is pressed. |
A program sitting on a wait block looks exactly like a program that has crashed. Watch one wait — including through two presses of the wrong button.
the other way to do it
A program parked on a wait block looks identical to a crashed one. This is why the status light or a line on the screen is worth setting before you wait.
The wait is watching one button. Up and right change the (button) reading, and the program does not care. That reading is a number rather than a name: 0 for nothing, then 1 left, 2 centre, 3 right, 4 up, 5 down.
Nearly every machine has a start button that is deliberately separate from switching the power on. A robot that begins moving the instant it is powered up is hard to set down and hard to test — one that waits for a press can be positioned first.
Say this back before moving on: “The buttons move a number. The number picks the job.”
Hold the model as you would use it. Can your thumb reach the Brick buttons while your finger is on the trigger? If not, the interface is wrong however good the program is.
| Part | What it is doing here |
|---|---|
| EV3 Intelligent Brick — and its buttons | For the first time the Brick is the interface, not just the computer. Its screen and buttons are the control panel, and they need no port because they are part of it. |
| Large Motor — the barrels | Spins the cluster, exactly as in Lesson 1. |
| Medium Motor — elevation | The new part. It is what makes a sweep mode possible, and therefore what makes a menu worth having. |
| Touch Sensor — the trigger | Fires whatever mode is selected. Note it no longer decides what happens — only when. |
Interface design is part of engineering. If the Brick faces away from the user when the model is held properly, the menu is unusable — and that is a build problem, not a programming one. Turn the Brick round if you need to.
Sensors go in ports 1, 2, 3, 4. Motors go in ports A, B, C, D. They are not interchangeable, and nothing will tell you politely if you swap them.
| Part | Port | Why this one |
|---|---|---|
| Barrels (Large) | A | Same as Lesson 1 — a program from then nearly runs on this. |
| Elevation (Medium) | B | The new motor. |
| Trigger (Touch) | 1 | Touch stays on 1. |
| Brick buttons | none | They are part of the Brick. The only input in the course that needs no cable — worth noticing. |
Check your own build now:
Two routes, and either is fine. USB is the reliable one and the one to fall back on when a room’s Bluetooth is busy; Bluetooth leaves the robot free to move, which some models need.
Do these in order. Naming the Brick after you go looking for it in the list is how groups end up driving each other’s robots.
EV3 until somebody changes it.EV3.The two failures, every class, every time. The Brick has gone to sleep while you were building — press the centre button to wake it. Or you have paired with the group at the next table, which is why the name matters.
The long version, including Port View and how to read the port tiles, is in the Brick & Bluetooth guide.
Bluetooth, and then unplug and use it. This is the first model designed to be operated without a computer — download it, disconnect, and hand it to somebody. That is the test of whether the menu works.
Stuck? The long version, with a photograph of every screen, is in the Brick & Bluetooth guide.
Three stacks: one is the menu, one is the trigger, one performs whatever was chosen. None of them knows much about the others.
define fire (rounds) (speed)
repeat (rounds)
play sound [Laser v] :: sound
[A v] start motor at (speed) % speed :: motors
wait (0.6) seconds :: control
[A v] stop motor :: motors
wait (0.2) seconds :: control
end
when program starts :: events hat
delete all of [options v] :: list
add [SHORT BURST] to [options v] :: list
add [LONG BURST] to [options v] :: list
add [THREE ROUND] to [options v] :: list
add [SWEEP] to [options v] :: list
set [selection v] to (1) :: variables
forever
clear display :: display
write [MODE:] at line (1) :: display
write (item (selection) of [options v]) at line (3) :: display
write [UP/DOWN THEN TRIGGER] at line (6) :: display
if <brick [up v] button pressed? :: sensors> then
change [selection v] by (-1) :: variables
wait (0.25) seconds :: control
end
if <brick [down v] button pressed? :: sensors> then
change [selection v] by (1) :: variables
wait (0.25) seconds :: control
end
if <(selection) < (1)> then
set [selection v] to (length of [options v]) :: variables
end
if <(selection) > (length of [options v])> then
set [selection v] to (1) :: variables
end
end
when program starts :: events hat
forever
wait until <[1 v] is pressed? :: sensors>
broadcast [run mode v] and wait :: events
wait until <not <[1 v] is pressed? :: sensors>> :: control
end
when I receive [run mode v] :: events hat
set status light to [red v] :: display
if <(selection) = (1)> then
fire (1) (100) :: custom
end
if <(selection) = (2)> then
fire (1) (100) :: custom
wait (1) seconds :: control
fire (1) (100) :: custom
end
if <(selection) = (3)> then
fire (3) (100) :: custom
end
if <(selection) = (4)> then
[B v] run [clockwise v] for (60) [degrees v] at (30) % speed :: motors
fire (2) (80) :: custom
[B v] run [counterclockwise v] for (60) [degrees v] at (30) % speed :: motors
end
set status light to [green v] :: displayfire block from Lesson 1 and its input from Lesson 2.selection, and that is set by somebody else entirely.broadcast and wait stops a second trigger pull landing on top of a running mode. Lesson 4.fire block. Twenty lessons on, the block from Lesson 1 is still doing the work.What success looks like: the screen shows a mode, Up and Down change it, it wraps at both ends, and pulling the trigger performs the mode that is showing.
If one press scrolls several items, the debounce wait is missing or too short. If the screen goes blank at an end, the wrap-round is missing and item 0 is returning nothing.
One change at a time. Predict, then run, then look.
length of. Small, and it makes the menu much easier to use.The user must always be able to see where they are and what will happen next. Everything else is detail.
Look back at Lesson 1 for a moment. Same model, same Large Motor, same trigger — and then the entire lesson was learning to give one sequence a name.
This program has a menu, a list, an input-taking My Block, two broadcasts and four modes, and it is not much longer than that first one. That is what twenty lessons of structure buys.
A menu needs a screen and three buttons. Some machines have neither. The next model has one button and nothing else — and still has to offer several behaviours, which forces a completely different answer.
Give the user a screen and they can choose. Take it away and the machine has to be cleverer.

Work through the challenges in order — each is harder than the last. The mission comes after all three, and it is meant to make you plan before you build.
Add a fifth mode. Add an option to the list and a rule to the runner. The wrap-round should need no change at all, because it asks the list how long it is. If you had to edit it, look at how you wrote it.
Show where you are. Add "2 of 4" to the screen using length of, and make the current option obviously the selected one. Then remove the debounce wait and try to select item 3. Count the presses — that is a tenth of a second of finger, seen from inside the loop.
Break the ends. Delete the wrap-round and press Up on the first item. The screen empties and the trigger does nothing. Fix it two ways — once by wrapping, once by stopping at the ends — and say which you prefer for a machine held in one hand.
Build a machine somebody else can operate with no instructions at all. Download it, unplug the cable, and hand the minigun to a person who has never seen it. Do not say anything. They must be able to work out how to choose a mode and fire it from the screen alone. Requirements: - at least five modes, each visibly different when fired - the screen always shows the current mode and how to change it - the position in the list is visible - one press moves exactly one item - pulling the trigger while a mode is running does not start a second one - the machine returns to the menu by itself afterwards Watch them without helping. Every hesitation is a fault in the interface, not in the person. Finally, look back at your Lesson 1 program for the same model. Write two sentences on what has changed and what has not — the fire block should still be in there somewhere.
Build the model before you read any further. Everything after this is about making it do something, and none of it will make much sense with nothing on the table in front of you.
Use the viewer's own controls to zoom and turn pages. Fullscreen makes it big enough to build from.
The same build on Google Drive — sometimes a video, sometimes a scan:
Use the viewer's own controls to zoom and turn pages. Fullscreen makes it big enough to build from.
Check the finished build against the picture before you switch anything on. A motor mounted the wrong way round is far easier to spot now than it is to debug later, when it looks like a program fault.