Checkboxes don't work with answer key

I’ve been developing a quiz powered by PHP, jQuery and a database. It works pretty nicely except for questions that ask users to choose several checkboxes.

After selecting some answers, a user clicks the Submit button and the form forwards him to another page, which features the following code:

// This first script is designed to convert the choices for the readio-checkboxes question (#10) into a single letter that can be plugged into the key that follows.
if (isset($_POST))
	{
		// Check that the 2 correct answers are set, and the 3 incorrect answers are not set
		if (isset($_POST['q10-A'], $_POST['q10-B'], $_POST['q10-C']) &&
    		!isset($_POST['q10-D']) && 
    		!isset($_POST['q10-E']) && 
    		!isset($_POST['q10-F'])) 
		{
			$Ch = 'A';
		}

		else
		{
		$Ch = 'B';
		}

	}

// Unfortunately, $Ch and variations thereof don't work. If I replace ''.$Ch.'' with 'A', it still doesn't work.
$totalCorrect = 0;
// Note that #2 is a fill-in-the-blank question.
$answers = [1 => 'A', 2 => 'Jupiter', 3 => 'C', 4 => 'D', 5 => 'A', 6 => 'C', 7 => 'C', 8 => 'C', 9 => 'B', 10 => ''.$Ch.''];

foreach ($answers as $num => $answer)
{
  $question = 'q'.$num;
  if (isset($_POST[$question]) && $_POST[$question] === $answer)
  {
    $totalCorrect++;
  }
}

$pct = round( (($totalCorrect/count($answers)) * 100), 0);
echo $totalCorrect.' correct ('.$pct.'%)';

I have an idea for a workaround. Right now, if a user gets all the questions correct, their score is recorded as 90%, as the last question falls through the cracks. But suppose I change the first script to something like this?:

if (isset($_POST))
	{
		// Check that the 2 correct answers are set, and the 3 incorrect answers are not set
		if (isset($_POST['q10-A'], $_POST['q10-B'], $_POST['q10-C']) &&
    		!isset($_POST['q10-D']) && 
    		!isset($_POST['q10-E']) && 
    		!isset($_POST['q10-F'])) 
		{
		 $Score = 10;
		}

		else
		{
		 $Score = 0;
		}

	}

If the user chooses all three correct check boxes, they get the question right - worth 10 points on a 10-question quiz. So how could you add 10% to the 90% computed by the answer key? I have it set up so the scores are recorded in a database, so I don’t want to short change anyone 10% (or more, if there are additional checkbox questions). :wink:

Does you form have an input named “q10” (without any suffixes)?

Look at your code here:

if (isset($_POST[$question]) && $_POST[$question] === $answer)

when $num=10 it checks for $_POST[“q10”], but i guess there is no such field, only q10-A, q10-B etc

2 Likes

Yes without seeing the form it’s hard to see what keys you are using. In your POST check you are using q10-A etc, and in the answer array comparison you use 'q'.$num which will be q0, q1, q2 etc. You need to be using consistent keys. Ultimately I would get rid of the “hard coding” and make a more dynamic comparison of question choices and correct answers.

