Need help simple challenge and response form without login

0
down vote

favorite

I am making a puzzle game where the user must input the solution (a string) of the level before going to the next level. I’m using a simple form, which I check to validate the answer, but I have not been able to get any form to work at all.

A couple things about the way I have it set up. Each level url is stored in the database as “chapter_location” because I don’t want the user to simply guess the next level’s url. Second, I have the answers stored in the database as “chapter_solution”.

This is the php for the page that’s supposed to display the content of each level. If I try to access a page by the location, it loads the page and automatically starts trying to validate the form, then it simply spits out an empty redirect url http://localhost/chapter.php?location= which causes a redirect loop error in any browser. I don’t know why it would try to automatically redirect before even displaying the page. Any ideas how to fix this? I have been searching tutorials for days, trying various methods and I can’t get anything to work.

<?php

include_once('includes/connection.php');
include_once('includes/chapter.php');
include('includes/header.php');

$chapter = new Chapter;

if (isset($_GET['location'])) {
    $location = $_GET['location'];
    $data = $chapter->fetch_data($location);

    ?>

            <div id ="wrapper">
                <h1><?php echo $data['chapter_title']; ?></h1> 
                    <section>

                        <p><?php echo $data['chapter_content']; ?></p>

                    </section>
            </div>

                    <div id ="solve-form">
                        <form action="chapter.php" method="post" autocomplete="off">
                        <input type="text" name="guess" placeholder="Solution:" />
                        <input type="submit" value="Solve" />
                        </form>
                    </div>

                    <?php
                    // Retrieve correct answer from the database stored as chapter_solution which matches current page location
                    $sql = "SELECT chapter_solution FROM chapters WHERE chapter_location = '$location'";
                    $result = mysql_query($sql);

                    //Validate user's solution guess against the answer stored in chapter_solution
                    if (isset($_POST['guess']) !== $result){
                    $error = 'You did not get it right. Try again.';
                    } else {
                        // increment the current chapter_id by 1 to get the next level's url from chapter_location
                        $next_id = ++$data['chapter_id'];
                        $query = 'SELECT chapter_location FROM chapters WHERE chapter_id = "$next_id"';
                        $next_url = mysql_query($query);

                        // Redirect to the url (chapter_location) of the next level. 
                        header('Location: chapter.php?location=' . $next_url);
                    }

include('includes/footer.php');

} else {
    header('Location: index.php');
    exit();
}

?>

i think you should wrap your validation code with

if (isset($_POST['submit'])) { .... }

so you will only validate form if it was already submitted

1 Like

also, this condition looks strange:

if (isset($_POST['guess']) !== $result)

isset() returns only true or false (indicating variable is exists), while $result can be true, false or resource
if you want to compare values from $_POST and your database you should do it like that:

$correct = mysql_fetch_assoc($result);    
$guess = $_POST['guess'];
if ($guess == $correct['chapter_solution']) { /* solution is correct */ }
1 Like

You need to change your single and double quotes here.

$query = "SELECT chapter_location FROM chapters WHERE chapter_id = '$next_id'";
1 Like

Thanks for all the help, everyone, I implemented all the changes and I can now view the page and submit on the form without any problems. The only issue I need to fix now is getting the page redirect correctly. I think I can dig in and get that to work, I just have to figure out how to pull the chapter_id of the next level and use the location as the variable in the redirect.

Also, the form sends me to index.php no matter if I enter a correct response or a wrong one.

Fix the action:

<form action="chapter.php?location=<?php echo $location; ?>" method="post" autocomplete="off">

you have to pass $_GET[‘location’] when you submit the form, otherwise this condition in the beginning of file returns false:

if (isset($_GET['location']))
1 Like

Well I think you have way overcomplicated things, not to mention multiple queries within content (ahhh), POST checks also within output, PLUS below the form… outdated mysql… using GET for possible database query in includes/chapter.php… ahhh so many issues.

Do ONE query and grab all data above output.
Use session to track quiz id KEY.
Do POST processing above output.
Increment the session key and use this to display the next question and check answer.

PDO example.

<?php
//MySQL Database user name.    
$login = "BnBDemo";
//Password for MySQL.
$dbpass = "";
//MySQL Database name.
$dbname = ""; 
//Establish a connection
$pdo = new PDO("mysql:host=localhost;dbname=$dbname", "$login", "$dbpass");
//Add session_start to top of each page//
session_start();

// Do all php and query above output \\

if(!isset($_SESSION['chapter'])){$_SESSION['chapter'] = 0;}

