Back

Activity 1.4: Numbers and Math

divider

The Idea

The arithmetic is the part of Lua with no surprises in it. Five operators, and they do what you expect.

main.lua
print(7 + 2)
print(7 - 2)
print(7 * 2)
print(7 / 2)
print(7 % 2)
Output
9
5
14
3.5
1

Note that 7 / 2 gives 3.5, not 3. Division in Lua never throws away the fraction. If you want a whole number you have to ask for one, which is what math.floor is for.

The one place two Luas disagree

This course runs on plain Lua now, and on LÖVE in Unit 2. Those are not quite the same interpreter, and here is the only difference you will actually see:

main.lua
print(10 / 2)
print(2 ^ 8)
Output — Lua 5.4
5.0
256.0
main.lua
print(10 / 2)
print(2 ^ 8)
Output — LÖVE
5
256

Same number, different spelling. Lua 5.4 tracks whether a number is a whole number or a decimal and prints 5.0 to tell you a division happened. Older Lua, which is what LÖVE runs, has only one kind of number and prints 5. Nothing is wrong with either. Do not be alarmed when the same code prints differently in Unit 2.

If you ever need the display to match exactly, run the value through math.floor or string.format and both will agree.

The math library

Anything beyond the five operators lives in math. These six cover almost everything you will need in Unit 2.

main.lua
print(math.floor(7 / 2))
print(math.ceil(3.2))
print(math.max(3, 9, 5))
print(math.min(3, 9, 5))
print(math.abs(-4))
print(math.sqrt(2))
Output
3
4
9
3
4
1.4142135623731

math.max and math.min take as many numbers as you want to give them. In a game they are how you stop a value escaping — health that cannot go below zero is math.max(0, health).

Text that looks like a number

Lua will quietly convert a numeric string when you do arithmetic on it. This is the reverse of the JavaScript behavior you may have been burned by, and it is worth seeing side by side with ...

main.lua
print("42" + 1)
print("42" .. 1)
print(tonumber("42") + 1)
print(tonumber("hello"))
Output
43
421
43
nil

Do not rely on that first line. Convert on purpose with tonumber, which hands back nil when the text is not a number rather than crashing. That nil is your chance to notice bad input before it spreads.

Random numbers, and the line you must not forget

math.random(1, 6) gives a whole number from 1 to 6, and both ends are included.

main.lua
math.randomseed(os.time())
print(math.random(1, 6))
print(math.random(1, 6))
Output — yours will differ
4
1

That first line matters more than it looks. Without math.randomseed(os.time()), LÖVE hands you the exact same "random" numbers every single time you run the program. Lua 5.4 seeds itself and hides the problem; Unit 2 will not. Seed once, at the top of the file, and it is correct everywhere.

divider

Build

Start a fresh main.lua. You are building a jump computer: how much fuel a trip needs, and how many tanks that means loading.


Task 1: Work out the fuel

  • Store a distance and a fuel cost per unit of distance.
  • Multiply them into a totalFuel variable.
  • Print the distance and the fuel needed.
main.lua
-- main.lua - Skynest jump computer
local distance = 47
local fuelPerUnit = 2.5
local totalFuel = distance * fuelPerUnit
print("Distance:", distance)
print("Fuel needed:", totalFuel)
Output
Distance: 47
Fuel needed: 117.5

Task 2: Turn that into tanks

  • Add a tank size and divide the fuel by it.
  • Print how many tanks to load — you cannot load part of a tank, so round up.
  • Print how many will be completely full. Round down.
  • Print what is left over using %.
main.lua
-- main.lua - Skynest jump computer
local distance = 47
local fuelPerUnit = 2.5
local tankSize = 40
local totalFuel = distance * fuelPerUnit
local tanks = totalFuel / tankSize
print("Distance:", distance)
print("Fuel needed:", totalFuel)
print("Tanks to load:", math.ceil(tanks))
print("Full tanks:", math.floor(tanks))
print("Left over:", totalFuel % tankSize)
Output
Distance: 47
Fuel needed: 117.5
Tanks to load: 3
Full tanks: 2
Left over: 37.5

Task 3: Add a noisy sensor

  • Seed the generator at the top of the file. Do this first — it is the habit, not the line, that matters.
  • Generate a drift between -5 and 5 and print it.
  • Print the distance with the drift applied.
  • Run it several times. The numbers should change. Then comment out the seed line, run it several more times, and see what happens instead.
main.lua
-- main.lua - Skynest jump computer
math.randomseed(os.time())
local distance = 47
local fuelPerUnit = 2.5
local tankSize = 40
local totalFuel = distance * fuelPerUnit
local tanks = totalFuel / tankSize
print("Distance:", distance)
print("Fuel needed:", totalFuel)
print("Tanks to load:", math.ceil(tanks))
print("Full tanks:", math.floor(tanks))
print("Left over:", totalFuel % tankSize)
local drift = math.random(-5, 5)
print("Sensor drift:", drift)
print("Corrected distance:", distance + drift)

Challenge (Optional)

  • Print the fuel figure to exactly one decimal place using string.format and the %.1f placeholder.
  • Clamp the corrected distance so it can never drop below zero, in one line, without an if.
  • Roll two dice and print their total. Then work out on paper why math.random(2, 12) is not the same thing.
divider

Check Your Work

The first five lines should match exactly. The last two will differ every run — that is the point of them.

Output
Distance: 47
Fuel needed: 117.5
Tanks to load: 3
Full tanks: 2
Left over: 37.5
Sensor drift: -3
Corrected distance: 44

That is the whole of Lua's data toolkit except one thing. Numbers, strings, booleans, and nil are four of the five types you will ever use. The fifth is the table, it does the work of arrays, dictionaries, objects, and modules all at once, and it is what the rest of this unit is about.