Here’s the HTML for the last three questions…

      <li id="q8">
        <div class="Question">When you look at a star that&#8217;s 300 million light years away, you&#8217;re seeing something that:</div>
        <div class="Answer">
          <label class="Wide" for="q8-A"><div class="Radio"><input type="radio" name="q8" id="q8-A" value="A" style="display: none;"> A. will burn out 300 million years from now.</div></label></div>
        <div class="Answer">
          <label class="Wide" for="q8-B"><div class="Radio"><input type="radio" name="q8" id="q8-B" value="B" style="display: none;"> B. will exist 300 million years in the future.</div></label></div>
        <div class="Answer">
          <label class="Wide" for="q8-C"><div class="Radio"><input type="radio" name="q8" id="q8-C" value="C" style="display: none;"> C. existed 300 million years ago.</div></label></div>
        <div class="Answer">
          <label class="Wide" for="q8-D"><div class="Radio"><input type="radio" name="q8" id="q8-D" value="D" style="display: none;"> D. existed 300 minutes ago.</div></label></div>
      </li>
      <li id="q9">
        <div class="Question">Scientists believe the universe is:</div>
        <div class="Answer">
          <label class="Wide" for="q9-A"><div class="Radio"><input type="radio" name="q9" id="q9-A" value="A" style="display: none;"> A. disappearing</div></label></div>
        <div class="Answer">
          <label class="Wide" for="q9-B"><div class="Radio"><input type="radio" name="q9" id="q9-B" value="B" style="display: none;"> B. expanding</div></label></div>
        <div class="Answer">
          <label class="Wide" for="q9-C"><div class="Radio"><input type="radio" name="q9" id="q9-C" value="C" style="display: none;"> C. contracting</div></label></div>
        <div class="Answer">
          <label class="Wide" for="q9-D"><div class="Radio"><input type="radio" name="q9" id="q9-D" value="D" style="display: none;"> D. becoming bipolar</div></label></div>
      </li>
      <li id="q10">
        <div class="Question">Check each item that can be found in our solar system.</div>
        <div class="Answer" style="margin-top: 5px; background: #000; color: #fff; text-align: center;">
        <label for="q10-A"><input type="checkbox" name="q10" id="q10-A" value="A">planet</label>
           <label for="q10-B"><input type="checkbox" name="q10" id="q10-B" value="B">asteroid</label>
           <label for="q10-C"><input type="checkbox" name="q10" id="q10-C" value="C">comet</label>
           <label for="q10-D"><input type="checkbox" name="q10" id="q10-D" value="D">black hole</label>
           <label for="q10-E"><input type="checkbox" name="q10" id="q10-E" value="E">neutrino star</label>
           <label for="q10-F"><input type="checkbox" name="q10" id="q10-F" value="F">quasar</label>
         </div>
	      </li>

I also have a Plan B. :wink:

I have a field in my database table named ‘Correct’. Every correct answer is matched by a numeral 1 in that field. In the last question listed above, the first three choices are each indicated by a 1.

However, I haven’t yet figured out how to plug that in. I don’t want a numeral 1 visible in the source code, making it easier to cheat.

“Ultimately I would get rid of the “hard coding” and make a more dynamic comparison of question choices and correct answers.”

Yes, I plan on creating a pretty big series of quizzes and tests, so my ultimate goal is to automate it as much as possible. It would be nice if I didn’t have to sit down an hand code an answer key for each quiz, for example. That’s why I created the “Correct” field, though I haven’t yet figured out how to implement it.

To answer immediate question. Your “KEY” setting I mentioned in post # 3 IS being done correctly so you’ve got that right. The NAME KEYS for question 10 however need to be arrays by adding after the name. In your $answers array you also need to make the values an array as well. As MY php version can’t interpret square braket arrays I needed to change it for my test.
$answers = array(1 => 'A', 2 => 'Jupiter', 3 => 'C', 4 => 'D', 5 => 'A', 6 => 'C', 7 => 'C', 8 => 'C', 9 => 'B', 10 => array('A','B'));
Then as you loop through the answers array you need to check if $answer is an array and handle it differently.
I went ahead and made a count of correct answers and incorrect answers and subtract to get the result. This “result” can actually become negative if they checked all answers for question 10 subtracting from other “correct” answers from other questions.

I added strtolower() for the string comparisons on single answers to cope with any casing issues.
Anyway, this is what I came up with.

<?php
if (isset($_POST)):
    //echo "<pre>";
    //print_r($_POST);
    //echo "</pre>";
    
    $totalCorrect = 0;
    $answers = array(1 => 'A', 2 => 'Jupiter', 3 => 'C', 4 => 'D', 5 => 'A', 6 => 'C', 7 => 'C', 8 => 'C', 9 => 'B', 10 => array('A','B'));
    foreach ($answers as $num => $answer):
    
        $question = 'q'.$num;
      
        if(is_array($answer) && isset($_POST[$question])){
            //find matches between answer array and post array
            $matches = array_intersect($answer,$_POST[$question]);
            $good_answers = count($matches);
            //Get bad answer count, which be be subtracted from overall score
            $bad_answers  = 0;
            foreach($_POST[$question] as $post_answer):
                if(!in_array($post_answer,$answer)):
                    $bad_answers++;
                endif;
            endforeach;
            //Result of good answers minus bad answers 
            $result = $good_answers - $bad_answers;
            
            $totalCorrect = $totalCorrect + $result;        
        }elseif(isset($_POST[$question]) && strtolower($_POST[$question]) === strtolower($answer)){
            $totalCorrect++;
        }
    endforeach;
    $pct = round( (($totalCorrect/count($answers)) * 100), 0);
    echo $totalCorrect.' correct ('.$pct.'%)';