$data = array(); 

$sql = "SELECT 
  chapter_id
, chapter_title
, chapter_content
, chapter_solution
, chapter_location 
FROM chapters 
ORDER BY chapter_id ASC";
$query = $pdo->prepare($sql);
$query->execute();     
while($row = $query->fetch(PDO::FETCH_ASSOC)){
    $data[] = $row;
}
/*
echo "<pre>";
print_r($data);
echo "</pre>";
*/ 

//Get last chapter key
end($data);         
$key = key($data);
reset($data);

//Validate user's solution guess against the answer stored in chapter_solution
if (isset($_POST['guess']) && $_SESSION['chapter'] <= $key):

    if(strtolower($_POST['guess']) !== strtolower($data[$_SESSION['chapter']]['chapter_solution'])){
        $error = 'You did not get it right. Try again.';
    }else{
        $_SESSION['chapter']++;
    }
    
endif;

//If we have reached the end display message    
if($_SESSION['chapter'] > $key):
    $data[$_SESSION['chapter']]['chapter_title'] = "Quiz is compete!";
    $data[$_SESSION['chapter']]['chapter_content'] = "Thank you.";
endif;

include 'includes/header.php';
if(isset($error)){echo $error;}
?>

<div id ="wrapper">
        <h1><?php echo $data[$_SESSION['chapter']]['chapter_title']; ?></h1> 
    <section>
        <p><?php echo $data[$_SESSION['chapter']]['chapter_content']; ?></p>
    </section>
</div>

<div id ="solve-form">
    <form action="chapter.php" method="post" autocomplete="off">
        <input type="text" name="guess" placeholder="Solution:" />
        <input type="submit" value="Solve" />
    </form>
</div>
<?php
include 'includes/footer.php';
?>
1 Like

That is just fantastic. I’ve only been doing php for a little bit, so I was trying to cobble together the material from the tutorials I’ve learned from. Your code works perfectly the first time through. I can’t tell you how much I appreciate your help. I hope to someday do the same for someone. Best regards!

Thank you amnion.
While on the topic of learning, let’s bump this example up a notch. Say you add another field to your chapters table called quiz_id and edit all the current questions you have so the quiz_id is 1. See where we are going? Now you could make another set of quizzes with a quiz_id of 2 etc.

Like we did for setting chapter to session, we can set the quiz_id to session. Now I’m setting it to 1 in this example but say you had a users table with a logged in user. You could query for the last quiz taken from their data and set session quiz_id to the next quiz, but that’s down the road.

if(!isset($_SESSION['quiz_id'])){$_SESSION['quiz_id'] = 1;}

Now, we’ll need some way for the user to move on to take the next quiz, so I will use a GET link.

<a href="chapter.php?next_quiz=yes">Yes</a>

I will set this to a variable where the last question has been answered and we will echo this variable to the page.

$next_link = 'Take the next quiz? <a href="chapter.php?next_quiz=yes">Yes</a> <a href="chapter.php?next_quiz=no">No</a>';

We will pick up this GET with isset and check for the value of “yes”. We’ll increment the session quiz_id and set our chapter session back to zero.

if(isset($_GET['next_quiz']) && $_GET['next_quiz'] == "yes"):
    $_SESSION['quiz_id']++;
    //reset chapter to zero
    $_SESSION['chapter'] = 0;
endif;

I want to note here that we are NOT using any user input directly in the query. they simply said yes, and we checked for “yes” and we apply the value WE wish to the query, (in this case $_SESSION['quiz_id']).

Now when making queries, we don’t want to directly place a variable, $_SESSION['quiz_id'] into the query. We want to bind the input

$query->bindParam(":quiz_id", $_SESSION['quiz_id']);

and use a placeholder

WHERE quiz_id = :quiz_id 

for the value in the query. So now our query looks like this.

$sql = "SELECT 
  chapter_id
, chapter_title
, chapter_content
, chapter_solution
, chapter_location 
FROM chapters
WHERE quiz_id = :quiz_id  
ORDER BY chapter_id ASC";
$query = $pdo->prepare($sql);
$query->bindParam(":quiz_id", $_SESSION['quiz_id']); 
$query->execute();

Now, because we are incrementing the quiz_id then making the query, there will come a point where there are no more quizzes to take resulting in no results from the query and therefor the array $data will be empty. As we are just echoing data keys in our html, e.g.

<?php echo $data[$_SESSION['chapter']]['chapter_title']; ?>

We will need to check for empty $data, set these keys to some value so you don’t get errors and while we’re at it, set a message for the user.

