Howdy, Stranger!

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

Newbie here! How do you randomize choices?

Hello!

I am trying to randomize my choices in a passage. I am aware of either() but I realize that if I use either(), the correct choice would appear twice or it won't appear at all. How do you make it so that the choices won't repeat itself?

Ex. He went left! I better follow him!

(Randomized)
Wrong
Correct
Wrong

Comments

  • You need to state which story format you are using, as answers can be different for each one.

    When you say randomized do you mean:
    a. You want all the options to appear but in a random order.
    b. You want one or more of the incorrect options to not appear but the options that do appear are always in the same order.
    c. A combination of points A and B.
    d. Something else....
  • On first blush, it sounds to me like jwhs wants to shuffle the order of the choices so that players cannot learn the order. My question would be do they want the order of the choices themselves randomized or which choice is the correct one, or both?

    And as greyelf says, knowing the story format you're using is kind of important.
  • It's more to a. greyelf but I don't want the same choice to pop up. I was using either() and it ended up like this

    "Wrong"
    "Correct"
    "Correct"

    What I am trying to do is make sure that the correct choice is not repeated and that the order of the choices are randomized if that make sense. And I'm not sure what you mean by story format. I'm using Twine 1.4.2 if that's what you mean.

  • Story menu > Story Format. By default, it's Sugarcane.
  • Oh yeah it's sugarcane
  • Any news on that subject?
    Maybe I should start a different thread, but really the subject is the same : how to randomize choices given to a player so they can't memorize a pattern.

    On the dev side the problem becomes : how to randomize Twine links in a passage?

    I am using Sugarcube-2 and I wrote some javascript code that randomizes an array of arbitrary length, comprised of strings, into <p></p> wrapped html code. I also named each answer slot so that one could stylize each answer individually with a CSS, if needed.
    function getRandomInt(min, max) {
        return Math.floor(Math.random() * (max - min)) + min;
    }
    
    function randomHTML(source) {
        var slotNumber = source.length;
        var copySource = source.slice();
        var content = "";
        for (var i = 0; i < slotNumber; i++) {
            var slot = "answerSlot" + (i+1);
            var index = getRandomInt(0, 10000) % (slotNumber-i);
            var removed = copySource[index];
            content = content + '<p id="' + slot + '">' + removed + '</p>' + "\n";
            copySource.splice(index, 1);
        }
    
        return content;
    }
    

    When you feed a dummy example to that function with 4 choices labeled option 1, 2, 3, and 4, like this :
    var source = [ "option 1", "option 2", "option 3", "option 4" ];
    var content = randomHTML(source);
    

    logging the value of the content variable above in the console, I get the following :
    <p id="answerSlot1">option 4</p>
    <p id="answerSlot2">option 1</p>
    <p id="answerSlot3">option 2</p>
    <p id="answerSlot4">option 3</p>
    

    and of course different order each time I run it.

    Now that's all good and it works but that does not interface with Twine's passages, which require an extra mile of coding.

    How can I integrate such typical Twine player's choices :
    passage1
    passage2
    passage3
    passage4

    to translate into the resulting HTML, knowing that the above Twine's passages transforms eventually to :
    <a data-passage="passage1" class="link-internal" tabindex="0">option 1</a>
    

    Ideally, my javascript function should (use regex and) parse the passage's links and be able to generate something like this :
    <a data-passage="passage4" class="link-internal" tabindex="0">option 4</a>
    <a data-passage="passage1" class="link-internal" tabindex="0">option 1</a>
    <a data-passage="passage2" class="link-internal" tabindex="0">option 2</a>
    <a data-passage="passage3" class="link-internal" tabindex="0">option 3</a>
    

    I am not sure this is the right way to go, but to be useful for devs, the macro would look something like this:
    Macro.add("randomizeChoices", {
        version: { major: 1, minor: 0, patch: 0 },
        skipArgs: false,
        handler: function() {
    		if (this.args.length !== 1) {
    			return this.error("Error using <<randomizeChoices>> : it needs 1 parameter! You put " + this.args.length + "!");
    		}
    
    		return randomHTML(this.args[0]);
        }
    });
    

    and use it in a passage like that :
    <<randomizeChoices "[[option1|passage1]] [[option2|passage2]] [[option3|passage3]] [[option4|passage4]]">>
    

    Is there a better way to do this ?
  • Yes, there is a better way.

    First. You don't need getRandomInt(), SugarCube has the built-in random() function (among many functions and methods which deal with randomness in some way). I'd suggest reading the documentation before deciding to reinvent the wheel.

    Second. I'm unsure why you're manually recreating <a> links in exactly the same way as SugarCube does automatically. You probably don't want to do that.


    Anyway. Let's say you initialize your array something like the following:
    <<set $choices to [
    	"[[option 1|passage1]]",
    	"[[option 2|passage2]]",
    	"[[option 3|passage3]]",
    	"[[option 4|passage4]]"
    ]>>
    
    You can destructively print out the randomized links like so:
    /* Print the links (will empty the array). */
    <<for $choices.length gt 0>>
    <<print $choices.pluck()>>
    <</for>>
    
    That does destroy the $choices array, however, so if you need it intact afterwards, you can print it non-destructively like so:
    /* Shuffle the array (this could be done during initialization). */
    <<set $choices to $choices.shuffle()>>
    
    /* Print the links. */
    <<for $i to 0; $i lt $choices.length; ++$i>>
    <<print $choices[$i]>>
    <</for>><<unset $i>>
    

    If you wanted them wrapped in additional markup, for example an unordered list, you might do something like the following:
    <ul>
    <<for $i to 0; $i lt $choices.length; ++$i>>\
    <li><<print $choices[$i]>></li>
    <</for>><<unset $i>>
    </ul>
    
    You should be able to figure the destructive version out from there, if you'd prefer that.
  • @vancekic: Further to TheMadExile's solutions.

    You can use a widget to implement a similar macro to your <<randomizeChoices>> one.

    eg. The following widget is based on TheMadExile's pluck based solution:
    <<widget "randomizeChoices">>
    	<<if $args[0]>>\
    		<<for $args[0].length gt 0>>\
    			<<print $args[0].pluck()>>
    		<</for>>\
    	<</if>>\
    <</widget>>
    
    ... you use the following to call it:
    <<set $choices to [
    	"[[option 1|passage1]]",
    	"[[option 2|passage2]]",
    	"[[option 3|passage3]]",
    	"[[option 4|passage4]]"
    ]>>\
    
    <<randomizeChoices $choices>>
    
    note: I added indents and line-breaks to the above two examples for readability, they can be safely removed.
  • Thanks for your help, both of you ;)

    It's not so much reinvent the wheel, rather investigate how sugarcube interfaces with javascript through macro, widgets and how this all works (the overall goal is to be fluent in sugarcube).

    I have read most of the documentation but I tend to lean towards javascript because it's naturally more powerful (because 1/ sugarcube is built on it and 2/ I'm more comfortable with js). But obviously, it's expressive enough (and I knew that from reading the doc) that one can do a lot of things with it. It naturally wasn't my first choice since I can express myself better with another means.

    The widget / macro wrapper was definitely my intention because I want all my passages to present randomized choices to the player.

    Thanks a lot again ;)
  • edited November 2015
    Hi again!

    I just hit another (stupid and easy) problem, but can't seem to solve it!
    Following this thread, say I want some of my choices-to-be-randomized to depend on a gender-like widget, as defined in the documentation :
    <<widget "he">><<if $pcSex eq "male">>he<<elseif $pcSex eq "female">>she<<else>>it<</if>><</widget>>
    

    Now of course this won't work with :
    <<set $choices to [
    	"[[<<he>> wants option 1|passage1]]",
    	"[[<<he>> wants option 2|passage2]]",
    	"[[<<he>> wants option 3|passage3]]",
    	"[[<<he>> wants option 4|passage4]]"
    ]>>
    

    I tried to isolate the use of the widget in a variable but it all boils down to not being able to capture the widget's value and encapsulate that result in quotes, in order to then insert it as a choice in the array of choices to be randomized.

    It must be obvious but I don't see it. Can anyone help ?
  • vancekic wrote: »
    It must be obvious but I don't see it. Can anyone help ?
    As I understand things there are two issues with what you are trying to do:

    1. A widget does not return a value, it can on the other hand output content to the page.

    2. A Markup link does not support embedding a macro call / variable the way you are trying to do. The following test show this:
    [[<<he>> wants option 1|passage1]]
    

    @TheMadExile would be a better person to answer this but the only ways I could think of to resolve the problem all revolved around different ways to generate the string values that are to be converted into markup links. The following is one such method:

    a. Change your he widget to assign a value to a $pronoun variable:
    <<widget "he">>
    	<<if $pcSex eq "male">>
    		<<set $pronoun to "he">>
    	<<elseif $pcSex eq "female">>
    		<<set $pronoun to "she">>
    	<<else>>
    		<<set $pronoun to "it">>
    	<</if>>
    <</widget>>
    
    b. Use the $pronoun variable to build your possible choices:
    // The pcSex is initialized somewhere else!
    <<set $pcSex to "male">>
    
    <<he>>
    <<set $choices to []>>
    <<set $choices.push("[[" + $pronoun + " wants option 1|passage1]]")>>
    <<set $choices.push("[[" + $pronoun + " wants option 2|passage2]]")>>
    <<set $choices.push("[[" + $pronoun + " wants option 3|passage3]]")>>
    <<set $choices.push("[[" + $pronoun + " wants option 4|passage4]]")>>
    
    <<randomizeChoices $choices>>
    
  • From the SugarCube 2 documentation on wiki links:
    […] may be either plain text or any valid TwineScript expression […]
    They, unfortunately, do not accept markup.

    The solution suggested by greyelf is one way to do it. Another would be to write small companion functions similar to the widgets. For example: (Twine 1: goes in a script tagged passage; Twine 2: goes in Story JavaScript)
    window.he = function () {
    	switch (State.variables.pcSex) {
    	case "male":
    		return "he";
    	case "female":
    		return "she";
    	default:
    		return "it";
    	}
    };
    
    You could use it like so:
    <<set $choices to [
    	"[[" + he() + " wants option 1|passage1]]",
    	"[[" + he() + " wants option 2|passage2]]",
    	"[[" + he() + " wants option 3|passage3]]",
    	"[[" + he() + " wants option 4|passage4]]"
    ]>>
    

    If you're using the widgets, you could then refactor them to use the functions via the <<print>> macro, so that you'd only have one set of pronoun code to maintain. For example: (I'm using the <<=>> alias here)
    <<widget "he">><<= he()>><</widget>>
    
    Of course, in that case, you could just use the <<print>> macro directly, like so:
    <<= he()>>
    
  • Thanks @greyelf and @TheMadExile
    I'll use a mix of both solutions, namely the framing of @greyelf and the form of @TheMadExile ;) Because ultimately here, I want as little code as possible in the passages, I'll go something along those lines (avoiding the many push() statements):
    <<set $choices to [
        "[[Suddenly, " + $player_3rd + " tells " + $enemy_3rd_sing + " to get out|passage1]]",
        "[[Magically, " + $player_3rd + " vanishes before " + $possessive_pronoun + " eyes|passage2]]"
    ]>>
    

    the good thing is, those variables don't change as long as genders don't change in the game. So far I am setting them to masculin by default, and updating them once and only once as soon the genders are known, before the story even starts.

    For my own curiosity (since the documentation isn't that verbose on the subject), would it be a good place to use <<remember>> on those variables ? Or does it serve a totally different purpose?

    Thanks again guys, great help!
  • vancekic wrote: »
    For my own curiosity (since the documentation isn't that verbose on the subject), would it be a good place to use <<remember>> on those variables ? Or does it serve a totally different purpose?
    I don't know, I think the SugarCube 2 documentation on <<remember>> is fairly clear, if not verbose. From said documentation:
    Functionally identical to <<set>>, save that it also causes the values of $variables to persist over page reloads, game restarts, and even browser restarts.

    Note: Generally, you do not need, or want, to use <<remember>>, as it is only useful in very specific circumstances and problematic in most others. Unless you know that you need to use it, you very likely do not.
    Especially poignant is the included notice.

    Within a single playthrough (incl. saves) all $variables are automatically "remembered". So, unless the gender choice is one that the player should only ever make once (even across multiple playthroughs/restarts), then you definitely do not want to use <<remember>>. It is only useful when you want a $variable to persist across different playthroughs/restarts.

    On the flip side, an achievement system would be an example of a reasonable use of <<remember>>.
  • I was never sure to trust myself in knowing that I need to use it.
    Thanks for the added details ;)
Sign In or Register to comment.