Let’s Learn Lua!

It’s always good to dive into various programming languages to see how they handle things and to improve yourself overall.

To me, Lua is interesting just because it’s the language used time and time again in game mods. I find game modding to be pretty rewarding and it satisfies that itch (at least partly) to make a game.

Goals

The main purpose of this series is to gain a “causual working understanding” of Lua, enough to start building game mods. From here, you should jump into Let’s Build a Elder Scrolls Online Mod - Part 1.

Since we’re going for a working understanding and we’re not using this in an enterprise setting, I’m not going to go deep into the language at all (what’s going on under the hood, etc).

This first part of the series will serve almost as a quick flyover of core language aspects, a cheat-sheet if you will. From there, we’ll put this into practice.

For this series, we’re going to build a terminal-based Blackjack game. This is usually my go-to “Hello World” into a language since it involves variables, classes (or not), math, arrays (sets, dicts, etc) and so forth. It really has a nice mix that I find great for learning the basics of a language.

We’ll start making the Blackjack game in part 2 of this series. This first part is a brief intro to the language.

Is This For You?

I’m going to make the assumption that you’re a smart person who is curious. Perhaps you’ve worked in other programming languages or perhaps you’ve just seen code.

I hope to make this series fairly easy to follow, but if you’re not sure what a “variable” is, you’ll probably have some research to do as you follow along. If you’re not sure, just jump in and see where it goes.

If you’re very experienced, this series may be fun but maybe you just want to head to the Lua docs to read and get going.

Setup

Core

Download: Lua
Docs: Lua 5.3
Live: Repl
GitHub Repo: Lua Blackjack

Go to the download page and either download the binary for your OS or if you prefer, get the source and compile it.

If you’re going to build from scratch on Linux, make sure you have these packages installed (I use Fedora/CentOs):

1
2
3
4
readline-devel
gcc
gcc-c++
kernel-devel

Additionally, you can visit the live repl for trying out Lua.

Testing Installation

Once you’re setup, go to the command prompt and type in:

1
$ lua

You should see something like:

1
2
Lua 5.3.4 Copyright (C) 1994-2017 Lua.org, PUC-Rio
>

And here’s “Hello World” quickly:

1
> print "Hello Lua!"

IDE & Running Lua Files

If you want a full IDE, you can either look into Lua in Eclipse or InteliJ.

I’m using Visual Studio Code for this. And you can see what extensions I’m using here: Visual Studo Code Extension Gist.

From terminal you can run Lua files by just called Lua and then the file to run:

1
$ lua my_file.lua

Lua Rocks

While outside of the scope of this series, Lua Rocks is something you may be interested in. It is the package manager for Lua. Self-contained packages are called rocks (probably because Lua is moon, so you get cool moon rocks). A package example would be lua-cjson, which add JSON parsing/encoding support for Lua.

If you want to check out it, the download page is here: Install Lua Rocks.

Foundation Notes

  • Lua is a dynamically typed scripting language.
  • It has a C API and is very portable.
  • Runs in environments that have C compilers
  • Many applications embed Lua interpreters, which means you can script them (games especially)
    • World of Warcraft, Elder Scrolls Online, Redis, Wireshark, VLC, Apache HTTP server, etc.

Core Language

Types

Lua has 8 basic types:

  • nil
  • boolean
  • number
  • string
  • userdata
  • function
  • thread
  • table (this is a key/value store)

If you want to set a string to a multiline value, use brackets like this:

1
2
3
my_string = [[ this
is many
lines]]

Variables

First it’s important to note that by default all variables declared are globally scoped.

If you want to create a locally-scoped variable (and you should quite often), you use the ‘local’ keyword. When you do this the variable is scoped to the block it is in.

In Lua declaring variables is pretty straighforward.

1
2
my_variable = "Hey now!" -- I am GLOBAL!
local something_else = 4 -- I am LOCAL!

You can also perform multiple assignments:

1
this, that, other = "something", "more things", "moar bacon"

As a side note, if you were going to print a variable, you’d wrap it in parentheses, like this:

1
2
name = "Jason"
print(name)

Code Commenting

A single-line comment is done with –.

And multiline comments are done with –[[]]–

1
2
3
4
5
6
my_var = 4 -- this is my comment
--[[
don't forget to check out the GitHub repo!
]]--
other = "hi"

Math

Docs: Lua 5.3 Math

Our Blackjack game is going to deal cards and it needs to randomly decide what cards to give. How will it do this?

