Class 1 - Hello World of Games
Play homework
- Colossal Cave Adventure (1996 DOS version on Internet Archive)
Topics
- REPL
- Strings
- Variables
- Functions
- Comments
- Booleans
- Conditionals
- While loop
- Input
- Random number generation
Description
This course teaches programming from the ground up in the context of Lua and LÖVE. It teaches basic computer science and software building skills along the way, but more importantly, teaches you how to teach yourself and find out how to go about solving a problem or building a solution. Tools come and go, so the goal is to teach things of value with less focus on the programming language and other tools used to build the software.
This first session will help ground the course and get you situated into the learning environment and get your feet wet while writing your first code.
The goal of this week is to teach the most necessary building blocks of programming. By the end you will be be able to build basic programs which we will apply with exercises in the following weeks.
Class notes
Interactive coding
What’s a REPL?
Programming doesn’t take much effort beyond loading up a REPL and just typing. What is a REPL? It’s an interactive window you can type code into and it spits out the results on screen when you hit enter. It stands for Read-Evaluate-Print-Loop. These are the 4 things the REPL does:
- Read the code that was just typed
- Evaluate, or process the code down into a result
- Print, or spit out the result
- Loop… do everything again and again until the programmer is done
It’s actually simpler than it sounds. Let’s go to a website with a REPL and try it out: https://repl.it/languages/Lua
You will see two window panes on the website: a light side on the left and dark side on the right. The right-side is the REPL and is what we’re interested in for now. It has a lot of information that isn’t necessarily useful to us at the moment. Something similar to this:
Lua 5.1 Copyright (C) 1994-2006 Lua.org, PUC-Rio
[GCC 4.2.1 (LLVM, Emscripten 1.5)] on linux2
This is just telling you what programming language this REPL is loading, in this case, Lua. If you click inside the window pane and start typing you will see your text appear.
Let’s try typing some code for the REPL to Read. You already know some code if you know arithmetic. Type:
2 + 2
Then hit ENTER and immediately the REPL will Print out:
=> 4
A lot happened very quickly.
After hitting ENTER, the REPL, Read the line 2 + 2
, it Evaluated the value of that statement to be 4
, it Printed 4 on the screen for you, then Looped back to a new line to await your next command.
Try out some more arithmetic.
Multiplication:
2 * 3
Subtraction:
2 + 2 - 4
Division:
6 / 2
You can use parenthesis to tell it which order to do the operations:
(2 + 2) * (3 + 1)
Which gives different results than:
2 + 2 * 3 + 1
If you give the REPL a single number:
12
It will give you back 12
, because this can’t be simplified down any further.
You can also do exponents using the ^
(caret) symbol:
2^4
Numbers are a type of data, and +
, -
, /
, *
, ^
, %
are operators.
Statements such as 2 - 2
and 23 * 19
are all operations.
One last arithmetic operation we’ll cover is modulo, which is done with the modulus operator.
The modulus operator is represented in most languages as a %
(percent) symbol:
8 % 3
Modulus operations are quite common in software and computer sciences.
The way it works is you take the 2nd number and subtract it from the bigger number as many times as possible until the 2nd number is bigger than the 1st.
The result is what’s left of the 1st number.
With 8 % 3
, if you keep subtracting 3
from 8
then you end up with 2
left.
A real world example is time elapsing on an analog clock. Imagine the face of a clock with the hour hand on noon. If 25 hours pass then the hour hand goes all the way around twice and ends on 1. That would be equivalent to writing:
25 % 12
=> 1
The hour hand resets every time it passes 12, so 13 % 12
, 25 % 12
, and 37 % 12
would all equal 1
.
Likewise, 10 % 4
results in 2
because 4 goes into 10 twice, and leaves a remainder of 2.
Exercises
- Try typing different modulo operations in and guessing what the answer will be.
- Try using negative numbers (
-3 + -2
). - Try using a set of parenthesis inside another set of parenthesis. Does it behave as you expect?
- After running through all the exercises press the ‘up’ key in the REPL. What happens and how can this speed up your work?
Strings
Numbers are one type of data that can be operated on. Let’s explore another data type within the REPL. Take a set of quotes and put some text in it and hit ENTER:
"hello"
The REPL will print hello
back to you.
This is a string.
A string is a set of characters (letters and symbols) stringed together as one single piece of data.
This string is made of 9 characters:
"H-E-L-L-O"
Like numbers, there are operators to make strings play with each other.
The concatenate operator (..
) combines strings together:
"hello" .. "world"
What’s the result? Notice that the resulting string has no space between the two words. If you wanted a space, you would have to put one in the quotes to be part of the operation:
"hello " .. "world"
You could even make a separate string with the space in it:
"hello" .. " " .. "world"
Strings can have any characters in them that you want.
"abc" .. "123"
"Япо́нский" .. "ロシア語!!"
Exercises
- Try using an arithmetic operator on strings
"hello" / "world"
. What happens? - Try using the concatenate operator (
..
) on numbers (1 .. 1
).
Nil and variables
Data, or the lack thereof
Humans have different ways of representing a lack of data.
If there are no sheep to count then we have zero sheep.
If there are no words on a page then the page is blank.
In a computer we may represent the number of sheep as 0
or the missing words on a page as an empty ""
.
These are still data though… a number and a string.
In software when you want to represent a lack of data we have:
nil
Sometimes called null
or undefined
data in other languages.
It’s seemingly useless.
You can’t use operators on nil
.
nil + nil
This will print an error like it did when you tried doing arithmetic on strings.
Let’s take a look at variables and we’ll discover the purpose of nil
.
Variables
Sometimes you want to write out data, but you want that data to be easy to change. Variables let you give data a name to reference. Here’s an example to try:
name = "Mandy"
"hello my name is " .. name
Since you told it what name
is, it knows what value to add to the string "hello my name is "
.
If you type:
name
…and hit ENTER, it will print out the value that belongs to this variable to remind you.
The =
(equal) sign tells Lua that you want to assign a value to the given name/variable.
You can change the value of a variable and get different results:
name = "Jeff"
"hello my name is " .. name
Assignment isn’t the same as it is in Algebra.
You can change the value of a variable multiple times.
We can tell name
that it equals itself with some additional information concatenated to it:
name = "abc"
name = name .. "def"
name
You can assign any type of data to a variable, including numbers:
name = "Jeff"
age = 16
"hello my name is " .. name .. " and I am " .. age .. "."
You can change numbers after assignment too:
age = 16
age = age * 2
"my age doubled is " .. age
So, what if you type in a made up variable name?
noname
You will see it has nil
, or no data yet.
If you try to use nil
in your string operation you will get an error:
"hello my name is " .. nil
[string "return "hello my name is " .. nil"]:1: attempt to concatenate a nil value
"hello my name is " .. noname
[string "return "hello my name is " .. noname"]:1: attempt to concatenate global 'noname' (a nil value)
Try assigning a value to a variable name:
best_color = "purple"
then assigning that variable data to another:
worst_color = best_color
worst_color
You’ll see that both variables now have the value "purple"
.
Variables can have names made up of letters, numbers and underscores (_
).
Variable names cannot begin with a number though, otherwise it will think you’re trying to type in number data.
Here’s some examples of valid variables:
my_dog = "Poe"
myDog = "Zia"
DOG3 = "Ember"
Exercises
- Try out different variable names. Try a few invalid variables names too just to see what the error message looks like. It’s important to see error messages and understand them. They help you understand how a program breaks so you can fix it.
Using functions
Most programming languages come with some variables already defined for us. Lua has many, so let’s type one in and hit ENTER to see what the value is:
string.reverse
=> function: 0x2381b60
Oh my.
So “function” is another data type, but what is 0x2381b60
?
It’s just telling you where in the computer’s memory that function exists, just in case you wanted to know.
Functions work very differently than numbers in strings.
Essentially functions are pre-defined instructions that tell the program how to do different things.
They take data and return back different data.
Let’s see how to give this function data:
string.reverse("hello")
=> olleh
At the end of the function’s variable name, string.reverse
, we type a set of parenthesis, string.reverse()
, and put inside the parenthesis some data we want changed (string.reverse("hello")
).
Making the function run is often called invoking the function.
Having a function that reverses text in a string for us can be useful, and we can capture the return value (the results) of the function using a variable.
Try it out:
greeting = "hello, how are you?"
backwards_greeting = string.reverse(greeting)
backwards_greeting
=> ?uoy era woh ,olleh
It should be obvious from the name what that function’s purpose is. How about this one?
string.upper("hello, how are you?")
Now try capturing that value by assigning it to a variable:
greeting = "hello, how are you?"
shouting_greeting = string.upper(greeting)
crazy_greeting = string.reverse(shouting_greeting)
We can get crazier. How about invoking a function when invoking another function??
string.reverse(string.upper("hey"))
What’s happening here is the string is being uppercased by string.upper
but then the value from string.upper
is being reversed by string.reverse
as soon as it is done.
It’s just like in arithmetic when you have nested parenthesis.
The inner-most parenthesis are resolved before doing the outer-most parenthesis.
Let’s try one more function. This function has two parameters, meaning it accepts two pieces of data which it requires to work properly.
math.max(7, 10)
When giving more than one piece of data to a function, you need to put a comma (,
) between the parameters
These are great functions, but wouldn’t it be great if we could make our own? We’ll give it a shot in just a few pages.
Exercises
- See if you can figure out what
math.max
does. Give it different numbers and examine the result. - There is another function called
math.min
that also takes two numbers. What does it return?
Comments in code
Sometimes we might want to write a comment in our code– an explanation to a friend or our future selves on what the purpose of some code is.
Perhaps we want to write a note to ourselves to change something later.
Comments work very similarly in different languages so they’re pretty easy to read even if you don’t understand the programming language or the code itself.
Lua denotes a comment as --
and any text that follows it:
1 + 1
-- This is a code comment
1 + 2
-- This is another line of comments
3 + 4
These comments will be completely ignored by the computer and are meant for the human to read. Comments can also be on the same line as code. The computer will just ignore the rest of the line when it sees a comment starting.
1 + 1 -- This is my comment. This code adds some numbers together in case you didn't know!
You will see comments appear in future example code, so don’t let it surprise you!
Scripting and printing
Looking back at the website, (you bookmarked it, right?) we have been using the REPL window pane on the right, but haven’t talked about the pane on the left. This window is just a text editor. Instead of running the program with each line you type, it allows you to write multiple lines of code before executing it all. Let’s try typing something in it. Once you are done typing all the code you can click the “Run” button.
number = 4
number = number + 1
But when you click run, nothing happens. So let’s provide another statement to our program.
number = 4
number = number + 1
print(number)
Now when you click Run, the text 5
appears in the right-hand pane.
When you told it to run, it read and evaluated each line of the code in sequence.
You can print any type of data, not just numbers:
print("hello")
Remember those other functions we used before? You can write those inside of a print statement.
print(string.reverse("hello"))
We can even print functions themselves:
print(string.reverse)
And see a memory location of where that function exists:
function: 0x1795320
This can serve as a unique indentity for that function, which we’ll see more of in a later page.
Lua provides this print
function to allow us to poke around while our program is running.
We can print as many things as we want.
print("hello")
print("world")
Exercises
- When we pass data into a function, it is called an argument. We passed 1 argument into
print
but it can pass in two, or three, or more. What does it look like when you print multiple arguments? - When using a text editor along-side the REPL you can run the code without the mouse by pressing ‘command + enter’ on Mac and ‘Ctrl + Enter’ on Windows. Does this speed up your learning?
Scripting and printing
Looking back at the website, (you bookmarked it, right?) we have been using the REPL window pane on the right, but haven’t talked about the pane on the left. This window is just a text editor. Instead of running the program with each line you type, it allows you to write multiple lines of code before executing it all. Let’s try typing something in it. Once you are done typing all the code you can click the “Run” button.
number = 4
number = number + 1
But when you click run, nothing happens. So let’s provide another statement to our program.
number = 4
number = number + 1
print(number)
Now when you click Run, the text 5
appears in the right-hand pane.
When you told it to run, it read and evaluated each line of the code in sequence.
You can print any type of data, not just numbers:
print("hello")
Remember those other functions we used before? You can write those inside of a print statement.
print(string.reverse("hello"))
We can even print functions themselves:
print(string.reverse)
And see a memory location of where that function exists:
function: 0x1795320
This can serve as a unique indentity for that function, which we’ll see more of in a later page.
Lua provides this print
function to allow us to poke around while our program is running.
We can print as many things as we want.
print("hello")
print("world")
Exercises
- When we pass data into a function, it is called an argument. We passed 1 argument into
print
but it can pass in two, or three, or more. What does it look like when you print multiple arguments? - When using a text editor along-side the REPL you can run the code without the mouse by pressing ‘command + enter’ on Mac and ‘Ctrl + Enter’ on Windows. Does this speed up your learning?
Making functions
Functions are the third data type we’ve seen. We’ve accessed some variables where functions were defined for us and had a blast using them (I know I did). Functions are the building blocks of software. You can compose them then snap them together like Danish plastic blocks. It takes time to understand how they work and much longer to master their inner power. So without further ado, let’s see what they actually look like:
function()
return 4 + 4
end
Type it out in the text editor window and let us break this down line by line and word for word.
Whenever we type function()
we are beginning a new function.
The 2nd line is the body of our function where things happen.
The body of the function can be many lines long.
The body of the function could also be empty (but that’s not very useful).
On the last line of the function body we write return
which tells our function to stop running and to “return” data back to the main program.
Then on the third line, we’re telling the computer we’re done writing our function.
In order to use this example function, we should probably use a variable to give it a name:
add = function()
return 4 + 4
end
The first bit should be understandable.
We declared a variable called add
, then we assigned some data to it on the right of the equal sign.
In this case, our function.
Now it is ready to use.
add = function()
return 4 + 4
end
result = add()
print(result)
8
We’ve made our very own function with our very own name for it and even invoked it and got back data! If you instead got an error message, double check what you typed that nothing is missing. Error messages give you a line number of where to find the error that crashed the program.
Take a look for a minute at how we invoked our function:
add()
We typed out the variable name that our function is assigned to, followed by some parenthesis.
In those parenthesis is the data that we passed into our function… wait a minute the parenthesis are empty.
We didn’t pass any data into our function.
Whenever we called those other functions we passed in data, like when we passed "hello"
into string.reverse("hello")
.
What if we modify our line where we invoke our function and give it some data?
add = function()
return 4 + 4
end
result = add(16)
print(result)
It seems it always returns 8
no matter what arguments we try to pass in.
We need to rewind to the first line of our function and take a close look at this bit:
add = function()
The ()
at the end of function()
is where we tell our program how many arguments we are accepting.
If the parenthesis are empty, then our function is ignoring all arguments and will likely always return the same result.
Let’s tweak the function slightly and give it one parameter with the name a
.
Let’s also tweak the second line while we’re at it:
add = function(a)
return a + 4
end
result = add(16)
print(result)
20
Now when we pass in different numbers, we get different results:
add = function(a)
return a + 4
end
print(add(16))
print(add(12))
To complete this function, let’s give it a second parameter of b
and modify the return statement in the function body:
add = function(a, b)
return a + b
end
print(add(16))
print(add(12))
If we try and run the code now, we’ll get another error:
[string "add = function(a, b)..."]:2: attempt to perform arithmetic on local 'b' (a nil value)
Let’s read this error carefully.
It is saying inside the square brackets that an error occurred when using the function we defined (add = function(a, b)...
).
To the right of the square brackets it is saying line 2 (:2
) of our text is the particular location of the crash.
To the right of the line number is what happened that made it crash.
It tried to perform addition with a + b
but the value of b
was nil.
We stated that our function requires two parameters now, a
and b
, and our program will crash if we try and invoke the function with only one parameter.
Let’s modify the lines where we invoke the function to give it two arguments each time we invoke it:
add = function(a, b)
return a + b
end
print(add(16, 10))
print(add(12, 2))
Great, everything is working again! With the experience of our first, fully-functional function, we can now start treading the waters of this great world.
Exercises
- To get used to writing functions, try writing some complimentary functions named
subtract
,multiply
,divide
, ormodulate
(modulus). - Make a
concatenate
function that accepts 2 strings and returns 1 combined string. - Try making a function that takes 3 or more parameters.
Booleans
Data types are like elements on the periodic table. The more elements you have the more chemicals can create. Luckily there aren’t as many data types as there are elements. In fact we’ve learned almost all of them. There are only two possible booleans:
true
and
false
That’s right. And you can assign them to variables just like numbers, strings, nil, and functions:
myboolean = true
print(myboolean)
The cool thing with numbers and strings is you can use them to create statements that can be evaluated as true
or false
.
Let me give an example by introducing some new operators.
Try these out in the REPL:
5 > 3
=> true
5 < 3
=> false
“5 is greater than 3” is a true statement so it returns a true
boolean.
Naturally, “5 is less than 3” is a false statement and returns false
.
We can check to see if two numbers are equal in value:
number = 5
number == 5
=> true
By using a double equal (==
) we can compare the equality of two numbers.
This also works for strings:
"hello" == "hello"
=> true
"hello" == "HELLO"
=> false
For strings, often time you will see single quotes ' '
(apostrophe) used instead of regular quotes (sometimes called double quotes) wrapper around the text.
Lua doesn’t care as long as the text inside both strings are identical.
We can prove that with an equality check:
'hello' == "hello"
=> true
Anyways, you can also do the inverse of an equality check and check for inequality (if two things are not equal):
5 ~= 3
=> true
"HELLO" ~= string.upper("hello")
=> false
Now let’s dig in a little deeper with two more operators.
First is the and
operator:
3 < 4 and 4 < 5
=> true
This reads out almost as plain English. 3 is less than 4 and 4 is less than 5. This is a logically sound statement so it evaluates to true. Just to be clear on what’s actually going on here though, let’s break it down. What we said is being grouped into 3 separate operations:
(3 < 4) and (4 < 5)
The two sets of parenthesis are evaluated first and internally the computer breaks these two operations down to:
(true) and (true)
True and true are both true. This sounds silly, but it is indeed logically sound. Let’s try one more just to get the hang of it:
"hello" == "hello" and 6 > 10
Finally, let’s try one more operator to put a bow on things.
Sometimes we don’t care that both operations are correct.
We only care if one or
the other is correct.
4 == 10 or 4 ~= 10
=> true
1 > 100 or 12 == 12 or "hello" == "bananas"
=> true
As long as one of the operations is correct, the entire statement is logically true.
With the introduction of true
and false
we’ve brought in a lot of new operators: “greater than” (>
), “less than” (<
), “equal” (==
), “not equal” (~=
), “and” (and
), and “or” (or
).
Trivia
Booleans get their name from George Boole who invented boolean algebra, which we’ve just seen a little bit of.
Exercises
- Try writing different statements with all the new operators.
- Try using two
and
operators in the same statement and see if you can make it evaluate totrue
. - Try out these two bonus operators with some numbers: “greater than or equal to” (
>=
), and “less than or equal to” (<=
).
Flow control
Typically the computer starts at the top of our script and reads each line down in a sequence. We make the programs jump around with functions in the mix Try this out in the text editor:
print("I'm called 1st")
add = function(a, b)
print("I'm called 5th")
return a + b
end
subtract = function(a, b)
print("I'm called 3rd")
return a - b
end
print("I'm called 2nd")
subtract(16, 10)
print("I'm called 4th")
add(12, 2)
We have a function that is saved to the variable add
but it isn’t invoked until further down in the code.
So in a sense our program has worked its way down the page then jumped back up to the function and worked its way through the body of the function then picked back up where it was before.
In a similar fashion, we can make our program take one path or another depending if the data is true
or false
.
noise = function(animal)
if (animal == "dog") then return "woof" end
return ""
end
print(noise("dog"))
print(noise("rabbit"))
Let’s analyze this function line by line.
The function is called noise and takes an animal name (string) as a parameter.
On the next line it says if “animal is dog” is true then return something special.
We put an end
at the end of our statement to make it obvious to the computer.
If the statement was false, then "woof"
does not get returned.
Instead an empty string (""
) gets returned.
When we invoke the function with the argument “dog” then we get back “woof!”.
With “rabbit” we get back silence.
Maybe the rabbit doesn’t want the dog to hear where she is.
Let’s make our function more versatile by adding more animals:
noise = function(animal)
if animal == "dog" or animal == "wolf" then return "woof" end
if animal == "cat" then return "meow" end
return ""
end
print(noise("dog"))
print(noise("cat"))
print(noise("rabbit"))
print(noise("wolf"))
We have branching paths happening within our function. If we were to map out these branches it may look something like:
|
+--> "woof"
+--> "meow"
|
+--> ""
There’s no requirement that a statement has to be all written out on one line. Sometimes when doing multiple things inside an if statement we may want to put it on multiple lines:
if my_age > 17 then
print("You're an adult!")
print("Get a job!")
end
Similar to functions having bodies, everything between then
and end
is considered the body of the if statement.
Sometimes it is necessary for our branches to have forks within them.
Let’s say our function takes a language as a second, optional parameter:
noise = function(animal, language)
if animal == "dog" or animal == "wolf" then return "woof" end
if animal == "cat" then return "meow" end
if animal == "bird" then
if language == "spanish" then return "pío" end
return "tweet"
end
return ""
end
print(noise("dog"))
print(noise("rabbit"))
print(noise("bird"))
print(noise("bird", "spanish"))
The if statement for checking if the animal is a bird is 4 lines long.
Once we find out that the animal is a bird, while still in the body of the if statement, we stop to check and see if the language is set to Spanish.
If it is, we end up inside an if statement within an if statement!
Otherwise we’ll return "tweet"
if the language isn’t Spanish.
Maybe mapping out the paths will clear things up:
|
+--> "woof"
+--> "meow"
+-----> "pío"
| |
| +--> "tweet"
|
+--> ""
Our code can get unreadable very quickly if we start nesting if statements inside each other. Fortunately doing so isn’t usually necessary.
Let’s talk about another aspect of if statements. Suppose I have two branches of code that are opposite of each other:
if daytime == true then
thermostat = 71
end
if daytime == false then
thermostat = 68
end
Rather than writing this out as two if statements and checking the value of daytime twice, I can take advantage of the keyword else
:
if daytime == true then
thermostat = 71
else
thermostat = 68
end
That way if daytime
is not true
, it will default to the second branch.
You could read this off almost like a sentence:
“If daytime is true then set the thermostat to 71, otherwise set the thermostat to 68.”
Not having to check things twice when doing computations saves us time and makes our program run more efficiently.
Since daytime
is a boolean in this case, we don’t need to check if it is true or false.
We can just pass it to the if statement to be checked for true
/false
and make our operation even simpler.
if daytime then
thermostat = 71
else
thermostat = 68
end
Better.
“If daytime then set thermostat to 71, otherwise set thermostat to 68.”
There’s one more feature of if statements we should discuss.
If there is another condition you need to check, maybe several more, you can use the elseif
keyword.
It looks something like this:
color = "green"
if color == "blue" then
print("That's my favorite color!")
elseif color == "green" then
print("Very subtle choice. I like it.")
elseif color == "pink" then
print("Nice, bold choice.")
else
print("I don't think that color would match your shoes.")
end
Try it out!
- The beginning of the if statement…
if color == "blue" then
… is false. This code gets skipped over. - Then the next part of the if statement…
elseif color == "green" then
… is true so that section of code underneath it…print("Very subtle choice. I like it.")
is ran. - The rest of the if statement is skipped without checking if its true or not. So
elseif color == "pink" then
/else
are never processed.
Exercises
- Write out a function that takes 1 parameter named “sides”. Make the function return the name of the shape depending on the number of sides (for instance, “triangle”). Try to make the if statement include an
else
at the end to account for everything else that the if doesn’t.
While
Another way to check conditions is with the while
keyword.
while 1 + 1 == 2 do
print("My math is correct!")
end
While a condition is true, the body (everything between the do
and end
) will be run repeatedly and not stop.
So if you tried to run that bit of code, your screen probably went crazy printing over and over in a never-ending loop.
We need to make sure the condition can get changed so we’re not stuck in a never-ending loop.
Let’s write a loop we can escape out of.
boolean = true
-- This condition will get checked twice. The first time it
-- is checked it will be true and the body of the while-loop
-- will be run. The second time the condition is checked,
-- our boolean will be false and the while-loop won't be run again!
while boolean do
print("Switching boolean to false.")
boolean = false
print("Boolean has been set to false.")
end
print("We made it out of the loop!")
Understanding that we can change the while condition from inside the body of the loop, we have the power to write programs that end exactly when we want them to. Can you guess what this will do when we run it?
countdown = 10
while countdown > 1 do
print(countdown .. "...")
-- This line is critical to make our number shrink.
countdown = countdown - 1
end
print("Blast off!")
…And remember to use a >
and not a <
, or your loop may never run.
Exercise
- Come up with your own idea for a while loop.
Type checking
Lua doesn’t care what type of data a variable has.
data = 12
data = "hello"
data = true
To this end, we can use the type
function to check what kind of data a variable is holding.
type(data)
=> boolean
We can check the type of function:
type(string.reverse)
type(type)
We can also use it to check what type of data a function is returning back to us:
type(string.reverse("hello"))
=> string
type(type(12))
=> string
Converting data types
We’ve already seen data type conversion previously when we took numbers and an operation in, transforming that into a true or false statement.
type(12 > 3)
=> boolean
There are also ways to convert between numbers and strings using tonumber
and tostring
.
number = tonumber("24")
print(type(number))
string = tostring(number)
print(type(string))
number
string
Interesting but maybe less useful, you can convert other data types to string:
print(tostring("already a string"))
print(tostring(true))
print(tostring(nil))
print(tostring(tostring))
Exercises
- Which of these strings can be converted to a number successfully?
"001"
,"7.12000"
," 5 "
,"1,943"
First game
Let’s learn about a few new functions and then we’ll be able to write our first game!
Reading input
Not only can our program print out data, but using the function io.read
it can take data too.
This function doesn’t need any arguments because it will prompt us in the window on the right for us to type in data.
print("Enter your name:")
name = io.read()
print("Your name is " .. name .. ".")
After you click “Run”, the program will pause when it runs io.read()
.
Type your name and hit ENTER and look, the program prints back out the name you gave it.
Notice the last print statement. We combined the name with two other strings to form a sentence.
You can prompt the user multiple times if you need to get additional information:
print("Enter your name:")
name = io.read()
print("What's your favorite food?")
food = io.read()
print("Your name is " .. name .. " and your favorite food is " .. food .. ".")
One limitation with doing this is the data will always come in as a string:
print("What's your favorite number?")
data = io.read()
print(type(data))
string
In the last section we talked about converting data between different types. If we wanted to find out whether your favorite number is odd or even, we would need to convert it to an actual number to perform operations on it. Type this in your text editor and run it:
print("What's your favorite number?")
data = io.read()
number = tonumber(data)
-- If the user gave us an answer that isn't a
-- number, then the value of "number" is nil.
if number == nil then
print("Invalid number.")
elseif number % 2 == 0 then
print("Your number is even.")
else
print("Your number is odd.")
end
Random number
Many languages give us access to a random number generator.
Randomness is how we generate secure passwords and keys in the real world.
To generate a random number in Lua, we use math.random
:
math.random(100)
=> 63
This generates a random number between 1 and 100. Except, if you run the program repeatedly you may notice that it spits out the same number. That’s because nothing in the computer world is random. If we fed in random noises through a speaker or white noise from an old television set then our computer could use this to generate random numbers. Since we don’t easily have access to those things, we need to seed Lua with some perceived randomness.
If we run os.time
we will get the computer’s current time in integer form:
os.time()
=> 1.529098167e+09
This number is hard enough to guess that it will work as a seed for our program.
Let’s take the system time and feed it in using math.randomseed
then from there, Lua will be able to generate a “random” number in the range we want (1-100).
seed_number = os.time()
math.randomseed(seed_number)
print(math.random(100))
=> 19
Success! It is generated different numbers each time we run it, with no pattern.
Putting it all together
I should probably explain what this game is. It’s quite simple. We want the computer to make up a number and the user has to guess what the number is. If they’re wrong, then we should give them a hint and make them guess again. We can take advantage of the while loop to make them continue guessing while their guess is incorrect.
-- The computer's secret number
math.randomseed(os.time())
number = math.random(100)
print("Guess my secret number. It is between 1 and 100.")
guess = tonumber(io.read())
-- While the user's guess is not equal to
-- the number, repeat the body of the loop.
while guess ~= number do
-- Give them some hints
if guess > number then
print("Your guess is too high.")
end
if guess < number then
print("Your guess is too low.")
end
-- Make them guess again and again until they get it
print("Guess again:")
guess = tonumber(io.read())
end
-- Winning message
print("You guessed correctly! The number was " .. number .. ".")
Let’s re-factor one bit of this code to make it easier to read.
When we talked about if statements, remember the keyword else
?
-- The computer's secret number
math.randomseed(os.time())
number = math.random(100)
print("Guess my secret number. It is between 1 and 100.")
guess = tonumber(io.read())
-- While the user's guess is not equal to
-- the number, repeat the body of the loop.
while guess ~= number do
-- Give them some hints
if guess > number then
print("Your guess is too high.")
else
print("Your guess is too low.")
end
-- Make them guess again and again until they get it
print("Guess again:")
guess = tonumber(io.read())
end
-- Winning message
print("You guessed correctly! The number was " .. number .. ".")
Now that things are cleaner, let’s add one feature to our program.
It would be more fun if the game kept track of how many guesses we made so we could give them a special message.
Let’s create a variable called guess_count
that will start at 1
and increment every time the user makes another guess.
We’ll also go ahead and add some messages to the bottom to praise the user if they did it in a reasonable number of guesses.
-- The computer's secret number
math.randomseed(os.time())
number = math.random(100)
-- Our starting number of guesses
guess_counter = 1
print("Guess my secret number. It is between 1 and 100.")
guess = tonumber(io.read())
-- While the user's guess is not equal to
-- the number, repeat the body of the loop.
while guess ~= number do
-- Increment the guess counter by 1
guess_counter = guess_counter + 1
-- Give them some hints
if guess > number then
print("Your guess is too high.")
else
print("Your guess is too low.")
end
-- Make them guess again and again until they get it
print("Guess again:")
guess = tonumber(io.read())
end
-- Winning messages
print("You guessed correctly! The number was " .. number .. ".")
if guess_counter <= 5 then
print("Amazing! It only took you " .. guess_counter .. " tries.")
else
print("It took you " .. guess_counter .. " tries. Not bad.")
end
Exercises
- Try adding more messages for different numbers of guesses. You can modify the if statement where
guess_counter
is checked. - Make the
while
condition exit ifguess_counter
goes above 10 and tell the user they lost the game (but that should try again). - Try adding some messages to the if statement with the hints for when the user guesses an invalid number too far out of range. What if they guess a number that is more than 100? How would you do that?
Code homework
For homework, you will code a text-based adventure game in Lua. Your game must have at least 7 rooms and at least 3 items. Each room should have a text description and describe its exits.
- Start planning out your game by drawing a map with lines connecting “rooms” (areas).
- The game should start with an intro that sets up the world and the challenge. Who is the player? Where are they? What are they trying to do?
- All rooms must have a description
- There should be a way to win (and possibly a way to lose)
- There should be items described in the game that a player can interact with
- The game must be a challenge. The game can’t simply be a room1->room2->room3 etc. exercise
- ASCII art and emojis are permitted
Credits
- Image of Colossal Cave Adventure (1977) by Don Woods and Will Crowther
- Image of Mabel Addis from GDC.
- Learn2love Chapter 1 copyright © 2018 Jay Thomas