Demotron Learn to make games in Picotron!

Demotron / Beginner basics / 03

Numbers, Variables and Repeating

notes.txt

1 Doing Sums

We can type a sum straight into print and Picotron works it out before it prints anything:

1print(3 + 4)  -- 7
2print(3 - 7)  -- -4
3print(3 * 4)  -- 12

Picotron's terminal runs Lua typed at its prompt, so we can try any of these there without making a program at all. We write times as *, since there's no × on the keyboard.

Click the op sign until it shows -, and make b bigger than a. Demi ends up owing gems, and the red outlines are the ones owed.

2 Brackets First

Picotron doesn't read a sum left to right the way Fuzz does:

1print(2 + 3 * 4)    -- 14
2print((2 + 3) * 4)  -- 20

Times and divide go before plus and minus, wherever they sit in the line, so in 2 + 3 4 the 3 4 is worked out first and the 2 is added to the 12 it makes. Fuzz doesn't know that. Divide is /.

Brackets go before everything, so when we want the plus done first we put it in brackets.

The strip along the bottom of the slide has the whole order, and the one we haven't met is ^ for powers. It goes after brackets and before times. 2 ^ 8 is eight 2s multiplied together, which is 256. In 2 * 3 ^ 2 the power happens first, so we get 18.

Picotron prints both of those as 256.0 and 18.0. The .0 says the answer is a float, a number that's allowed to have a fraction, and ^ always gives one back, even when the answer comes out whole.

Not sure? Bracket it:

1print(2 + (3 * 4))  -- still 14

3 Keeping Score

A variable is a name with a number in it, and = puts the number there:

1score = 0
2points = 10

Then every time Demi picks up a gem we run one line:

1score += points

That's short for score = score + points. Picotron works out the right side first with the score as it is now, then puts the answer back in score. A score of 20 with points at 10 becomes 30. -= and *= work the same way. Plain Lua doesn't have them, Picotron added them, so you won't find them in a Lua book.

Drag the points slider while Demi is collecting and the next gem adds the new amount, because the line reads points again every time it runs. The arrows steer Demi if you want to help.

high-score is the one that bites. Picotron reads it as high minus score and stops with an error about a nil value called high. We write high_score instead. A name can't start with a digit either. Case matters too, so Score and score are two different variables.

To show the score with a label we join text onto it with .., which turns the number into text on the way:

1print("score: "..score)

The joining waits until the sums in the line are done, so "gems "..2 + 3 comes out as gems 5.

4 It Remembers

This one's broken on purpose. Turn forget on, or click the score = 0 line, to break the slide the same way:

1function _update()
2  score = 0
3  -- grab a gem:
4  score += 10
5end

A variable keeps its number from one frame to the next until we change it. _update runs 60 times a second, and this one starts every frame by putting 0 back in score. A gem's 10 shows for one frame and is gone in the next, while the gems collected counter keeps going.

The fix is to set score once and only add to it in the update. A line at the top of the file runs once when the program starts, because it isn't inside any function. _init (Basics 2) runs once too. Put it in _init and calling that again starts the score over.

5 Sharing Gems

Seven gems, two friends:

1print(7 / 2)   -- 3.5
2print(7 \ 2)   -- 3
3print(7 % 2)   -- 1

/ always gives a float too, so even 10 / 2 comes out as 5.0. Whole numbers with no point we call integers.

Turn whole on to share with \ instead. Each plate gets 3 and one gem sits in the bowl, and % is how many are left over like that. \ and % are dividing too, so they go before plus and minus, the same as times.

\ rounds down. Below zero, round down still means smaller, so -7 \ 2 is -4, not -3. We get an integer back when both numbers are integers, but 7.0 \ 2 is 3.0.

Where you'll notice the difference is on screen. With a score of 30 we get this:

1print(score / 2)  -- 15.0, which looks like a bug
2print(score \ 2)  -- 15

flr fixes it too, and it rounds any number down to an integer.

Note: Which kind is it? math.type(x) says "integer" or "float".

6 Going Round

Every frame we move Glitch and take the remainder after dividing by 480, the width of the screen:

1x = (x + speed) % 480

% takes away as many 480s as it can and gives what's left, so 482 % 480 is 2 and Glitch comes back on the left. The remainder can't be 480 or more, so x never reaches 480.

Drag speed below zero and it works the other way. In Lua the remainder takes the sign of the number we divide by. With 480 that means it's never negative, so -2 % 480 is 478, just inside the right edge.

Near an edge we draw Glitch twice, 480 pixels apart. Whatever hangs off the screen isn't drawn. The copy 480 to the left shows the part that has already come round. With only one, Glitch would slide off the right edge cut in half, then jump to the left edge in a single frame.

7 Blinking

t() is still the seconds since the start, from Basics 2. Taking % 1 of it throws away the whole seconds, and we keep the part after the point:

1if (t() % 1 < 0.5) circfill(x, y, 3, 10)

At 3.25 seconds that's 0.25, and it starts again from 0 with each new second. < is less than, and a one-line if runs the rest of the line when the bit in brackets is true, so the fireflies are lit for the first half of every second.

The dashed on line is that 0.5, and you can drag it. Put it at 0.1 and we get a quick blink, at 0.9 a quick wink of dark.

Other rhythms come from changing the numbers around the %. A < gives back true or false, which is what we store in lit here:

1lit = t() % 2 < 1        -- a second on, a second off
2lit = t() * 4 % 1 < 0.5  -- four blinks a second