Lua has a math library built in that helps us accomplish this and many other tasks.

Basically to create a random number between 1 and whatever, you need to generate the random seed and perform assignment.

math.random takes 0-2 arguments.
math.random() returns a real number between 0 and 1
math.random(upper) returns a real number between and the provided number
math.random(lower, upper) returns a real number between the two provided numbers.

1
2
math.randomseed(os.time())
our_random_number = math.random(52)

Operators

In most things we build with Lua, we’re going to need to do some math, concatenation and logical checks.

Lua has operators found in many languages, but there are some tweaks to how they’re typed. Let’s look at some.

1
2
3
4
5
6
= is assignment
== is equals
~= is not equals (notice the tilde here instead of the bang equals !=)
.. is concatenation
# is length (#something)
and, not, or :: these are words instead of &&, !, ||

User Input

Docs: Lua 6.8 Input and Output Facilities

Aside from some math, our Blackjack terminal game is going to require a little bit of user input, so let’s look at how that’s done.

We can accomplish this task easily with io.

By default io.stdin:read will return a string of the user’s input. In some cases we’re going to need a number, so we can call it with the “n” option like this:

1
2
normal_input = io.stdin:read
number_input = io.stdin:read 'n'

A simple Lua program to illustrate this:

1
2
3
4
print "Enter a number."
user_number = io.stdin:read 'n'
print "Your number multipled by 3:"
print (user_number * 3)

For fun enter something other than a number. You’ll get an error. Why?

Since we specified that the input needs to be a number, if anything else is added, the final result of user_number will be the type: nil. And you can perform math on nil, so you get an error.

How to handle this? Excellent question. That leads us into conditionals.

Conditionals

Lua has the usual conditions if, elseif, else as one might expect. In our above error example, we need to test the variable for a number value before doing math on it.

1
2
3
4
5
6
if user_number then
print "Your number multipled by 3:"
print (user_number * 3)
else
print "You didn't enter a number"
end

This works because when we fetched the user input we asked for a number. If the user doesn’t enter a number, Lua sets the value to nil.

Looping

In Lua you can loop via For, Repeat and While. And you can use Break to bust out of a loop. Let’s take a quick look at how to do each one.

It’s important to note the two patterns for Repeat and While.

While: express then do things.

1
2
3
while something ~= something_else do
-- whatever here
end

Repeat: do things then expression. So if you wanted to make sure something was always done at least once no matter what, use Repeat.

1
2
3
repeat
-- whatever
until something == something_else

With For loops, there are two types. Here are their differences:

Numeric For Loop: (start, end, step) The step is optional and the default is 1.

1
2
3
for index = 1, 10 do
-- do stuff here
end

Iterating For Loop: (key, value in iterated_item).

1
2
3
for key,value in pairs(my_table) do
print(key,value)
end

You can also use ipairs to iterate with index and value. Although the order isn’t guaranteed, so keep this in mind.

Functions

If you didn’t already know, a function is a great way to organize code into highly-readable, functional pieces.

Functions are first-class citizens. You can pass them as args to other functions, assign to variables, return them and so on.

In Lua you declare functions like this:

1
2
3
function someFunctionName(myValue)
-- things here
end

Also you can use a spread operator as the argument (variadic function) and then multi-assign like this:

1
2
3
4
function coolFunction(...)
coolName, coolAge, coolHobby = ...
-- other things here
end

Tables

As mentioned above, a table in Lua is basically a key, value store.

A table is noted by the use of curley braces, like this:

1
2
3
4
5
-- empty table
local my_table = {}
-- now add something to it
my_table[player] = "Jason"

And here’s how to setup a table with values in it:

1
2
3
4
local my_table = { Name = "Jason", Hobby = "Coding" }
print (my_table[Name])
-- or
print(my_table.Name)

If you want to use tables like an array, just populate it with values. By default the key in a table is in the index. NOTE: In Lua the first item in an index is 1 not 0 (what the what?).

And if you’re looking to push onto the table in array-like fashion, you can just get the length (#) and increment by 1 like this:

1
2
3
4
actions = { "hit", "stand", "stand" }
actions[#actions + 1] = "hit"
-- actions is now: hit, stand, stand, hit

Next Step

At this point we have the core of the Lua language down and have discovered documentation resources for more information.

From here, we’re going to start building up a simple BlackJack terminal game in lua.

Look for part 2 to start by September 01, 2017