Random numbers
Sometimes a robot should not be predictable. A game that always asks the same question, or a creature that always turns the same way, stops being interesting after one run. A random number fixes that.
Blocks reference
| Block | What it does |
|---|---|
(pick random (1) to (10)) | Reports a number between the two values, including both ends. A fresh number every time it runs. |
(pick random (1.0) to (10.0)) | The same range, but written with decimal points — so it reports decimals instead of whole numbers. |
Whole numbers, or decimals?
This catches everybody once, because the two blocks look almost identical and behave completely differently. The block decides from what you typed, not from the size of the range:
- Both ends written as whole numbers —
(pick random (1) to (10))— and you get whole numbers only. 1, 2, 3 … 10. Never 4.5. - Either end written with a decimal point —
(pick random (1.0) to (10))— and you get decimals: 4.5281749263, 9.9903, anything in between. A whole number is possible but vanishingly unlikely.
And both ends are included. That matters more than it sounds: (pick random (1) to (2)) has exactly two possible answers, not one, which is what makes it a coin toss. A range of whole numbers from a to b has b − a + 1 answers in it.
Type your own range below and press it. Then press it five hundred times.
(pick random (1) to (10))
Only 10 answers exist here, and 1 and 10 are two of them — both ends are included, so the count is 10 − 1 + 1. Nothing lands between the marks.
Press pick a few more times. A handful of picks tells you nothing about whether it is fair.
Change 10 to 10.0 and nothing about the range changes — but the row of separate marks becomes an unbroken band, because the answers stop being a short list of possibilities and become anywhere in between. That is the whole rule, and it is the difference between one keystroke.
Keep the range sensible. A random turn between 1 and 360 degrees mostly looks like chaos; one between 30 and 90 looks like a creature deciding.
Where it goes
Dropped into a motor block it varies the movement; into a wait it varies the timing; into a display block it varies what is shown. Anywhere a number can go, a random number can go.
[A v] run [clockwise v] for (pick random (30) to (90)) [degrees v] :: motors wait (pick random (1) to (4)) seconds :: control write (pick random (1) to (6)) at line (1) :: display
Turning a random number into a random decision
The step that makes this block genuinely useful is feeding it into an if … then … else. On its own a random number varies how much something happens. Inside a decision it varies what happens at all.
The smallest version is the coin toss: 1 or 2, two branches, and a fifty-fifty chance of each. Here it is deciding how a driving base gets round an obstacle — one way it drives for three seconds and turns right, the other it drives for two and turns left.
when program starts :: events hat set [coin v] to (pick random (1) to (2)) :: variables if <(coin) = (1)> then move [forward v] for (3) [seconds v] :: movement move [right: 100 v] for (0.5) [rotations v] :: movement else move [forward v] for (2) [seconds v] :: movement move [left: 100 v] for (0.5) [rotations v] :: movement end
The same shape gives a motor a fifty-fifty chance of turning one way or the other — 90° or −90°. On the EV3 the sign lives in the direction dropdown rather than in the number, so −90° is written as counterclockwise 90:
when program starts :: events hat set [coin v] to (pick random (1) to (2)) :: variables if <(coin) = (1)> then [A v] run [clockwise v] for (90) [degrees v] :: motors else [A v] run [counterclockwise v] for (90) [degrees v] :: motors end
when program starts :: events hat set [coin v] to (pick random (1) to (2)) :: variables if <(coin) = (1)> then move [forward v] for (3) [seconds v] :: movement move [right: 100 v] for (0.5) [rotations v] :: movement else move [forward v] for (2) [seconds v] :: movement move [left: 100 v] for (0.5) [rotations v] :: movement end
Let it loop eight times. Both branches come up four times — and they do not take turns doing it, which is the part that looks like a bug and is not.
Two things are worth watching there. Only one branch runs — the other does not happen at all, so the robot ends up somewhere the other branch would never have put it. And the tally along the bottom is uneven: two 2s in a row, then two 1s. Over the eight runs it comes out four and four, but it does not get there by taking turns, and a program that looks broken for three runs in a row is usually just being random at you.
Storing the draw in a variable is not decoration here — it is required. The next section is why.
A new number every time it runs
This is the part that bites. The block does not hold a number — it produces one, freshly, each time it is reached. Use it twice and you have asked two questions and got two answers. Both creatures below turn a head one way and then back again.
store it, then reuse it
set [angle v] to (pick random (30) to (90)) :: variables [A v] run [clockwise v] for (angle) [degrees v] :: motors [A v] run [counterclockwise v] for (angle) [degrees v] :: motors
ask twice
[A v] run [clockwise v] for (pick random (30) to (90)) [degrees v] :: motors [A v] run [counterclockwise v] for (pick random (30) to (90)) [degrees v] :: motors
Let it loop. The angle changes every run on both creatures, as it should. Only the right-hand one also ends up facing somewhere new.
Both are unpredictable, which is what was wanted. But the one on the right asked the random block a second time for the return journey, so it turns back by a different amount and never comes home — by a different margin every run, which is exactly why the bug survives being tested a few times.
The fix is the one on the left. Ask once, store the answer, then use the store:
set [angle v] to (pick random (30) to (90)) :: variables [A v] run [clockwise v] for (angle) [degrees v] :: motors [A v] run [counterclockwise v] for (angle) [degrees v] :: motors
The same rule is why the coin toss above says set [coin v] to (pick random (1) to (2)) :: variables on its own line. Putting the random block straight into the if would work for one test and then fail strangely the moment anything below the if needed to know which way it went — because asking again is a fresh toss.
Advanced — picking a random thing, not a random number
A random number is rarely what you actually want. What you want is a random something: a move, a colour, a name. Put the two together and the number stops being the answer and becomes the position of the answer.
(item (pick random (1) to (length of [Names v])) of [Names v])
Read it from the inside out. (length of [Names v]) asks the list how many things are in it. The random block picks a position between 1 and that. (item () of [Names v]) fetches whatever is standing in that position.
Asking the list for its own length is the part to copy. Type the number in instead and the program works perfectly until the day somebody adds one more item — and then that item is never picked, silently, for ever. Try it below.
when program starts :: events hat clear display :: display set [pick v] to (pick random (1) to (length of [Names v])) :: variables write (item (pick) of [Names v]) at line (3) :: display
The range is asked from the list itself, so adding a name is enough — press «add a name» and the new one is in the draw immediately.
Switch the range to the typed 5, add a name, and press pick 40 more. The counts under the new faces stay on zero. Nothing is misspelled, nothing reports an error, and one name simply never comes up — which is the same class of bug as the creature that never comes home, and just as hard to notice by running the program once.
It makes testing harder
This is the honest cost. A program with randomness in it behaves differently every run, so a bug that appears once may not appear again — and you cannot tell whether a fix worked from a single test. When hunting a bug in a random program, temporarily replace the random block with a fixed number, fix the bug, then put it back.
More data tutorials
- Variables — Give the robot a number it can remember and change as it works.
- Lists — Store several readings in order, so the robot remembers them all.
- Broadcasting a message — Let one stack of blocks tell another stack to start.
- My Blocks — Name a group of blocks so it can be reused instead of copied.