Fundamentals of Lua (Part 2)

In this article, I continue with my fundamentals series by describing control flow constructs in Lua. In Part 1, I explained variables and the values that go in them, along with a means to manipulate values using operators. Let’s use the variables and make decisions in our code with them.


Comparison Operators

I conveniently left out comparison operators from the previous article since it wouldn’t be immediately obvious what they’re used for. These operators compare two values and give back a boolean (true or false). They compare! You’re familiar with some of them from math class (greater than, less than and their equal-to variants):

-- Just some variables for the purpose of demonstration
friends = 15
gold = 500
guesses = 3
name = "Ozzy"
-- Comparison operators compare two values
print(friends > 10) --> true. Greater than
print(gold >= 250) --> true. Greater than or equal to
print(guesses < 5) --> true. Less than
print(guesses <= 3) --> true. Less than or equal to
print(name == "Ozzy") --> true. Equality
print(name ~= "Joe") --> true. Inequality
-- The "not" operator flips a boolean:
print(not true) --> false
print(not false) --> true

Notice how equality is tested with double equals signs. The single equals sign is reserved for assignment. Inequality uses a tilde (~ ). All the other comparison operators are used with numbers. We’ll use these expressions in the print  calls for the following constructs.

Comparison operators have a lower precedence than “and” and “or” boolean operators, which were covered in part 1. Use these to test two expressions at once without the need for parenthesis:

-- Scenario: testing if we can buy an item (have money and space in backpack)
gold = 300
backpack_space = 5
cost = 250
size_of_item = 10
print(gold >= cost and backpack_space >= size_of_item) --> false; 5 < 10
-- Another scenario: testing if we've completed a quest
princessIsSaved = true
packageDelivered = false
print(princessIsSaved and packageDelivered) --> false
-- One more scenario: 
strength = 10
agility = 5
print(strength > 8 or agility > 8) --> true, both conditions hold!

If-Then

If-then checks an expression and runs a block of code once if that expression is non-false and non-nil (i.e. it holds).

First up is the if-then-else construct.  It checks if the expression between if and then evaluates to a non-nil and non-false value (i.e. the condition holds). If it does the code after then is run. If not, it skips forward and runs the code between else and end (or past the block entirely if there’s no else). Here’s an elementary example that displays a message with a simple condition:

strength = 12
if strength > 10 then
	print("You are very strong!") --> Will print because 12 > 10
end

Adding an else block allows us to run another block of code if the condition didn’t hold.

Here’s an example of buying an item – we need enough gold, so we use the gold >= swordCost  expression inside if-then to test this. If this condition holds, we subtract the gold and set the hasSword  variable to true. Otherwise, print how much gold the player needs by subtracting.

gold = 300
hasSword = false
swordCost = 250
if gold >= swordCost then -- Expression checks if we have enough gold
	gold = gold - 250
	hasSword = true
	print("You bought the sword! You have " .. gold .. " gold left.")
else
	print("You cannot afford the sword. You need " .. (swordCost - gold) .. " gold")
end

Use any number of elseif blocks to check multiple conditions sequentially.

What if you need to check multiple cases? For that, you can use any number of elseif (one word). Consider this example of checking what key a player pressed:

keyPressed = "s"
if keyPressed = "i" then
	print("Opening the inventory window...")
elseif keyPressed == "s" then
	print("Opening the spells window...")
elseif keyPressed == "c" then
	print("Opening the character window...")
else
	-- else-block runs when if and elseifs conditions fail
	print("I don't know what you just pressed.")
end

The if condition is checked first, followed by the elseif in order. If just one condition holds, it runs that block and jumps over the rest.


While-do

A while-do will check a condition and run some code much like if-then, but it also checks again repeatedly after each time the code is run.

Next up is while-do. This works just like a simple if-then-end works, but when the block is done running it loops back to the condition again and again until the condition is false. This block of code counts from 1 to 10 using a while-do:

counter = 1
while counter <= 10 do
	print(counter) --> 1, 2, 3...10 etc
	counter = counter + 1 -- Increment the counter
end
print("I'm done counting now")

Use break to jump out of the loop early.

You can also stop the loop early by using break. This is very useful if your stopping condition is somewhat complex or if your algorithm has special stopping conditions that would otherwise be messy to put between while-do. In this example, we’ll stop early when counter  hits 7:

num = 1
while num <= 10 do
	print(num)
	num = num - 1
	if num == 7 then
		break -- Jump out of the loop immediately
	end
end
print("I'm done counting now")

Repeat-Until

A repeat-until block works like while-do, except that it checks after running the block of code instead of before. It always runs at least once.

As the name implies, repeat-until defines a block of code and tests an expression at the end of the block. If the expression holds (like in if-then), the entire block is repeated once more. It continues to do so until the expression is false.

The below example uses repeat-until to display a progression of the Collatz Conjecture. In a nutshell: start with a number. If it is even, divide it by two. If it is odd, multiply by three and add 1. Continue until you reach 1. Is there a number that will never reach 1? Find out by running the code below:

-- Simulate a progression of the Collatz conjecture.
-- https://en.wikipedia.org/wiki/Collatz_conjecture
num = 9 -- Can you find a number that will never reach 1?
repeat
	print(num)
	-- To test cardinality, use the modulo operator (%)
	-- to take the remainder of a division by 2
	if num % 2 == 0 then
		-- num is even
		num = num / 2
	else
		-- num is odd
		num = num * 3 + 1
	end
until num == 1
print(num) --> 1
print("Done")

There’s another control structure that’s a bit more complicated, called the for loop. There’s two variants: numeric for and generic for. I’m saving this for later since it is a bit more complicated in Lua than in other languages. For now, you can accomplish the same tasks using if-thenwhile-do, and repeat-until.

Conclusion

We now have a means of performing complex logic in Lua code now.  We’re not yet Turing complete, but we’re getting close. You should experiment with these constructs to really understand how they work.

Exercises
  1. It’s a hot day, and staying hydrated is important. You have a cylindrical water bottle with a height and radius. You plan on going on an adventure which requires you to drink 100 units of water. Given the dimensions of some bottle, print “Hydrated” if the bottle will hold enough water for you to stay hydrated during the adventure. Otherwise, print “Thirsty”.  Solution
  2. You have a little brother who is an annoying copycat. When you say a word, he’ll repeat it once for every letter in the word. Hint: Use string.len(s)  to get the length of a string. Ex: string.len(“Hello”)  will give 5. Given a word you’ve said, print what your brother would say. Solution
  3. Imagine a game where a player starts at level 1 and gains a level when they gain 10 times their current level in experience. Given a player’s total experience, calculate what level they are. Solution

Author: Ozzypig

Roblox developer and computer scientist. I love all things coding and game design!