//Check if data is empty after the last quiz
    if(empty($data)):
        $data[$_SESSION['chapter']]['chapter_title'] = "Quiz is compete!";
        $data[$_SESSION['chapter']]['chapter_content'] = "There are no more quizzes to take. <br />Thank you.";    
    endif;

Anyway, this should give you something to think about. Happy coding!

<?php
//MySQL Database user name.    
$login = "";
//Password for MySQL.
$dbpass = "";
//MySQL Database name.
$dbname = ""; 
//Establish a connection
$pdo = new PDO("mysql:host=localhost;dbname=$dbname", "$login", "$dbpass");
//Add session_start to top of each page//
session_start();

// Do all php and query above output \\

if(!isset($_SESSION['chapter'])){$_SESSION['chapter'] = 0;}

if(!isset($_SESSION['quiz_id'])){$_SESSION['quiz_id'] = 1;}

if(isset($_GET['next_quiz']) && $_GET['next_quiz'] == "yes"):
    $_SESSION['quiz_id']++;
    //reset chapter to zero
    $_SESSION['chapter'] = 0;
endif;

$data = array(); 


$sql = "SELECT 
  chapter_id
, chapter_title
, chapter_content
, chapter_solution
, chapter_location 
FROM chapters
WHERE quiz_id = :quiz_id  
ORDER BY chapter_id ASC";
$query = $pdo->prepare($sql);
$query->bindParam(":quiz_id", $_SESSION['quiz_id']); 
$query->execute();     
while($row = $query->fetch(PDO::FETCH_ASSOC)){
    $data[] = $row;
}
/*
echo "<pre>";
print_r($data);
echo "</pre>";
*/ 

//Get last chapter key
end($data);         
$key = key($data);
reset($data);

//Validate user's solution guess against the answer stored in chapter_solution
if (isset($_POST['guess']) && $_SESSION['chapter'] <= $key):

    if(strtolower($_POST['guess']) !== strtolower($data[$_SESSION['chapter']]['chapter_solution'])){
        $error = 'You did not get it right. Try again.';
    }else{
        $_SESSION['chapter']++;
    }
    
endif;

//If we have reached the end display message    
    if($_SESSION['chapter'] > $key):
        $data[$_SESSION['chapter']]['chapter_title'] = "Quiz is compete!";
        $data[$_SESSION['chapter']]['chapter_content'] = "Thank you.";
        $next_link = 'Take the next quiz? <a href="chapter.php?next_quiz=yes">Yes</a> <a href="chapter.php?next_quiz=no">No</a>';
    endif;
    
//Check if data is empty after the last quiz
    if(empty($data)):
        $data[$_SESSION['chapter']]['chapter_title'] = "Quiz is compete!";
        $data[$_SESSION['chapter']]['chapter_content'] = "There are no more quizzes to take. <br />Thank you.";    
    endif;

include 'includes/header.php';
if(isset($error)){echo $error;}
if(isset($next_link)){echo $next_link;}
?>

<div id ="wrapper">
        <h1><?php echo $data[$_SESSION['chapter']]['chapter_title']; ?></h1> 
    <section>
        <p><?php echo $data[$_SESSION['chapter']]['chapter_content']; ?></p>
    </section>
</div>

<div id ="solve-form">
    <form action="chapter.php" method="post" autocomplete="off">
        <input type="text" name="guess" placeholder="Solution:" />
        <input type="submit" value="Solve" />
    </form>
</div>
<?php
include 'includes/footer.php';
?>
2 Likes

Again, thanks much for all your help. I’m going to have to take a look at your last post when I have more time. I do have one question, though, since I’m still trying to work out the mechanics of this code. When it sends the user to the next chapter, is it incrementing the id number by one or just picking the next entry in the database? I know I had my original code set to ++ the id number, but I didn’t take into account if I deleted any entries, the sql would auto increment over the skipped id number.

Ahhh see, this is why I’m building the data array with open keys. If we relied on id numbers, and a record is deleted then it wouldn’t be found. By using the open key during building the data array, the keys will always be incremental starting at 0, 1, 2 etc. This is why we set the session chapter to 0 when we start.
So in the DB you might have id 1, id 2, id 5 but the data keys will be 0, 1 and 2.

1 Like

Yeah, that makes way more sense. I’ve been dissecting the code for a while and I’m trying to add to it. I basically want to update the database each time someone fails and also each time they succeed, so each chapter can display the total amounts. I’ve made a new row for each called “chapter_fails” and “chapter_succeeds”. I have added the following code under the if statement when a person gets the wrong answer, and I’ve gone through enough to get no more errors, but the database does not update. The code seems to do nothing. I’ve added the two new rows to the initial query at the top as well. I figure I’d ask here instead of starting a new topic entirely since we are already familiar somewhat with the code.

