There are five beginner tutorials that start with a simple Lua script and gradually add more complexity. These are a good place get started if you're new to programming or unfamiliar with Lua. Each tutorial has a tech supplement that drills down on some of the new information in the tutorial.
|Beginner 1||Tech 1|
|Beginner 2||Tech 2|
|Beginner 3||Tech 3|
|Beginner 4||Tech 4|
|Beginner 5||Tech 5|
This intermediate tutorial is designed to walk you through the process of writing and running a Lua script. The beginner tutorials cover much of this, but the pace is a little quicker here.
For this tutorial, all you need is some experience playing Foldit and using the in-game tools. You should be familiar with basic game vocabulary like segment, wiggle, shake, and so on. Experience running cookbook recipes – or better yet, building your own recipes – is a plus, but not absolutely necessary.
When you've completed this tutorial, take a look at Lua Scripting Tutorial (Advanced), which expands on some of the topics covered here.
Loading the Script
To begin, load the sample Lua script called "simple wiggle by ones v2" into the cookbook.
If you're not familiar with using the cookbook, see 101 - Cookbook, which describes it in detail.
From the recipe page, with Foldit running, click "Add to Cookbook" on the recipe's web page. The recipe should appear in the Recipes divider of your cookbook.
If that doesn't work, click on the small blue "network folder" icon in the cookbook. This should open the "Download Manually" dialog. Enter the recipe's "node number", 102608 and click Download. The recipe should now be available.
The recipe, "simple wiggle by ones v2", is a simple backbone walk. It doesn't do anything amazing or revolutionary, but it is a great first script to look at and modify. It includes lots of comments to explain what is going on in the code, as well as some verbose output that you can follow in the output window as the script runs.
Opening an output window
Open the recipe output window by clicking the "Show recipe output" button on the cookbook. It looks like a small "DOS prompt".
Position the output window wherever you like, so it doesn't obstruct your view of the protein.
Running the Script
Locate "simple wiggle by ones v2" in the cookbook.
Hover over it and click the name or the green arrow to run it. Watch what happens -- it works pretty much as you would expect -- it wiggles the first segment of the protein, then moves on to the next one, wiggles that, and so on until it reaches the end of the protein.
While it runs, some text appears in the output window. That text is telling you what is happening in the script moment-by-moment and is a great way to see what is going on and (when you start writing your own scripts) to check to make sure no errors are occurring. If an error does occur, you will get some text explaining what the problem is.
Looking at the code
Next, you will want to start looking at the code in the script. Do this by hovering over the name of the script and clicking on the notepad icon ("Edit recipe"). The recipe opens in the Foldit recipe editor.
A Lua recipe in depth: comments, functions, loops, commands, and variables
There are five basic components you will see in the recipe: comments, functions, loops and if-then-else statements, commands, and variables. Let's work through each one in turn.
Comments are pretty easy to pick out -- they begin with a "--". You can write a comment anywhere in a script (they can take up a whole line or part of a line), and although they're not required, they're a great way to explain what is happening in the code to others who might look at it. Other than that, they do absolutely nothing! Go ahead and take a quick glance at those comments now.
Near the top of the recipe, we define our own function:
function round ( x ) return x - x % 0.001 end
This function takes a number x, and rounds it to three decimal places using the "mod" operator, %. We won't get into the details, but "mod" is short for "modulus", or "remainder". (The rest is available from Google!)
By itself, this code doesn't do anything. Later in the recipe, you'll see the name round again, when we call our function. It's handy when used in a print statement.
Loops and if-then-else
Like most computer languages, Lua has control structures, which affect the order in which statements are executed. Control structures include if-then-else statements, which will look at a little later, and loops.
Lua has several different types of loops, but this script uses a for loop that looks like this:
for seg = 1, numSegs do ...body of the loop... end
This type of for loop repeats the statements between the for and the end -- the "body" of the loop -- one of more times.
In Foldit, many recipes use basically the same for loop to process each segment in a protein. Here, the variable seg starts with a value of 1. The body of the loop runs with seg = 1. At the end, seg is increased by 1. As long as numSegs is greater than one, the body of the loop is executed again, with seg = 2. This repeats until seg is greater than numSegs.
What is our for loop doing? The basic structure of the for loop is:
for [something] to [something else] do [some things] end
In Lua, the different parts of a for loop could be all on one line or separated onto multiple lines. Using multiple lines looks nicer, and is easier to work with, but you can jam them all together if you want.
Basically, we're telling the computer to do some things over and over again for a specified interval from something to something else, and then end. We'll worry about the details of how this works later, because we need to learn about commands and variables first. For now, it is enough to understand the basic idea of what this loop is doing.
Now that you understand how that main loop is letting us do things to each segment in the protein in turn, let's figure out how we actually "do things"! If you take a look at the code inside of the for loop, ignoring the comments, what you will see is a list of commands:
selection.DeselectAll () selection.Select ( seg ) segscore = current.GetSegmentEnergyScore ( seg ) print ( "segment " .. seg .. ", score = " .. round ( segscore ) ) structure.LocalWiggleSelected ( 5 ) afterscore = current.GetSegmentEnergyScore ( seg ) if afterscore > segscore then gain = round ( afterscore - segscore ) if gain > 0 then print ( "segment " .. seg .. " gained " .. gain ) else print ( "segment " .. seg .. " tiny gain" ) end end
A command is pretty much what it sounds like -- it is a statement that tells Foldit to do something in particular to the protein. The commands are statements like selection.DeselectAll and selection.Select. You can do similar things using clicks or shortcut keys.
The commands here are actually calls to functions, which are defined as part of the Foldit Lua Interface. In that way, they're similar to the small function round discussed earlier.
The first part of each function is the "namespace" that groups related functions together. For example, the selection namespace has functions that select or deselect segments.
See Foldit Lua Functions for the complete list of commands/functions available.
The commands for shaking, wiggling, and mutating are found the structure namespace. In this recipe, the main "doer" is structure.LocalWiggleSelected.
There are also commands that just give us information, such as the score of the protein or the structure at a given segment. The commands current.GetEnergyScore and current.GetSegmentEnergyScore are examples of this type of command also known as a "getter". The current namespace has functions that work with the current pose of the protein.
The easiest commands to use are the ones that can do one and only one thing, for example selection.DeselectAll (). If any segments have been selected, this command unselects them. That's all there is to it. Similarly, we can selection.SelectAll () if we want. When a command has just empty parantheses -- () -- it's being used with no parameters or arguments.
Other commands require parameters, for example:
selection.Select ( seg )
We need to tell selection.Select which segment to select. Here, seg is a variable representing the segment number. We already saw that seg is the "index" of the for loop.
The main "doer" command in the recipe also requires a parameter:
structure.LocalWiggleSelected ( 5 )
As the name implies, LocalWiggleSelected applies the local wiggle tool to any selected segments. In this recipe, we're only selecting one segment at a time, although we could select more.
The parameter "5" is the "number of cycles" for LocalWiggleSelected. We don't know exactly what happens in each cycle, but in general, the more cycles you request, the longer the command takes.
We've already seen how a variable called seg represents the segment numbers of the protein, determining which segment selection.Select selects each time through the for loop.
Variables are also used for keeping track of the score and other information. The statement:
segscore = current.GetSegmentEnergyScore ( seg )
gets the score of the segment specified by seg, and places it in a variable called segscore.
The next statement:
print ( "segment " .. seg .. ", score = " .. round ( segscore ) )
prints out the segment number and the score. It uses the round function we looked at earlier to limit the score to three decimal places. See Lua Scripting Tutorial (Advanced) for more on using the print command.
After the LocalWiggleSelected command, we again get the score for the segment, storing it in a varible called afterscore:
afterscore = current.GetSegmentEnergyScore ( seg ) if afterscore > segscore then gain = round ( afterscore - segscore ) if gain > 0 then print ( "segment " .. seg .. " gained " .. gain ) else print ( "segment " .. seg .. " tiny gain" ) end end
The recipe uses an if statement to compare afterscore with segscore. Like for, the if statement is a control structure. For determines how many times a block of statements gets executed, while if determines whether statements get executed. If evaluates a "logical condtion, such as afterscore > segscore. A logical condition is either true or false. In this case, the condition will be true if there's been a gain.
If there's been a gain, the recipe uses a little trick to print it correctly. The recipe applies the round function to the difference between afterscore and segscore, rounding it to three decimals places. If the gain is less 0.001 (less than a thousandth of a point), the rounded value is zero. It's still a gain, but the recipe prints "tiny gain" in this case. For a larger gain, the recipe prints the exact numeric value, even if it's just 0.001. LocalWiggleSelected often makes very small gains, and without this trick the recipe would reporting "segment gained 0" quite a bit.
As we see here, if executes the statements following then when the logical condition is true. If the logical statement is false, the statements following the else are executed instead. The else part of the statement is optional.
You should now be able to download a shared recipe, run it, look at its output, and also view its code.
While you may not understand every line in the code, you should have a good general idea of how it works and how it uses loops, variables, and commands. Great, but what next?
Modify a Script
A great way to get started writing recipes is to modify the ones that other people have written.
The "simple wiggle by ones" recipe should be pretty easy to play with and modify.
For example, you can try making it wiggle by twos or threes, add a shake after each wiggle, or add a combination of shakes/wiggles after the loop is finished. Many local wiggle recipes add an outer for loop, and process the entire protein repeatedly in hopes of making small gains. Some variations process segments in random order, or use weak bands to nudge the protein.
Other people's scripts online are also ripe for modification, so take a look at what scripts are shared online. You'llj need to save these scripts under a new name. Feel free to share a modified script online, but it's always a good idea to attribute the original author(s) in the comments!
Write a Script
If you want to write a script from scratch, simply select the "New (ScriptV2)" button at the bottom of the recipe editor window. You'll get a boring blank screen that you can fill with code to your heart's content.
Lua is a simple language, so you should find that you can get a new script up and running right away. Even a single line with a command on it will run properly, so don't feel like you need to put together something elaborate with loops and other structures.
It's possible to cut and paste from a plain text editor if you'd prefer to write your scripts there. The Foldit recipe editor also lets you import recipes from a file, so you can use the editor of your choice. See Editing Foldit Recipes for details on using external editors.
Debugging is a fancy term for "trying to figure out what the heck went wrong" and you're going to have to do it a lot if you start writing or modding scripts.
Mistakes are easy to make, and sometimes a program doesn't behave exactly as you expect. Keep an output window open whenever you run a script so that you can see when and where an error occurs and get some information about the error.
The most common situation is that you've missed a parentheses or misspelled a variable name or are missing an "end" statement in your loop.
The output window will yell at you, but computers are "stupid", so what it says is wrong may or may not make sense. Make note of the line number where the error is occuring and study that line carefully to see if you can find your mistake.
Keep in mind that the real error may have happened somewhere else -- for example, sometimes a variable ends up not getting a value. In that case, the output window will tell you that an error is on the line where the variable is being used. But the point at which it was given the wrong value (or no value) in the first place is earlier in the code.
Debugging can be a fine art, so don't give up if your code doesn't run right away. Relax and work through it line by line to find the problem or share your code with someone else -- perhaps they can find the error.
In this script, you'll notice several print commands, which display the score at various points and show where gains occurred.
Print commands are a great way of giving your user (and yourself) helpful output as the script runs. They are also vital to debugging. You often need to give yourself information about what is happening in the script to help you find that annoying error.
For example, if you're debugging a loop, something like
print ( "loop number: " .. numSegs )
is helpful because it will print each time the loop executes. Then you can find out at what exact point during the loop the script errors out.
If there may be something wrong with a variable getting no value or getting the wrong value, put in something like:
print ( "num1 = " .. num1 )
right after the point where your variable num1 should be getting a value. Then you'll see what that variable is storing at that exact moment.
Revisions and Attributions
The original version of this tutorial and "simple wiggle by ones" were provided by themarquis, with assistance from Crashguard303 and Mat747.
The original version, simple wiggle by ones, was written using the original version of the Foldit Lua interface. It's a little simpler than the current version, and didn't print as much score information. Otherwise, the function is exactly the same.