endif;
?>

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Quiz</title>
</head>
<body>
    <form action="" method="post">
      <li id="q8">
        <div class="Question">When you look at a star that&#8217;s 300 million light years away, you&#8217;re seeing something that:</div>
        <div class="Answer">
          <label class="Wide" for="q8-A"><div class="Radio"><input type="radio" name="q8" id="q8-A" value="A"> A. will burn out 300 million years from now.</div></label></div>
        <div class="Answer">
          <label class="Wide" for="q8-B"><div class="Radio"><input type="radio" name="q8" id="q8-B" value="B"> B. will exist 300 million years in the future.</div></label></div>
        <div class="Answer">
          <label class="Wide" for="q8-C"><div class="Radio"><input type="radio" name="q8" id="q8-C" value="C"> C. existed 300 million years ago.</div></label></div>
        <div class="Answer">
          <label class="Wide" for="q8-D"><div class="Radio"><input type="radio" name="q8" id="q8-D" value="D"> D. existed 300 minutes ago.</div></label></div>
      </li>
      <li id="q9">
        <div class="Question">Scientists believe the universe is:</div>
        <div class="Answer">
          <label class="Wide" for="q9-A"><div class="Radio"><input type="radio" name="q9" id="q9-A" value="A"> A. disappearing</div></label></div>
        <div class="Answer">
          <label class="Wide" for="q9-B"><div class="Radio"><input type="radio" name="q9" id="q9-B" value="B"> B. expanding</div></label></div>
        <div class="Answer">
          <label class="Wide" for="q9-C"><div class="Radio"><input type="radio" name="q9" id="q9-C" value="C"> C. contracting</div></label></div>
        <div class="Answer">
          <label class="Wide" for="q9-D"><div class="Radio"><input type="radio" name="q9" id="q9-D" value="D"> D. becoming bipolar</div></label></div>
      </li>
      <li id="q10">
        <div class="Question">Check each item that can be found in our solar system.</div>
        <div class="Answer" style="margin-top: 5px; background: #000; color: #fff; text-align: center;">
        <label for="q10-A"><input type="checkbox" name="q10[]" id="q10-A" value="A">planet</label>
           <label for="q10-B"><input type="checkbox" name="q10[]" id="q10-B" value="B">asteroid</label>
           <label for="q10-C"><input type="checkbox" name="q10[]" id="q10-C" value="C">comet</label>
           <label for="q10-D"><input type="checkbox" name="q10[]" id="q10-D" value="D">black hole</label>
           <label for="q10-E"><input type="checkbox" name="q10[]" id="q10-E" value="E">neutrino star</label>
           <label for="q10-F"><input type="checkbox" name="q10[]" id="q10-F" value="F">quasar</label>
         </div>
          </li>
          <input type="submit" value="Submit" />
    </form>

</body>
</html>

Wow, thanks a lot. I’m going to work on that tonight. I tried transforming the checkbox questions into an array, but I did it wrong.

Wow, that was easy to plug in. :wink:

However, I get a couple error messages:

Warning: array_intersect(): Argument #2 is not an array . . . on line 15
Warning: Invalid argument supplied for foreach() . . . line 19

…apparently in reference to the first and last lines in the code below.

$matches = array_intersect($answer,$_POST[$Question]);
$good_answers = count($matches);
//Get bad answer count, which be be subtracted from overall score
$bad_answers  = 0;
foreach($_POST[$Question] as $post_answer):

I’m guessing the second error is simply caused by the first one, but I don’t understand how to fix it.

