This is a really simple tutorial to get you started coding Lua for The Powder Toy. It tries to expect no programming knowledge of the reader, and tries to give the reader an idea of how to think algorithmically as well as how to create their very first element.
I will take a bunch of liberties writing this, as well as skimming over lots of details. I’ll try to be thorough though.
What is Lua?
Lua is a programming language. Programming languages are much like human languages – you’re making a concept in your head into words, so that other people can understand them. Computers in general don’t really speak any languages except machine instructions (which are really tedious to write in), so programmers came up with the idea of writing programs that read different kinds of programming languages and run the machine instructions accordingly. So, they kind of make it simpler to talk to the computer (or, in our case, TPT).
I’d like to mention that writing “LUA” in allcaps will annoy mniip, so it’s better in general to avoid doing just that.
Running a script
Before we get started on actually teaching you how to code, we’ll make sure you know the process how to run Lua scripts in TPT. It’s simple – you won’t need any kind of tool that isn’t already installed on your computer.
- Open Notepad or TextEdit or gedit or nano (make sure TextEdit is in “plain text” mode – the kind where you can’t set fonts or bold or italics). If you have a preferred plain text editor, you can use that instead. Notepad++ and Sublime Text are good alternatives. Rich text adds tiny pieces of markup to show where various styles (bold, italic) start and end, and we don’t want any of that inside our source code.
- Copy-paste the following into your text editor window:
tpt.log("Hello, world")
- Save the document next to your TPT program as “autorun.lua”. The name must be exactly this – quotes included. If this sounds a bit extreme, look at image.
- Open The Powder Toy. You should see something like image on the bottom left of the screen.
You’ve successfully written a script that automatically opens when TPT opens! We’ll use this technique further on for simplicity’s sake.
Language
I’ll keep it simple, however if you are into some technical reading feel free to browse the excellent Lua manual.
I recommend you follow the coding examples given below with a Lua REPL. REPLs let you try out code immediately and see how it interacts with variables or anything else. For example, the TPT console is basically a REPL. Here’s a link to another kind of REPL, with no TPT functions inside.
Variables
Whenever you program something, there will be a need to keep some data (numbers, particles, text) in store. That’s what you’ll use variables for. If you’ve got some basic algebra, the concept will sound extremely similar. If not, well, variables are like making a box that you put stuff into, so you can later find the box by its name and get the stuff back out of the box.
Storing a value (like a number) is very simple in Lua:
a = 5
That’s all. Now whenever you use the variable name “a”, it will be the same as typing in 5. There’s a difference though – when in the future you set the value of “a” to something else, you will get the new value back instead. So variables can change when you use them.
Say we want to do simple math on this value. Computers are beefed up calculators after all…
a = a + 1
This line of code is something more complex. On the right side of the equals sign, we first ask for the value of a, then we add 1 to it, and in the end, whatever comes out of this addition we assign back to a.
So, if we asked the computer to show us the value of a now, it would show 6.
As you can see, you have to be kind of literal to computers. Computer languages are an extension of this literalness – there’s not much that is left to be guessed when you’re programming something.
Other than simple integers (numbers without fractions), you can store things like text inside variables. Variables aren’t picky about what you store in them – you can use “a” to store a number first and then assign it a string later on!
a = "this is a line of text!"
However, if you use a variable without assigning anything to it first, say after running the above code you want to use b like this:
a = b + 1
…you will usually get a loud error. That’s because variables by default have a special value called “nil”, which means “nothing useful”. You can use this nil value yourself simply by doing this. You generally will not need to.
a = nil
Also, about the error – you’ll get the error because you tried to add 1 to nil. What happens if you add 1 to nothing? Before you think – nil isn’t zero, so it shouldn’t also default to adding 1 to zero!
This kind of error is called a type mismatch, which means that you tried to add a number and a nil, when instead the + sign only works on two numbers.
Variable Naming
You can name your variable mostly anything when you keep to using the English alphabet, but it can’t start with a number, or have spaces in the middle of the name. There’s also a few words that are already important in the Lua language, so using them as variable names would confuse the reading program (parser). You can also use underscores anywhere in the name.
a = 5 -- ok
thing = 2 -- also ok
biggerThing = 2.1 -- also ok!
a10000 = 5 -- ok
_1 = 5 -- also ok!
1 = 5 -- not ok!
and = 5 -- not ok! "and" is a special keyword
_and = 5 -- ok!
and1 = 5 -- ok!
and_or = 5 -- ok! "and_or" isn't a special keyword, while
-- "and" and "or" are.
Note: It’s often an incredibly good idea to name variables according to what they contain, or how they’re used.
a = 5 -- Not sure how a is used...?
speed = 3 -- it's used to keep track of something's speed!
Comments
You also might have noticed that I used double dashes when putting notes behind the variable declarations, in the last lesson.
This is a special notation that turns everything until the end of the line into a code comment – useful for marking down things you’re doing. Code comments are usually kept short and simple, and they usually don’t try to rewrite the source code – the comments are to be read by programmers, not laypeople!
Comments are completely ignored by the parser, meaning you can basically write anything in them.
Here’s a recap on how the comments look again:
-- Hello world! I am a comment!
-- a = b + 1
-- you can also "comment out" pieces of code, they will be ignored.
Sometimes you may want to not append “—” before every line in the comment, so Lua will help you out with multi-line comments. They look like this and will run until the end of the double brackets.
--[[
The line above started a comment.
It doesn't care about new lines, instead it looks for this thing here:
]]
--[[
People usually append a -- before the end just to look symmetrical, or so
that they could simply delete the top line to un-comment their blocks of code.
--]]
DataTypes
We talked earlier about different variable types. There’s eight of them, in fact.
"Hello, world" -- string
10.401 -- number
2 -- number
true -- boolean
nil -- nil
{1, 2, 3} -- table
math.floor -- function
You can assign any of those to a variable. I talk about tables and functions later in this very tutorial.
Strings
Strings are simply a bunch of text.
They start with a double quote ” and end with a double quote. You can use single quotes, aka apostrophes ‘, in place of double quotes.
You can’t have new lines inside a string, but instead of actually pressing Enter you can type in the character sequence n which is a representation of a new line.
Other special characters can be found out about here.
a = "hellonworld!"
Nil
Nil is simply nil. You use nil as a “no value here” marker.
a = nil
Booleans
Booleans are named after Boolean logic, which is named after George Boole. They represent a really simple value of either “true” or “false”, like a switch that can be on or off.
a = true
Numbers
Numbers are nothing special. You can use both numbers without fractions (integers) and numbers with fractions (floats, or floating point values).
a = 0.5
a = 4
Tables
Aside from regular single variables, Lua can store multiple variables in a sort of list.
Imagine if you wanted to store, for example, a shopping list. Or a queue of people. You wouldn’t be able to do that with usual variables, that’s where tables come in!
As arrays
Since the variable’s value isn’t a single value anymore, you can’t simply use it inside math or other purposes – you have to use special syntax to access the specific value inside this table.
mytable = {}
This is an empty table. Nothing is inside it.
mytable = {"CLNE", "DUST", "WATR"}
This is a table with some text strings inside. To get the first string out of the table, we will access the table’s first element. This is done with the syntax below:
mytable[1] -- results in "CLNE"
Simple, right? We just add some brackets and which value we want from the table. What happens if we try to get something from the table that isn’t there?
mytable[100] -- results in nil
Not much new there. What if we want to store something in that specific location? We can simply assign to the above syntax!
mytable[100] = "NEUT"
You will now have a table with a first, second, third and hundredth value. Nothing inbetween either. How the hell does this happen, you ask? Read on…
As hashes
As you may read from the title, tables can be both simple lists of values and a more complex structure called hashes. Hashes are the simplest data structure ever, here’s what they do:
They assign a name to a value. Imagine a really old kind of filing cabinet. You have a bunch of things there, with little labels popping out. You pick one of the files based on these labels and use what you get out. Hashes are literally filing cabinets.
mytable = {}
mytable["moo"] = "cracker64"
mytable["potato"] = "jacob1"
-- mytable["moo"] returns "cracker64", for example
And that’s how to use a hash. Easy peasy. You can also have a regular list that also has some random names assigned to values.
mytable = {1, 2, 3}
mytable[1] = "moo"
mytable["filing cabinet"] = true
-- mytable looks like this now: {1, 2, 3, "filing cabinet" = true}
Now that you think about it, aren’t regular lists just hashes that use numbers instead of strings for names?
Extra note: You can also access hash names (that are valid variable names) without quoting or using brackets, like this:
mytable = {
[1] = "beep boop",
x = 15,
y = 20,
type = "DRAY"
}
tpt.log(mytable.type) -- ok
tpt.log(mytable.1) -- not ok
Table literals
If you saw before we created an empty table with {} and sometimes added values to that empty table with {1, 2, 3}. Both of those are actually called “table literals” – they’re programming constructs that let you make a table without using tons of usual assignments.
This is how they look in their purest form. The tables in each box will end up as the same thing, except more parts can be taken away to still mean the same thing.
mytable = {
[1] = 1,
[2] = 2,
[3] = 3
}
-- same table
mytable = {1, 2, 3}
-- still the same table
mytable = {}
mytable[1] = 1
mytable[2] = 2
mytable[3] = 3
mytable = {
[1] = 1,
[2] = 2,
[3] = 3,
["potato"] = true
}
mytable = {1, 2, 3, potato=true}
mytable = {1, 2, 3}
mytable["potato"] = true
mytable = {}
mytable["potato"] = true
mytable[1] = 1
mytable[2] = 2
-- you get the point...
Expressions
Now I’ll try explaining the concept of expressions. Over the course of the tutorial above you might’ve noticed how different kinds of syntax can “return” a value. For example, when you assign something to a variable, the right side always has to have something that has a value. Or, in the case of 1 + 2, results in a value.
Expressions are pieces of code with a value, or end result. For example, the code 2 + 3 has an end value, which is 5. The code 2 + a is also an expression, even if we can’t immediately tell what its value will be without knowing the value of a.
…Speaking of which, simply a is also an expression. So is 5 or {}. All of those are pieces of code that return a value. So let’s use this new terminology in the rest of the tutorial!
Functions
Functions are a special kind of expression that can expect values (also called arguments or parameters) and return other values. They run some code off in another point in the source file and return whatever the function gives back.
(Note: some functions are defined outside of your source file – those are called the standard library, or in TPT’s case the API.)
function add(x, y)
-- if we write add(1, 2) later
-- then x = 1
-- and y = 2
return x + y
-- x + y would return 1 + 2
-- ... which is 3
-- therefore the return value of this function call is 3.
end
Here’s an incredibly simple function. It’s called “add”.
It takes in two values (that will be named x and y inside the function), and returns the value of the expression after the “return” word.
That expression happens to add x and y together, which is what we want the function to do!
To use that function later in the code, you call it like this:
thing = add(1, 2)
The expression add(1, 2) passes two “arguments”, 1 and 2, to the function. After that, the function is found and run from the top. The parameters 1 and 2 can be referred to inside the function as x and y. Outside the function those names never exist!
Another note: Functions can return more than one value. For example:
function coordinatesOfParticle(index)
return tpt.get_property("x", index), tpt.get_property("y", index)
end
x_coord, y_coord = coordinatesOfParticle(15)
Useful Expressions
Here’s a bunch of expressions (or, rather, operators since that’s what the characters in the middle are called) you may need. The thing in parentheses describes which type the expressions on the left and right side must be.
Math
a + b -- (number and number) Addition
a - b -- (number and number) Subtraction
a * b -- (number and number) Multiplication
a / b -- (number and number) Division
-a -- (number and number) Negation (-5 for example)
Comparison
You can compare two variables and these statements will return a boolean depending on the end value. Incredibly useful in, say, branching!
a > b -- (number and number) true if a is larger than b
a < b -- (number and number) true if a is smaller than b
a >= b -- (number and number) true if a is larger or equal to b
a <= b -- (number and number) true if a is smaller or equal to b
a == b -- (any types) true if a and b are the same objects, or at least equal
a ~= b -- (any types) true if a and b are not the same objects, or equal
Boolean logic
There’s a bunch of logical operators here that are useful in combining the above expressions into one single expression. For example, if you want to check if (a + b > 10) and (a + b < 15), then …that’s exactly what you write. Sounds easy, right?
There’s also this concept of falsey and truthy values. Those are values that are not literally of the boolean type, but kind of fit in. For example, nil is the only falsey value. If you include it in logic below it’s pretty much equivalent to false, except it’s not a boolean-typed value.
Every other value is truthy, meaning that you can check for the existence of a variable without using comparison operators.
not a -- (any value) true if a is falsey
a and b -- (any values) true if both a and b are truthy†
a or b -- (any values) true if a or b or both are truthy†
† Actually, it’s more like the below. This is called “short-circuited” logic, meaning the parser can stop reading the rest of the logic statement when the first part already gives you the end value. You can use this property in a lot of tricks.
a and b -- (any values) b if a is truthy otherwise a
a or b -- (any values) a if a is truthy otherwise b
-- example trick: assign a to empty table only if a isn't defined already
-- remember how "nil" is a falsey value? if a isn't defined, a is nil, so
-- "a or {}" will evaluate to {}.
a = a or {}
Branching
Sometimes you may want to do something depending on an expression. For example, you may want to increase the speed of the car if a car is below speed limit, but decrease its speed if the car is below it.
This can easily be accomplished by using branching statements in Lua.
speed = 30
if speed < 45 then
speed = speed + 10
end
This is a very simple branching statement: if the expression speed < 45 is true, then speed is incremented by 10. Otherwise, the code is ignored.
If you want to write something to do otherwise, you can use the else keyword:
speed = 30
if speed < 45 then
speed = speed + 10
else
speed = speed - 10
end
The else block is only applied when any expressions before it don’t apply.
If you want to write more than one branch, for example to stick to the same speed when the speed is between 45 and 60 units, then you can add an elseif clause before the end. It takes another expression, which is only checked when the ifs/elseifs before it have all failed.
You can have multiple else-ifs in a single if statement, which means you can have basically infinite different branches of code.
speed = 30
if speed < 45 then
speed = speed + 10
elseif speed > 45 and speed < 60 then
-- do nothing
elseif speed > 60 and speed < 75 then
-- slow down slowly
speed = speed - 5
else
-- speed down quick
speed = speed - 10
end
Looping
What if you have a table and you want to go over every item once, running the same code on it, what options do you have?
…
I’ll answer for you.
First, you can simply run the code once for every index:
mytable = {"ARAY", "BRAY", "CRAY", "DRAY", "ERAY"}
tpt.set_property("temp", 1000, mytable[1])
tpt.set_property("temp", 1000, mytable[2])
tpt.set_property("temp", 1000, mytable[3])
tpt.set_property("temp", 1000, mytable[4])
tpt.set_property("temp", 1000, mytable[5])
Kind of annoying, right? We could also define a function with our code and call it with a different table entry each time:
mytable = {"ARAY", "BRAY", "CRAY", "DRAY", "ERAY"}
function doThing(i)
tpt.set_property("temp", 1000, mytable[i])
end
doThing(1)
doThing(2)
doThing(3)
doThing(4)
doThing(5)
…This is getting old fast. How do we automate this?
While loops
While loops are the simplest kind of loop: do everything inside the code block as long as the expression is true. When it turns false, don’t run any more.
i = 0
-- i = i + 2. when i is 100 or bigger than it, the loop stops running and the
-- rest of the code is executed.
while i < 100 do
i = i + 2
end
-- run until i + 1 is smaller than 0, then stop
while i + 1 >= 0 do
i = (i / 2) - 1
end
-- Adds 1 to i forever and ever
-- Also, spam the TPT log with lol.
while true do
i = i + 1
tpt.log("lol")
end
while true do i = i + 1 if i % 1000 == 0 then — new statement: stop looping! break end end
Easy enough?
while loops are incredibly practical, but beginners often make them infinite loop accidentally. That’s why for loops are often more practical.
Repeat-until
They’re exactly like while loops, except like flipped upside down. The statement tested for has to stay false instead of true, and the value is checked for at the end of the iteration instead of the start.
For example, these two loops are exactly the same:
while true do
if not (i < 1000) then
break
end
i = i + 1
end
while i < 1000 do
i = i + 1
end
…but at the same time these two loops are the same too:
while true do
i = i + 1
if i > 1000 then
break
end
end
repeat
i = i + 1
until i > 1000
Getwhati’msaying?
C-style for loops
(They’re called C-style because they look similar in the wildly popular C programming language.)
For loops are called that because typically you read them like “for every i from 1 to 500 do this”.
Anyway. Here’s how we make one.
for i=1, 5, 1 do
-- the value of i will be different every loop, or in other words, iteration
doThing(i)
end
(Note: the variable doesn’t have to be i, or a single letter. i is commonly used because the word “iterable” starts with the letter I.)
The syntax is more simple than might appear: we first initialize i to some value (in this case, i = 1).
After a comma, we say that i’s value can’t be bigger than 5.
After another comma, we say that we want i to be incremented by 1 every time.
You can implement this for loop in a while loop like this:
i = 1
while i < 5 do
doThing(i)
i = i + 1
end
Generic for loops
Here’s a terse tutorial on generic for loops. I’ve chosen to omit it in my own description, because it’s quite unnecessary to start making TPT elements.
http://www.lua.org/pil/4.3.5.html
Okay, that’s about it for basic Lua. You’re pretty much ready to make an element now. You should try to read through Programming in Lua if you want a more detailed introduction to Lua.
If you want a very detailed manual to look up what things do exactly, here’s the Lua 5.2 manual.
Author: Johannes Kadak (@boxmein)