Howdy, Stranger!

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

[Harlowe] Doing a loop with Javascript?

There are a number of questions asking how to do a for loop in Harlowe, since no macro is provided for that: this, and this, and also this bug report. Some workarounds that are proposed include using the (live:) macro or embedding (display:) within itself. I have used extensively the (live:) trick and I know its issues (mainly, that the content under the macro gets printed before the loop has finished running).

But I didn't have this idea until today: is there any way to directly use Javascript loops within Harlowe?

Please forgive that I don't provide any experiment or test, but I can't speak Javascript (and I tried to learn, believe me). But if this can be done it could be a major improvement for the game I'm writing (I need use (live:) loops all over the place). Thanks!

Comments

  • edited April 2016
    I've just tried to put this in the Javascript and then running it with a <script> tag:
    if (! window.harloweLoop) {
      window.harloweLoop = function() {
        document.getElementById("loop").innerHTML = "";
        var text = "";
        var i;
        for (i=0; i<3; i=i+1) {
          text = "(set: $d to $a's " + i + ")(display: $d)<br>";
          document.getElementById("loop").innerHTML += text;
        }
      }
    }
    

    It perfectly prints the Harlowe macros with the correct values, three times. Of course, it doesn't run the Harlowe macros. That's about as far as my Javascript knowledge gets.

    Any suggestions, please? The lack of a loop macro is by far my biggest problem with Harlowe.
  • GtBGtB
    edited April 2016
    Did you try to use a recursive (display:) ? (display:) a passage that (display:) itself.
    It is very effective even if you need to be very carefull to set up some controls, to avoid infinite loops, etc.

    If you really need some javascript I have some code that works, I like to use Harlowe code because it is more fun but it can be done. As far as I know javascript is only usefull to return a value, it can not generate Harlowe code as you found because there is no way to recompile the code in an already displayed passage.
  • GtB wrote: »
    Did you try to use a recursive (display:)
    Some care needs to be taken when using recursive (display:) macros because each loop increases the nesting of the generated HTML elements and if the nesting becomes to deep it will cause an error.

    Take the following very basic three passage implementation of a for macro I quickly put together as an example:

    1. The setup passage:
    Using display macro recursion to implement a for loop.
    
    {
    (set: $list to (array: "One", "Two"))
    
    (set: $forInit to "$i to 1")
    (set: $forCondition to "$i <= $list's length")
    (set: $forIncrement to "$i += 1")
    (set: $forStatement to "(print: (text: $i) + ' ' + $list's ($i))<br>")
    
    }(display: "for-macro")
    
    2. The for-macro passage:
    {
    (print: "(set: " + $forInit + ")")
    
    }(display: "for-macro-inner")
    
    3. The for-macro-inner passage:
    (print: "(if: " + $forCondition + ")[" + $forStatement + "(set: " + $forIncrement + ")" + "(display: \"for-macro-inner\")" + "]")
    

    The above very basic example generates the following HTML elements with just two items in the array:
    <tw-expression type="macro" name="display">
      <tw-collapsed>
        <tw-expression type="macro" name="print">
          <tw-expression type="macro" name="set"></tw-expression>
        </tw-expression>
        <tw-expression type="macro" name="print">
          <tw-expression type="macro" name="if"></tw-expression>
          <tw-hook>
            <tw-expression type="macro" name="display">
              <tw-expression type="macro" name="print">
                <tw-expression type="macro" name="print">1 One</tw-expression><br data-raw="">
                <tw-expression type="macro" name="set"></tw-expression>
                <tw-expression type="macro" name="if"></tw-expression>
                <tw-hook>
                  <tw-expression type="macro" name="display">
                    <tw-expression type="macro" name="print">
                      <tw-expression type="macro" name="print">2 Two</tw-expression><br data-raw="">
                      <tw-expression type="macro" name="set"></tw-expression>
                      <tw-expression type="macro" name="if" class="false"></tw-expression>
                      <tw-hook></tw-hook>
                    </tw-expression>
                  </tw-expression>
                </tw-hook>
              </tw-expression>
            </tw-expression>
          </tw-hook>
        </tw-expression>
      </tw-collapsed>
    </tw-expression>
    
    ... the above has nine levels of nesting.

    Now look what happens if we change the $list array in the setup passage to have four items instead: eg (set: $list to (array: "One", "Two", "Three", "Four"))
    <tw-expression type="macro" name="display">
      <tw-collapsed>
        <tw-expression type="macro" name="print">
          <tw-expression type="macro" name="set"></tw-expression>
        </tw-expression>
        <tw-expression type="macro" name="print">
          <tw-expression type="macro" name="if"></tw-expression>
          <tw-hook>
            <tw-expression type="macro" name="display">
              <tw-expression type="macro" name="print">
                <tw-expression type="macro" name="print">1 One</tw-expression><br data-raw="">
                <tw-expression type="macro" name="set"></tw-expression>
                <tw-expression type="macro" name="if"></tw-expression>
                <tw-hook>
                  <tw-expression type="macro" name="display">
                    <tw-expression type="macro" name="print">
                      <tw-expression type="macro" name="print">2 Two</tw-expression><br data-raw="">
                      <tw-expression type="macro" name="set"></tw-expression>
                      <tw-expression type="macro" name="if"></tw-expression>
                      <tw-hook>
                        <tw-expression type="macro" name="display">
                          <tw-expression type="macro" name="print">
                            <tw-expression type="macro" name="print">3 Three</tw-expression><br data-raw="">
                            <tw-expression type="macro" name="set"></tw-expression>
                            <tw-expression type="macro" name="if"></tw-expression>
                            <tw-hook>
                              <tw-expression type="macro" name="display">
                                <tw-expression type="macro" name="print">
                                  <tw-expression type="macro" name="print">4 Four</tw-expression><br data-raw="">
                                  <tw-expression type="macro" name="set"></tw-expression>
                                  <tw-expression type="macro" name="if" class="false"></tw-expression>
                                  <tw-hook></tw-hook>
                                </tw-expression>
                              </tw-expression>
                            </tw-hook>
                          </tw-expression>
                        </tw-expression>
                      </tw-hook>
                    </tw-expression>
                  </tw-expression>
                </tw-hook>
              </tw-expression>
            </tw-expression>
          </tw-hook>
        </tw-expression>
      </tw-collapsed>
    </tw-expression>
    
    ... the above has fifteen levels of nesting.

    There are a number of ways to improve the above basic for-marco example so that it has less nesting but the issue still remains.
  • Oh well, I could see that beeing a problem. I hate XML notation, it hurts my eyes so much.
  • GtB wrote: »
    […] XML notation […]
    Pedantry incoming. That's no moon not XML. Those are simply tags for custom (HTML) elements.
  • edited April 2016
    @GtB
    As TheMadExile stated, that is not XML, that is some of the HTML dynamically generated by Harlowe's engine when the Reader viewed the setup passage. All I did was add indentation and line-breaks to make it slightly more readable.
  • GtB wrote: »
    Did you try to use a recursive (display:) ? (display:) a passage that (display:) itself.

    So far I have been working with (live:) loops but as I add more loops in more places, there are more bugs and more things that occasionally fail. Speed of execution seems to be an issue: I had a loop that didn't work unless I made it slow enough. I'm afraid of fine-tuning for my own computer and then finding out it doesn't work anywhere else.

    I fear the recursive methods because of the nesting problem (I easily hit 30 levels of nesting in my tests). Any other solution would be a blessing.
  • One way to get Harlowe to run the macros contained in the loop is to return the result of the loop as a string, and then use the (print:) macro to display it.

    So your javascript would be:
    if (! window.harloweLoop) {
      window.harloweLoop = function(times) {
        var text = "";
        var i;
        for (i=1; i<=times; i=i+1) {
          text += "(set: $d to $a's " + i + ")(display: $d)<br>";
        }
        return text;
      }
    }
    
    NB: In your version the loop started at 0, but arrays in Harlowe start at the index 1.
    I've also changed it so you can pass in how many times you want the loop to run as a parameter.

    Then in the passage you'd write:
    (set: $a to (a: "page1", "page2", "page3"))
    
    (print: window.harloweLoop(3))
    
  • Actually printing the results as a string is what I proposed a bit after posting this discussion, here, but done in pure Harlowe. I wonder if your Javascript method may have performance gains, I'll try to test it later. Thanks!
Sign In or Register to comment.