Computers love repetition. It’s what they are good at. Give a repeatable task to a computer and you will make a happy pile of silicon.

When writing programs, you end up writing lots of loops. With interactive stories, your mileage may vary. You may just write very text-centric stories, favoring links instead of code. In that case, you might not write a single loop.

Other times, you’ll want to utilize loops to process or act on your story data. This tutorial will get you started with Twine loops.

Back when I was a kid, the BASIC programming language was the new hotness. BASIC (otherwise known as Beginners’ All-purpose Symbolic Instruction Code) was a language specifically designed for new coders. When my dad brought home a new computer, my brother got right to work.

His first program went something like the following:

10 PRINT "BRIAN IS A WEENIE"
20 GOTO 10

Ahh yes, the result of my brother’s “hard” work was our new computer declaring that I was a weenie.

In truth, my brother actually demonstrated a very basic loop. The first line of code printed out a message. The second line directed the computer back to the first line. There was no end to his loop. It would declare I was a weenie until the end of time; or I until I unplugged the machine.

Understanding loops

A Twine loop is very much like my brother’s BASIC loop. In fact, loops in other programming follow the same pattern.

First, every loop must have a condition. The condition determines how long a loop to run. My brother’s loop didn’t have a condition, thus the loop infinitely repeated itself. The majority of loops will only run for a set amount of time.

For example, you may want to loop ten times. To do this, you might designate the loop to run from 1 to 10.

You can also loop over collections. In a previous tutorial, you used an array to hold inventory items. You can (and will) write a loop over each item in the array.

During each cycle of the loop, a value is passed into it. If the loop is cycling over an array, then that value is the current item. If you are cycling between ranges, the value represents the current iteration; so if you were looping between 51 and 61, the passed-in value for the first iteration would be 51.

Okay, enough theory. It’s time you saw loops in action.

Getting started

Before you start, let me just preface with a little opinion. In the Harlowe story format, loops are ugly. In fact, whenever I use a Harlowe loop, I have to look up the syntax because they are so clunky.

Mind you, if you use Harlowe loops enough, you’ll grow accustomed to it. Regardless, if you start feeling a little bit antsy about loops, believe me – it’s not you.

Open up your tutorial in progress or download the starter project located here:

When you last left off, you adjusted the inventory to print out the first inventory item so long as the player was carrying one. As you add items, this approach quickly grows burdensome. A better approach is to loop through the inventory and print out each item.

Open Camp Entrance and scroll down to your previous code.

You’ll replace this block of code with a loop.

Your first Twine loop

To write a loop in Harlowe, you use the (for:) macro. In this case, you are going to loop a series of items in an array. This is known as for-each loop. You can think of it as “for each item in my inventory, do this”.

Delete the code after “You are carrying:”. Now add the following:

(for: each _item, ...$inventory) [

]

Your code should look like the following;

So here’s the breakdown.

(for: each _item, ...$inventory) [

]

First, you start the (for:) macro. It’s called (for:) because in most languages, the most common type of loop is a for loop.

(for: each _item, ...$inventory) [

]

Then, you use the each keyword to indicate you want to loop over every item. The each keyword is actually a type of lambda.

What’s a lambda? Well, it’s basically a self-contained code block that you can define. In this case, it’s already defined for you. There’s another lambda keyword that allows you to filter which you’ll do later in this tutorial.

Don’t worry about not understanding lambdas or filtering. Just think of each as every item in the loop.

(for: each _item, ...$inventory) [

]

Next, you define a local variable. You’ll notice there is an underscore before the variable name. This indicates that it is a temporary variable. Any variable preceded by an underscore means you are using it for just a short amount of time. In this case, the variable will be unavailable after the loop.

(for: each _item, ...$inventory) [

]

The three dots is the spread operator. It means you are providing all the values in a collection versus just the variable. If the inventory array contained “keys, recipe, gas” then the spread operator turns your code into this:

(for: each _item, keys recipe gas) [

]

Each item becomes a single iteration in the loop.

(for: each _item, ...$inventory) [
  // code goes here
]

Finally, all of your code goes between the brackets. And that’s it. You have a loop. Just so you know, to do the same in the Swift programming language, you’d write:

for item in inventory {

}

Harlowe loops are just overly complex.

Printing items

Okay, you have your first loop up and running but it’s not displaying anything. This is an easy fix. Remember, the _item variable represents each item in the array.

Update the code to the following:

(for: each _item, ...$inventory) [
  (print: _item + " ")
]

Your code should look like the following:

All that you are doing is printing each item and then adding a space after it. If you didn’t add a space, then all the items would combine into a single word like “keysrecipegas” instead of “keys recipe gas”.

Run your story and pick up the keys. You’ll see that it works just like before, except this time, you can now add items to your inventory and have them printed on the screen.

Nice work!

Loop through ranges

For the majority of the time, you’ll be looping through arrays but you can also loop through ranges.

For example, you may want to run a loop five times. In this case, the loop syntax is exactly the same with one small addition. Instead of passing in a self-defined array, you pass in a (range:) macro.

For the (range:) macro, you define a minimum value and a maximum value. This will return an array with those numbers from the minimum value to the maximum value.

For example, here you define a range between 1 and 5:

(range: 1, 5)

This returns an array containing the numbers:

1 2 3 4 5

Thus, if you wanted to run a loop five times, you’d define the loop like so:

(for: each _item, ...(range:1,5)) [
  (print: (str: _item) + " ") 
]

In this case, the loop will print out the numbers in order. You’ll notice that there is a new macro in there; the (str:) macro.

(for: each _item, ...(range:1,5)) [
  (print: (str: _item) + " ") 
]

This converts a number into a string (text). If you don’t do this conversion, Harlowe will attempt to perform an addition between a number and a blank space, resulting in an error.

Where to go from here?

When starting out writing interactive stories, you won’t be using a lot of loops. Yet, once you start adding complexity to your story, you’ll find yourself using them more and more.

If you do use loops and need a reference, you can also check out the Twine cookbook.

As is, you can now add lots and lots of different items to your player’s inventory. Unfortunately, your inventory system is a little unwieldy. It’d be nice for it to appear on every passage as well as link to each object’s description.

You’ll accomplish this task in the next tutorial!