Howdy, Stranger!

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

Problem with checkbox macro // array (SC2)

edited October 2016 in Help! with 2.0
Hey everybody,

I am having a hard time understanding how to use the <<checkbox>> macro in SC2:
<<set $rightAnswer to [true, false, false, true, false, false, false, true, false]>>

<h3>Which of these ingredients do you need for the potion?</h3>
<<checkbox "$userInput[0]" false true>>stinging nettle
<<checkbox "$userInput[1]" false true>>mandrake
<<checkbox "$userInput[2]" false true>>yellow cress
<<checkbox "$userInput[3]" false true>>fly agaric
<<checkbox "$userInput[4]" false true>>asarabacca
<<checkbox "$userInput[5]" false true>>woodruff
<<checkbox "$userInput[6]" false true>>garlic
<<checkbox "$userInput[7]" false true>>lady's mantle
<<checkbox "$userInput[8]" false true>>puffball

<<button Check>>
	<<if $userInput == $rightAnswer>>
		<<goto "correct">>
	<<else>>
		<<goto "false">>
	<</if>>
<</button>>

I try to compare the two arrays, but the condition in <<if>> is never met, as $userInput remains empty.
What am I doing wrong?

Comments

  • richVIE wrote: »
    I try to compare the two arrays, but the condition in <<if>> is never met, as $userInput remains empty.
    What am I doing wrong?
    First. You don't seem to be initializing the $userInput story variable as an array anywhere before attempting to use it as one. Initialize your variables.

    Second. Trying to compare two reference types as you would two primitive types. Comparing two variables containing references with an equality operator will only tell you if the references are equal. In this case, you want to compare the contents of the arrays.

    Third. You should probably be using a temporary variable for the correct answers variable, rather than a story variable. Unless you're using it later on, there's no reason for it to be a story variable—you're bloating your history for no good reason.

    I suggest the following for the initial setup:
    <<set 
    	$userInput to [],
    	_correct   to [true, false, false, true, false, false, false, true, false]
    >>\
    <h3>Which of these ingredients do you need for the potion?</h3>
    <label><<checkbox "$userInput[0]" false true>> Stinging Nettle</label>
    <label><<checkbox "$userInput[1]" false true>> Mandrake</label>
    <label><<checkbox "$userInput[2]" false true>> Yellow Cress</label>
    <label><<checkbox "$userInput[3]" false true>> Fly Agaric</label>
    <label><<checkbox "$userInput[4]" false true>> Asarabacca</label>
    <label><<checkbox "$userInput[5]" false true>> Woodruff</label>
    <label><<checkbox "$userInput[6]" false true>> Garlic</label>
    <label><<checkbox "$userInput[7]" false true>> Lady's Mantle</label>
    <label><<checkbox "$userInput[8]" false true>> Puffball</label>
    


    And the following for your "Check" button:
    <<button "Check">>
    	<<set _allAreCorrect to true>>
    
    	<<for _i to 0; _i lt $userInput.length; _i++>>
    		<<if $userInput[_i] isnot _correct[_i]>>
    			<<set _allAreCorrect to false>>
    			<<break>>
    		<</if>>
    	<</for>>
    
    	<<if _allAreCorrect>>
    		<<goto "correct">>
    	<<else>>
    		<<goto "false">>
    	<</if>>
    <</button>>
    


    An alternative, and shorter, way to code the "Check" button—if you don't mind using an Array method and an anonymous function—would be something like the following:
    <<button "Check">>
    	<<if $userInput.some(function (val, i) { return val isnot _correct[i]; })>>
    		<<goto "false">>
    	<<else>>
    		<<goto "correct">>
    	<</if>>
    <</button>>
    
  • Hey, TME,

    I have been using this button for quite some time now - thank you very much. But I was wondering if there is a way to call the function each time a checkbox is checked or unchecked (like an onclick-behavior on a container element or so).

    All the other buttons I use just compare two variables (e.g. _userInput and _rightAnswer), so for the multiple-choice button I have to use the above code, which makes it somehow inelegant. If the above function checked if the right options were selected, I could just go and look if _userInput and _rightAnswer both are set to true.

    (That's what "more elegant" looks like in my world at least :-) )

    best,

    richVIE
  • edited July 12
    The current behavior forwards the player to a passage—"false" or "correct"—upon checking their answer/solution. If you had it fire every time they changed the value of any of the checkboxes, then the first change would likely forward them to the "false" passage.

    Unless you change that basic behavior, which you could, it's not going to work out very well.
  • Sorry, I obviously did not make myself clear enough.

    If we leave the <<goto>> behaviour at the end of the original code aside:

    Let´s say _allAreCorrect is set to false per default.
    I would like the function to be called each time a checkbox is checked or unchecked and if the two arrays match, set _allAreCorrect to true. (and set it back to false if another false option is checked)

    From there, I can have the <<button>>-code take care of the proper <<goto>> behaviour.

    Just to make sure: In the moment, all the correct options (and only the correct ones) are checked, nothing happens. The player should have to make their choice and then click the button to see what comes next.

  • edited July 12
    Okay. I misread that post. Mea culpa.

    Just to ensure that I'm clear on this. You want to change the code you have now, which is working exactly as intended, because you find that having the code within button itself is somehow inelegant?
  • With my very limited coding experience, I do not give my own perception on what is "elegantly coded" too much importance.

    Please allow to elaborate on my main motivation:

    In a quiz-game I have some 50+ passages, all of which finish with a button that sends them to the next level - according to their performance.

    Most of these question-passages have either single-choice (radiobutton) or fill-in-the-gap (textfield)-questions, both of which allow me to use a button, that just compares the players answer with a _correct variable stored within each passage.

    For about ten multiple-choice questions (checkboxes) I use the button code you provided.

    This works as I expected it to, but the fact that I am using two different button codes forces me (I guess?) to put the respective button code at the bottom of each passage,
    sometimes the one for the first type, sometimes the one for the checkboxes. Loads of repeated code.

    I reckoned, if the variable _allAreCorrect could be set dynamically upon checking or unchecking a checkbox, I could just use one single button code, which could go like
    <<button "Check answer">>
    	<<if $userInput eq _correct || _allAreCorrect>>
    		<<goto "passage A">>
    	<<else>>
    		<<goto "passage B">>
    	<</if>>
    <</button>>
    

    By using just one button, instead of putting the button code at the bottom of each passage, I could put it in a PassageFooter special passage, this way avoiding repeated code.

    ps. the way the next passage is determined is more complex than just goto passage A or B, this example is just for the sake of simplicity.
  • That makes more sense. I don't have time now, but I'll post an example later.
  • Using the <<checkbox>> doesn't really help you here, so I've switched to minimal HTML checkboxes.

    Markup
    <<set 
    	/* Set this to the correct sequence. */
    	_sequence  to [true, false, false, true, false, false, false, true, false],
    	
    	/* These will, probably, not need to be modified. */
    	_selection to _sequence.map(function () { return false; }),
    	_correct   to true,
    	$userInput to false
    >>\
    <h3>Which of these ingredients do you need for the potion?</h3>
    <label><input type="checkbox" tabindex="0"> Stinging Nettle</label>
    <label><input type="checkbox" tabindex="0"> Mandrake</label>
    <label><input type="checkbox" tabindex="0"> Yellow Cress</label>
    <label><input type="checkbox" tabindex="0"> Fly Agaric</label>
    <label><input type="checkbox" tabindex="0"> Asarabacca</label>
    <label><input type="checkbox" tabindex="0"> Woodruff</label>
    <label><input type="checkbox" tabindex="0"> Garlic</label>
    <label><input type="checkbox" tabindex="0"> Lady's Mantle</label>
    <label><input type="checkbox" tabindex="0"> Puffball</label>
    
    And the check button:
    <<button "Check answer">>
    	<<if $userInput is _correct>>
    		<<goto "passage A">>
    	<<else>>
    		<<goto "passage B">>
    	<</if>>
    <</button>>
    


    JavaScript (Twine 2: goes in Story JavaScript; Twine 1: goes in script-tagged passage)
    postrender['checkbox-summation'] = function (content) {
    	function equiv(a, b) {
    		return a.every(function (aVal, i) {
    			return aVal === b[i];
    		});
    	}
    
    	var sv = State.variables;
    	var st = State.temporary;
    
    	if (Array.isArray(st.sequence) && st.sequence.length > 0) {
    		$(content)
    			.find('input[type="checkbox"]')
    			.each(function (i) {
    				$(this)
    					.on('change', function () {
    						st.selection[i] = this.checked;
    						sv.userInput = equiv(st.sequence, st.selection);
    					});
    			});
    	}
    };
    
Sign In or Register to comment.