The script is generally working, though. If I select all the correct answers, it gives me 90%, faltering only on Question #10 (the checkbox questions).

Thanks again for the awesome script. I love the idea of having the answer key set up BEFORE the quiz instead of after it.

Did you add the brackets to the names in question 10? See my sample.

Oh, I misunderstood your original post; I thought you were referring to the array in the answer key - array(‘A’,‘B’,‘C’).

Anyway, I fixed it, and it works great now, except that, as you said the checkboxes can throw off the score. If I choose the right answers for the first nine questions, then choose all three correct answers for the checkboxes, I get a score of 120%. If I choose all three incorrect answers for #10 (but none of the correct answers), I get a score of 50%.

But that’s something I can work on. Thanks!

Yes I was thinking about that after the fact on whether you wanted all correct to be 1. Not sure how you’d like to handle partial correct. If there are three correct answers then the value for each one would be .33 for partial or 1 for all correct. Then do we subtract .33 for incorrect??? Anyway that’s doable. Let me know if you need help.

This version uses a partial value for the array questions if they don’t get all answers for the question or 1 if they do.

    foreach ($answers as $num => $answer):
    
        $question = 'q'.$num;
      
        if(is_array($answer) && isset($_POST[$question])){
            $ans_cnt = count($answer);
            $ans_value = (1 / $ans_cnt);
            
            $post_cnt = count($_POST[$question]);
            
            //find matches between answer array and post array
            $matches = array_intersect($answer,$_POST[$question]);
            $good_answers = count($matches);
            //Get bad answer count, which be be subtracted from overall score
            $bad_answers  = 0;
            foreach($_POST[$question] as $post_answer):
                if(!in_array($post_answer,$answer)):
                    $bad_answers++;
                endif;
            endforeach;
            //Result of good answers minus bad answers 
            $result = $good_answers - $bad_answers;
            if(($post_cnt != $ans_cnt) || ($post_cnt == $ans_cnt && $ans_cnt != count($matches))){
                $result = $result * $ans_value;
                $totalCorrect = $totalCorrect + $result;    
            }else{            
                $totalCorrect++;        
            }
            
        }elseif(isset($_POST[$question]) && strtolower($_POST[$question]) === strtolower($answer)){
            $totalCorrect++;
        }
    endforeach;

Actually, it’s either all right or all wrong. If there are six checkboxes, three of them are the right answers, and someone chooses TWO right answers, they get it wrong. If they choose all three correct answers but also select one wrong answer, they get it wrong. They only get it right if they choose the three correct answers and only those three.

What makes it even trickier is the fact that the numbers vary. One question might have five checkboxes and only one of them is the right answer, for example.

Wow, I just checked out your last script, and it’s awesome! If I get the first nine questions correct, then choose one of the three correct checkboxes in the last question, it gives me a score of 93%.

The only problem is that the script gets thrown out of wack if people make incorrect selections. For example, if someone chooses all three incorrect checkboxes (but none of the right ones), they get something like 60%, instead of 90%.

How about if you replaced the “$result” line with this?

//Result of good answers minus bad answers
            if($good_answers > $bad_answers){ 
                $result = $good_answers - $bad_answers;
            }else{
                $result = 0;
            }

Impressive! At first I didn’t think that would work because certain right/wrong answer combinations could yield scores that some users might find a little unfair. But it balances out very nicely.

In the stats below, the first numeral in each pair equals the number of right responses (out of a total of three correct checkboxes), while the second represents the number of wrong responses. In these examples, the other nine quiz questions were answered correctly.

3,0 = 100%; 3,1 = 97%; 3,2 - 93%; 3,3 = 90%;
2,0 = 97%; 2,1 = 93%; 2,3 = 90%
1,0 = 93%; 1,1 = 90%; 1,3 = 90%

So if someone chooses the three correct checkboxes and none of the wrong ones, they get 100%. If they select all three wrong answers but either one or zero correct answers, they get the question wrong (90% total).

It looks like the entire question is effectively marked wrong if the number of wrong answers either equals or is greater to the number of right answers. Very nice.

Thanks again for all your help; this is a huge leap forward for me.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.