Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

[Harlowe] Loop method based on subarrays

edited April 2016 in Help! with 2.0
This idea randomly struck me barely a couple of days after I was whining again for the lack of loops in Harlowe: talk about serendipity. I believe this implementation prevents the problems of other proposed methods for loops: HTML nesting (if you use a recursive method with (display:) or something else) or the rest of the passage being printed before the end of the loop (if you use a (live:) macro). It's also a simpler method.

But please check it and suggest any possible problem that I have not seen. I have tested it to run complicated code and it seems to work the same as the other methods, maybe a bit faster than (live:).

First, when you need to use a loop at some point in your game, you optionally define any variables that need to exist inside the loop, for example:
(set: $loopCounter to 1)

You define how many times you want to run the loop:
(set: $loopIterations to 5)

And you define what you want to do in each iteration. We will display a passage with more code in it:
(set: $l to "(display: 'loop iteration code')")

Now you have the necessary loop parameters $l and $loopIterations, you run the loop generator, that is always the same and so it's located in a separate passage that you will reuse for all your loops:
(display: "loop")

Now the loop generator passage. How many iterations do you expect the longest loop in your game to have? Ok, let's give it a margin and repeat each iteration 30 times:
(set: $loop to (array: $l, $l, $l, $l, $l, $l, $l, $l, $l, $l, $l, $l, $l, 
$l, $l, $l, $l, $l, $l, $l, $l, $l, $l, $l, $l, $l, $l, $l, $l, $l, ))

How do we limit the loop to the intended number of iterations? We extract a subarray:
(set: $loopRun to (subarray: $loop, 1, $loopIterations))

And how do we actually run the loop? We print the array, using .join parameter to remove the commas that Harlowe inserts when printing arrays:
(print: $loopRun.join(""))

Finally, we can empty the temporary variables so they don't take space in the history:
(set: $loop to "")(set: $loopRun to "")

All together:
::story passage
STORY
(set: $loopCounter to 1)
(set: $loopIterations to 5)
(set: $l to "(display: 'loop iteration code')")
(display: "loop")
MORE STORY

::loop
{
(set: $loop to (a: $l, $l, $l, $l, $l, $l, $l, $l, $l, $l, $l, $l, $l, 
$l, $l, $l, $l, $l, $l, $l, $l, $l, $l, $l, $l, $l, $l, $l, $l, $l, ))
(set: $loopRun to (subarray: $loop, 1, $loopIterations))
(print: $loopRun.join(""))
(set: $loop to "")
(set: $loopRun to "")
}

::loop iteration code
$loopCounter. Arbitrary code or text that you want to repeat. (set:$loopCounter to it+1)

The result when you run this is:
1. Arbitrary code or text that you want to repeat. 2. Arbitrary code or text that you want to repeat. 3. Arbitrary code or text that you want to repeat. 4. Arbitrary code or text that you want to repeat. 5. Arbitrary code or text that you want to repeat.

Changing $l and $loopIterations allows you to repeat any text or code (as far as I know), any number of times to the maximum set in $loop.

Comments

  • Well done, a very inventive solution.

    If you don't mind using Javascript then you could dynamically generate the $loopRun array like so:
    (print: "(set: $loopRun to Array.apply(null, Array(" + (text: $loopIterations) + ")).map(function(_, i){return \"" + $l + "\";}))")
    
    ... although the above does not increment the $loopCounter variable and I only did very basic testing so it may not for all the different possible values that can be assigned to the $l variable.
Sign In or Register to comment.