== Lesson 4: Doing It Again image::learn/04-repeating.png[The finished lesson, 480] You have drawn one word and one box. A game needs a hundred of things: stars, bricks, bullets, the rows of a menu. Typing a hundred lines is not the answer, and if it were, you would have to type another hundred the moment you wanted a hundred and one. This lesson is the answer. By the end of it a field of stars will be drifting down your window, every one of them drawn by the same three lines of code. === Start a New Folder A folder called `repeating`, a file in it called `repeating.singe`, and `Singe -R repeating` running in a terminal. === Forty Dots Type this in and run it. [source,lua] ---- function onOverlayUpdate() overlayClear() colorForeground(255, 255, 255) for star = 1, 40 do overlayPlot(star * 8, 100) end return OVERLAY_UPDATED end ---- A row of forty white dots across the middle of the window. `overlayPlot` is the simplest drawing call there is: it sets one pixel to the current foreground colour, and it is exactly what a star wants. Everything interesting here is the three lines around it. [source,lua] ---- for star = 1, 40 do ---- That says: do what follows forty times, and each time round, `star` holds a different number -- one the first time, two the second, forty the last. Both ends are included, so `1, 40` really is forty times round and not thirty-nine. This shape is a *loop*, and this particular one is a *numeric for loop*. `star` is a name you invent, the same way you invented `frames` in lesson two, and the loop fills it in for you. You do not set it, and you should not change it inside the loop. It exists only between the `for` and its `end`; ask for it afterwards and you get `nil`. The `do` is required and is easy to forget. Leave it out and Lua says `'do' expected near`, followed by whatever it found instead, which will be the first thing on the next line. [source,lua] ---- overlayPlot(star * 8, 100) ---- The body. This is what runs forty times, and it is the same line every time -- what changes is `star`, so the first dot lands at x of 8, the second at 16, and the last at 320. A loop is only useful when the body does something with the loop's number, and the first thing you will reach for every time is arithmetic on it. The body is indented one step further than the `for`, for the same reason everything else has been indented so far. [source,lua] ---- end ---- Closes the loop. You have now met three things that need an `end`: `function`, `if`, and `for`. They all work the same way, they nest inside each other, and the indentation is how you keep track of which `end` belongs to what. === Counting by More Than One The loop counts up by one unless you tell it otherwise. Add a third number and it counts by that instead. [source,lua] ---- for x = 0, overlayGetWidth() - 1, 10 do overlayPlot(x, 100) end ---- Read it as *from, to, by*. This one goes 0, 10, 20, and so on, stopping at or before the last pixel of the surface, which draws a dotted line all the way across whatever size the window happens to be. It is the same row of dots as before, described in terms of where they go rather than how many there are. The step can be negative, which counts down. [source,lua] ---- for countdown = 10, 1, -1 do ---- Ten, nine, eight, down to one. With a negative step the second number is the floor rather than the ceiling, which is what you would expect, and getting the sign wrong is a loop that never runs at all: `for i = 10, 1 do` with no step counts up from ten towards one, is already past the end before it starts, and does nothing. No error, no output. If a loop of yours never seems to run, check the direction first. The step can be a fraction too, though you will want that less often than you think. === One Loop Inside Another The body of a loop is ordinary code, and ordinary code can contain a loop. [source,lua] ---- for row = 0, overlayGetHeight() - 1, 20 do for column = 0, overlayGetWidth() - 1, 20 do overlayPlot(column, row) end end ---- That draws a grid of dots over the whole window. Put it in your `onOverlayUpdate` in place of the row and look at it, because the way it runs is worth getting straight in your head now rather than in lesson eleven when something depends on it. The outer loop runs once for `row` of 0. Inside that, the whole inner loop runs from beginning to end -- eighteen columns, eighteen dots, one row of the grid. Only when the inner loop has finished does the outer loop move on to `row` of 20 and run the entire inner loop again. The inner loop's body therefore runs eighteen times eighteen, which is three hundred and twenty-four times, for eighteen trips round the outer one. That multiplication is why nested loops are how you fill a rectangle with anything, and also why you should glance at the numbers before you nest three of them. Notice that each dot's position needs both loop variables: `column` for the across and `row` for the down. That is the pattern. If the inner body only uses the inner variable, the inner loop is drawing the same thing over and over in the same place, and you have written a slow way of doing it once. === When You Do Not Know How Many A `for` loop needs to know how many times before it starts. Sometimes you do not, and then you want the other kind. [source,lua] ---- x = 0 while x < overlayGetWidth() do overlayPlot(x, 100) x = x + 10 end ---- A `while` loop checks a question before every trip round, exactly the question an `if` would ask, and keeps going for as long as the answer is true. When the answer is false it stops and carries on with the line after the `end`. If the answer is false the very first time, the body never runs at all. That example draws the same dotted line as the `for` with a step of ten, and it takes three lines to do what `for` did in one, so use `for` for that. Here is the difference that decides it: * Use `for` when the number of trips is known before you start. Forty stars. Every tenth pixel across. Every row of a grid. * Use `while` when you are waiting for something to become true and cannot say in advance how long it takes. Deal cards until the deck runs out. Keep asking until the player types something valid. Step through a list until you find what you were looking for. A `while` has three parts and you have to write all three yourself: set the variable up before the loop, test it in the `while`, and change it inside the body. The `for` loop does all three for you, which is why it is the one to reach for when it fits. Forget the third part in a `while` and the question never stops being true, which you will do on purpose at the end of this lesson. === A Star Made Out of Its Own Number Now the star field, and a problem that shapes the rest of part one. Each star needs an x and a y of its own, and you have no way to keep a hundred separate variables -- writing `star1X`, `star2X` and so on up to a hundred is worse than typing the hundred lines you were avoiding. So do not store them. Work each one out from the only thing that makes a star different from its neighbours: its number. [source,lua] ---- starX = (star * 37) % width ---- The `%` is the last of the arithmetic operators and it is the *remainder*: what is left over after dividing. Fifteen divided by four is three with three left over, so `15 % 4` is 3. Its useful property is that the answer can never be as large as the number on the right, so a remainder by `width` is always a position somewhere on the screen no matter how big the left-hand side gets. Star 1 goes to 37, star 5 to 185, star 10 to 10 -- 370 wrapped round past 360 and came back at the left. Thirty-seven is chosen because it shares no factor with 360, which is what stops the stars landing in neat stripes; try 36 instead and you will see the problem immediately. The downward drift is the same trick with a number that grows over time. [source,lua] ---- drift = 0 function onOverlayUpdate() drift = drift + 1 ... starY = (star * 61 + math.floor(drift * layer * 0.5)) % height ---- `drift` lives outside the function and climbs every frame, exactly like `frames` in lesson two. Adding it to every star's y moves the whole field down together, and the `%` brings a star that falls off the bottom back on at the top, forever, for free. `math.floor` throws away the fraction of a number and hands back the whole part below it: `math.floor(7.9)` is 7. Lesson two pointed out that Singe does that for you when it draws, and it does, but doing it yourself means the number you are working with is the number that appears on screen. That will matter in lesson seven, when two things have to agree about whether they are touching. Note that `layer` in there is a second loop variable. The stars are drawn by a loop inside a loop, and the outer one is not a row of a grid but a *distance*: three layers of stars, each drifting at its own speed and its own brightness, which is what makes a flat field of dots look like it has depth. === Chance Stars twinkle. Twinkling means a brightness that is a bit different every frame, and "a bit different" is `math.random`. [source,lua] ---- shade = math.random(60, 90) + layer * 55 colorForeground(shade, shade, shade) ---- `math.random(60, 90)` hands back a whole number from 60 to 90, both included, chosen afresh every time you call it. Call it with no arguments at all and you get a fraction between 0 and 1 instead, which is the form to use when you want something to happen one time in five. You do not have to set the generator up; Lua does that when Singe starts. Adding `layer * 55` lifts the near layers out of the dim range, so the far stars stay faint and the near ones are almost white. Equal red, green, and blue, as lesson three explained, is a shade of grey. Because `shade` is picked again on every frame for every star, each star flickers on its own, which at sixty frames a second reads as a twinkle rather than as a fault. === The Whole Thing [source,lua] ---- LAYERS = 3 STARS_PER_LAYER = 40 ACROSS_STEP = 37 DOWN_STEP = 61 LAYER_SHIFT = 13 LAYER_SPEED = 0.5 LAYER_BRIGHT = 55 width = overlayGetWidth() height = overlayGetHeight() drift = 0 function onOverlayUpdate() drift = drift + 1 overlayClear() for layer = 1, LAYERS do for star = 1, STARS_PER_LAYER do starX = (star * ACROSS_STEP + layer * LAYER_SHIFT) % width starY = (star * DOWN_STEP + math.floor(drift * layer * LAYER_SPEED)) % height shade = math.random(60, 90) + layer * LAYER_BRIGHT colorForeground(shade, shade, shade) overlayPlot(starX, starY) end end return OVERLAY_UPDATED end ---- A hundred and twenty stars, drawn by five lines, and the file is shorter than the one in lesson three. It is also as far as this approach goes, and it is worth being honest about why while you are looking at it. Every star in there is a formula. Nothing about a star is *remembered*: you cannot make one brighter than its neighbour and have it stay that way, you cannot knock one out of the sky, and you cannot have the player collect one, because there is nowhere to write down that anything happened to it. Star seventeen is not a thing. It is a number that briefly passes through a variable called `star`. The moment you want a hundred things that each remember something of their own, you need somewhere to put a hundred sets of facts. That is lesson six, and the star field is why it exists. === Try It . *More and fewer.* Try `STARS_PER_LAYER = 200`, then `4`. Then `LAYERS = 8` and watch what `layer * LAYER_BRIGHT` does to `shade` when the layer number gets big. The numbers `colorForeground` takes stop at 255. . *Change the spacing.* Set `ACROSS_STEP` to 36 and run it. The stars fall into stripes, because 36 divides into 360 exactly ten times. Put it back to 37 and try 71 and 90. . *Stop the twinkle.* Replace the `math.random` line with a plain `shade = layer * 70` and compare. Decide which you prefer; there is no right answer, and noticing that you have a preference is part of the job. . *Make them fall upward.* One character. . *Draw the grid as well.* Put the nested grid loop from earlier back in, after the stars, in a dim colour. Two nested loops in one function, drawing two different things, and the second one does not disturb the first. === Break It on Purpose Every error so far has been a message. This one is not, and that is exactly why you should meet it while you are expecting it. Put this in your `onOverlayUpdate`, run it, and read the next paragraph before you do anything else. [source,lua] ---- x = 0 while x < width do overlayPlot(x, 100) end ---- The line that moves `x` along is missing, so `x` stays at zero, so `x < width` never stops being true. The loop goes round for ever, plotting the same pixel, and it is inside `onOverlayUpdate`, so `onOverlayUpdate` never returns. Singe is waiting for your function. It cannot draw, it cannot read the keyboard, and it cannot quit. What you see is a window that stops updating and stops responding. Escape does nothing. Q does nothing. Your desktop may grey the window out or offer to force it closed, and it may say the program is not responding, which is true. Go to the terminal you started Singe in and press *Ctrl* and *C* together. That kills it. Then take the loop out and save. There is no message because nothing went wrong, as far as the computer is concerned. You asked for a loop that runs while `x` is less than the width, and that is precisely what you got. This is the one kind of mistake the error messages cannot help you with, and the cure is a habit: every time you write a `while`, write the line that changes the variable before you write anything else in the body. === What You Learned * `for name = first, last do ... end` runs its body once for each number from first to last, both included. * A third number is the step, and it may be negative to count down. * The loop variable exists only inside the loop, and the body should use it. * `for` needs a `do`, and every loop needs an `end`. * A loop inside a loop runs the whole inner loop for each trip round the outer one, which is how you fill a rectangle. * `while question do ... end` repeats for as long as the question is true, and you have to change something in the body yourself. * Use `for` when you know how many times, and `while` when you are waiting for something to become true. * `%` is the remainder, and it keeps a growing number inside a range. * `math.random(a, b)` gives a whole number from a to b; `math.floor` throws away the fraction. * A loop that never ends freezes the whole game with no error message, and *Ctrl* and *C* in the terminal is how you get out. === Next Time Look at that `onOverlayUpdate` again. It draws stars, it works out positions, it picks colours, and it is starting to be a function that does four jobs instead of one. In lesson five you write functions of your own, give them arguments the way Singe's functions take them, and get answers back out of them -- and the star field becomes one line that says `drawStars()`.