8 Repeating

A for loop runs the lines inside it again and again, with i counting up:

1for i = 1, 5 do
2  circfill(i * 40, 200, 6, 8)
3end

The first time round i is 1 and the circle goes at x 40. Then i becomes 2 and the next one goes at 80, and so on until it has run with i at 5. Make it 500 and the code's no longer.

The slide plays it back slowly so Demi has time to walk to each spot. A real loop does all of that in one go, inside a single frame, before anything shows on the screen.

A third number is the step:

1for i = 1, 10, 3 do
2  print(i)  -- 1, 4, 7 then 10
3end

Set n to 0 and the loop doesn't run at all, because i would start at 1 and that's already past the end. Once the loop's over, i is gone again and reads as nil.

9 Random

Every call to rnd gives a different number. Give it 480 and we get anything from 0 up to just under 480:

1x = rnd(480)         -- 379.37624260783, say
2d = flr(rnd(6)) + 1  -- a dice roll, 1 to 6
3c = rnd({8, 9, 10})  -- one of the three

The numbers are floats with lots of digits, so for a whole one we round down with flr. Give rnd a list in curly brackets and it picks one of them, so the last line is a random warm colour.

They aren't really random. Picotron works them out with a sum, one after another, from a starting number called the seed, and srand sets the seed:

1srand(42)
2x = rnd(480)

After that we get the same numbers in the same order every time. Turn same on and from the second shower on, each lands exactly where the last one did, the pink ticks right under last shower's. The first shower after the switch still lands somewhere new, because the one before it wasn't seeded.

It's also how we draw 60 random stars every frame without them jumping about. Call srand with the same number first and we get the same stars.

10 Gem Catch

The arrows move Demi and Z hops over Glitch. When time's up, X starts a new round. Leave the keys alone for a second and a half and Demi plays by itself.

The hearts in the corner come from a loop that runs once per heart:

1for i = 1, hearts do
2  heart(386 + i * 22, 18, 8)
3end

Each time Glitch gets Demi, hearts drops by one. From the next frame on the loop runs one time fewer and there's one heart fewer on the screen. Nothing has to rub a heart out, because the whole screen is drawn again every frame.

The clock counts frames down from 1800, which is 30 seconds at 60 frames a second, and shows them with left \ 60. Dividing by 60 with \ turns frames into whole seconds. 1799 frames shows as 29, and the clock never reads 29.98.

The other labels point at the rest, like Glitch's % and the gems' rnd. Turn labels off if they're in the way.

11 Try It

This lesson's try-it demo is a small night-sky program.

  • The moon's dist is set once, at the top. Then dist += speed adds 1.5 to it every frame, and dist remembers the total from one frame to the next.
  • dist % 540 - 30 brings the moon back round. 540 is the screen plus 60 pixels, so the moon is all the way off the right before it comes back at -30 on the left. We count the laps by dividing dist by 540 and rounding down.
  • srand(5) at the start of every draw makes rnd hand out the same numbers each frame, so the 60 stars stay put.
  • There's a loop for the stars and one for the hills, a pair every 80 pixels. A third draws the fireflies.
  • flr(t() * 4) % 41 counts from 0 to 40, four a second, before it starts over, and we draw that many fireflies. Inside the loop, i % 10 and i \ 10 turn i into a column and a row, 10 to a row. They need no brackets, because they share a level with the times after them and the plus waits for both.
  • Each firefly's size comes from (t() + i / 10) % 1. It grows over a second and snaps back, a tenth of a second ahead of the one before it, so a ripple runs along each row.

Everything here moves by itself. Nothing listens to the keyboard yet, and Basics 4 is where we start reading the arrows.

Start up Picotron, copy the code below and paste it into the code editor's main.lua, then press Ctrl + R.

try.lua
1-- night sky: a moon going round, the same
2-- stars every frame, and rows of fireflies
3
4dist = 0     -- how far the moon has gone
5speed = 1.5  -- pixels a frame
6
7function _update()
8  dist += speed
9end
10
11function _draw()
12  cls(1)
13  -- srand(5) restarts rnd's numbers, so the
14  -- stars land in the same places each frame
15  srand(5)
16  for i = 1, 60 do
17    pset(rnd(480), rnd(150), 6 + i % 2)
18  end
19  -- % brings the moon back round
20  circfill(dist % 540 - 30, 50, 14, 10)
21  -- hills, one every 80 pixels
22  for i = 0, 6 do
23    circfill(i * 80, 290, 70, 3)
24    circfill(i * 80 + 40, 310, 70, 27)
25  end
26  -- 4 new fireflies a second, 10 to a row,
27  -- starting over after 40
28  n = flr(t() * 4) % 41
29  for i = 0, n - 1 do
30    x = 60 + i % 10 * 40
31    y = 120 + i \ 10 * 22
32    -- each one pulses: t() % 1 goes 0 to 1
33    circfill(x, y, 1 + (t() + i / 10) % 1 * 4,
34      10)
35  end
36  print("fireflies: "..n, 8, 8, 7)
37  print("moon laps: "..flr(dist / 540), 8, 18, 7)
38end

Try This

  • Make the moon cross from right to left.
  • Make the stars come out one at a time, a new one every second.
  • Make some of the stars twinkle without moving.
  • Add a second, smaller moon that goes round twice as fast.
Demo — click me, then use the keys
1 / 11

Built with Picotron 0.3.0d.