//Validate user's solution guess against the answer stored in chapter_solution
if (isset($_POST['guess']) && $_SESSION['chapter'] <= $key):

    if(strtolower($_POST['guess']) !== strtolower($data[$_SESSION['chapter']]['chapter_solution'])){

        //add failed attempts to the database - new code starts here
            $fails = $data[$_SESSION['chapter']]['chapter_fails']++;
            $chapter = $data[$_SESSION['chapter']]['chapter_id'];
            
            //query
            $query = $pdo->prepare("UPDATE chapters SET chapter_fails = :fail WHERE chapter_id = :chapter");
            $query->bindParam(':fail', $fails, PDO::PARAM_INT);
            $query->bindParam(':chapter', $chapter, PDO::PARAM_INT);
            $query->execute();

Fixed it. I had to do a var_dump on my $fails and $chapter variables and came to find out that the system was treating them as strings instead of ints (how they are stored in the db). So to fix I had to change them to ints and then it worked.

if(strtolower($_POST['guess']) !== strtolower($data[$_SESSION['chapter']]['chapter_solution'])){
    //add failed attempts to the database
        $current_fails = $data[$_SESSION['chapter']]['chapter_fails'];
        $fails = (int)$current_fails;
        $fails++;
        $chapter = $data[$_SESSION['chapter']]['chapter_id'];
        
        //query
        $query = $pdo->prepare("UPDATE chapters SET chapter_fails = :fail WHERE chapter_id = :chapter");
        $query->bindParam(':fail', $fails, PDO::PARAM_INT);
        $query->bindParam(':chapter', $chapter, PDO::PARAM_STR);
        $query->execute();

Ah, very good that you fixed it. I was just reading the post.

I’ve been working with this code and at the current time I’m trying to get it so that logged in users go right to where they left off. I’m facilitating this by match the user level to the chapter location (differs from the chapter_id, which might not be incremental). So basically I have Chapter 1 location = 1, Chapter 2 = 2, etc. So I’m trying to set the user_level to equal the highest chapter location, and then in the beginning check the level against the current user and jumping ahead to that point.

I have gotten rid of all the parse errors and everything, but the code currently kicks me back to the beginning after the second level and it doesn’t update the user level in the database. I’m stuck at this point, but I have a feeling the issue has to do with the user session. Anyway, here is what I tried for this part:

<?php
session_start();
//Add session_start to top of each page//

include_once('includes/chapter.php');
include_once('includes/config.php');

// Do all php and query above output //


// If user is logged in, start chapter session where they left off
if( $user->is_logged_in() ){$_SESSION['chapter'] = $_SESSION['user_level'];}

$data1 = array(); 

$sql = "SELECT 
  user_id
, user_name
, user_level
FROM users 
WHERE user_id = {$_SESSION['user_id']}";
$query = $pdo->prepare($sql);
$query->execute();     
while($row = $query->fetch(PDO::FETCH_ASSOC)){
    $data1[] = $row;
}

// Reset Chapters session to 0
if(!isset($_SESSION['chapter'])){$_SESSION['chapter'] = 0;}


$data = array(); 

$sql = "SELECT 
  chapter_id
, chapter_title
, chapter_content
, chapter_solution
, chapter_location
, chapter_fails
, chapter_successes
FROM chapters 
ORDER BY chapter_id ASC";
$query = $pdo->prepare($sql);
$query->execute();     
while($row = $query->fetch(PDO::FETCH_ASSOC)){
    $data[] = $row;
}
/*
echo "<pre>";
print_r($data);
echo "</pre>";
*/ 

//Get last chapter key
end($data);         
$key = key($data);
reset($data);

//if(($data[$_SESSION['chapter']]['chapter_id'] > 3) && (!$user->is_logged_in() )):
//    header('Location=index.php');
//endif;

//Validate user's solution guess against the answer stored in chapter_solution
if (isset($_POST['guess']) && $_SESSION['chapter'] <= $key):

    if(strtolower($_POST['guess']) !== strtolower($data[$_SESSION['chapter']]['chapter_solution'])){
        //add failed attempts to the chapter in database
            $current_fails = $data[$_SESSION['chapter']]['chapter_fails'];
            $fails = (int)$current_fails;
            $fails++;
            $chapter = $data[$_SESSION['chapter']]['chapter_id'];
            
            //query
            $query = $pdo->prepare("UPDATE chapters SET chapter_fails = :fail WHERE chapter_id = :chapter");
            $query->bindParam(':fail', $fails, PDO::PARAM_INT);
            $query->bindParam(':chapter', $chapter, PDO::PARAM_STR);
            $query->execute();
        
        //$error = 'That is incorrect. You can do better than that.';
        $errorArray = array("That is incorrect. You can do better than that I hope.", "Nope. Certainly not that.", "Think hard. Don't hurt yourself, though.", "You'll get it eventually. Probably. Maybe.", "Do you need to phone a friend?", "Maybe go ask somebody for help.", "Rake is going to die if you don't get this right at some point.", "I think you're the only one who hasn't gotten this yet.", "Maybe you should play an easier game, Einstein.", "Some detective you are. Good thing this isn't a real murder.", "Do you have this much trouble using the ATM?", "This would be acceptable if English were your second language.", "Are you serious right now?", "You're a disgrace to third graders everywhere.", "It's a good thing a bunch of orphans aren't counting on you to solve this.");
        $error = array_rand($errorArray);
    } else {  
        
            //add success attempts to the database
            $current_success = $data[$_SESSION['chapter']]['chapter_successes'];
            $successes = (int)$current_success;
            $successes++;
            $chapter = $data[$_SESSION['chapter']]['chapter_id'];
        
            //query
            $query = $pdo->prepare("UPDATE chapters SET chapter_successes = :success WHERE chapter_id = :chapter");
            $query->bindParam(':success', $successes, PDO::PARAM_INT);
            $query->bindParam(':chapter', $chapter, PDO::PARAM_STR);
            $query->execute();
        
            // update logged-in user's level to match the location number of the next chapter, i.e Chapter 2 location == 2
            // this would update the user's level to Level 2, and allow the user to start where they left off on login
            $newLevel = $data[$_SESSION['chapter']]['chapter_location'];
            $updateLevel = (int)$newLevel;
            $updateLevel++;
        
            $query = $pdo->prepare("UPDATE users SET user_level = :level WHERE user_id = {$_SESSION['user_id']}");
            $query->bindParam(':level', $updateLevel, PDO::PARAM_INT);
            $query->execute();
            
            // take the session to the next chapter
            $_SESSION['chapter']++;
    }
    
endif;

//If we have reached the end display message    
if($_SESSION['chapter'] > $key):
    $data[$_SESSION['chapter']]['chapter_title'] = "You've beaten all the levels so far!";
    $data[$_SESSION['chapter']]['chapter_content'] = "Check back soon for more levels.";
endif;

include 'includes/header.php';
?>
<div id="wrapper">
<div id ="errors"><?php if(isset($error)){echo $errorArray[$error];} ?></div>

    <div class="stats-container">
        <div class="stats-bubble-fail">
            <div id="stats"><p><strong><?php echo $data[$_SESSION['chapter']]['chapter_fails']; ?></strong><br /><small>Fails</small></p>
            </div>
        </div>
        <div class="stats-bubble-pass">
            <div id="stats"><p><strong><?php echo $data[$_SESSION['chapter']]['chapter_successes']; ?></strong><br /><small>Passes</small></p>
            </div>
        </div><br />
        <div class="stats-bubble-rating">
            <div id="stats"><p><strong><?php echo round((int)$data[$_SESSION['chapter']]['chapter_fails'] / (int)$data[$_SESSION['chapter']]['chapter_successes']);?></strong><br /><small>Rating</small></p>
            </div>
        </div>
    </div>
    <div id="header-style"><?php echo $data[$_SESSION['chapter']]['chapter_title']; ?></div> 
    <section>
        <p><?php echo $data[$_SESSION['chapter']]['chapter_content']; ?></p>
    
        <div id ="solve-form">
            <div id = "form-header"><img src="img/ds-icon20px.png"> Enter your answer: </div><HR SIZE = "1" WIDTH="100%" NOSHADE>
            <form action="chapter.php" method="post" autocomplete="off">
            <input type="text" id="solution" name="guess" placeholder="Solution:" /><p>
                <input type="submit" value="Solve" /></p>
            </form>
        </div>
    </section>        
    </div>
<?php
include 'includes/footer.php';
?>

Update: I am aware now that this issue has to do with another problem I’m having with sessions. I also understand better what this code does than I did a few days ago, so I am confident that when I get this session issue resolved that I will be able to fix the issue listed in my post above.

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