PHP Tutorial: Free OOP Hangman Game (database required)

Leave a comment Standard

In this tutorial we’ll be modifying the original PHP hangman game so instead of reading from a text file it reads from the database. We’ll also be adding a new word difficulty option. This tutorial assumes you’ve already read the version that doesn’t require a database so I won’t cover the game mechanics, only the changes.

Getting Started

So first thing’s first, we have to create a table in our database to hold all of our hangman words. Our table will consist of two columns, a word column and a difficulty column.

CREATE TABLE IF NOT EXISTS `hangman_words` (
  `word` varchar(50) NOT NULL,
  `difficulty` enum('Easy','Medium','Hard') NOT NULL DEFAULT 'Easy',
  UNIQUE KEY `word` (`word`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

INSERT INTO `hangman_words` (`word`, `difficulty`) VALUES
('dog', 'Easy'),
('cat', 'Easy'),
('fish', 'Easy'),
('horse', 'Easy'),
('donkey', 'Easy'),
('cow', 'Easy'),
('mule', 'Easy'),
('lizard', 'Easy'),
('leather belt', 'Medium'),
('blue suit', 'Medium'),
('crochet shawl', 'Medium'),
('denim jeans', 'Medium'),
('black tuxedo', 'Medium'),
('seamstress', 'Medium'),
('sibilant', 'Hard'),
('obtest', 'Hard'),
('cicatrix', 'Hard'),
('pother', 'Hard'),
('sudorific', 'Hard'),
('mensch', 'Hard'),
('besot', 'Hard'),
('fulcrum', 'Hard');

Connecting To The Database

Now that we have our database table we need to write a script that connects us to our database. Anytime we want to access something from our database we need to include this connection string. So, let’s create a folder called inc, short for includes, and then store this as a PHP file inside of the folder. That way any time we need to connect to our database we can simply do include(‘inc/database.php’);

<?php
$host = "localhost";
$username = "your_username";
$password = "your_password";
$database = "your_database";

mysql_connect($host, $username, $password)
    or die ('cannot connect to the database because: ' . mysql_error());

mysql_select_db($database)
    or die ('cannot select database because: ' . mysql_error());
?>

Updating The Hangman Class

Now it’s time to update the game so it pulls a word from the database instead of pulling a word from our old wordlist.txt file. First we need to add a new difficulty variable to our class variables:

var $difficulty = 'Easy'; //lets set it to Easy by default

Next we update the loadWords function so it pulls from the database:

    /**
    * Purpose: load words from the the database at the given difficulty level
    * Preconditions: a difficulty level has been set already
    * Postconditions: the word list has been loaded
    **/
    function loadWords()
    {
        $loop = mysql_query("SELECT word FROM hangman_words WHERE difficulty='$this->difficulty' ORDER BY RAND() LIMIT 1")
            or die ('cannot load hangman words for this difficulty');

        while ($data = mysql_fetch_assoc($loop))
            array_push($this->wordList, trim($data['word']));
    }

Next we’re going to add a new function that allows the user to change the difficulty level and starts a new game.

    /**
    * Purpose: allow the player to change the game difficulty
    * Preconditions: the difficulty setting
    * Postconditions: game difficulty is changed and a new game is started
    **/
    function changeDifficulty($option)
    {        
        //change the difficulty level depending on what they selected
        switch ($option)
        {
            case 1: $this->difficulty = "Easy"; break;
            case 2: $this->difficulty = "Medium"; break;
            case 3: $this->difficulty = "Hard"; break;
            default:
                $this->difficulty = "Easy"; //always default to easy
        }

        //empty the current word list and repopulate it with new words
        $this->wordList = Array();
        $this->loadWords();

        //make sure we start a new game once they change the difficulty
        $this->newGame();
    }

Now we need to update the displayGame function to show a drop down option for changing the game difficulty. In addition we’re modifying the drop down box so the current difficulty will always be selected by default in the drop down box.

    /**
    * Purpose: display the game interface
    * Preconditions: none
    * Postconditions: start a game or keep playing the current game
    **/
    function displayGame()
    {
        //while the game isn't over
        if (!$this->isOver())
        {
            echo "<div id=\"picture\">" . $this->picture() . "</div>
                  <div id=\"guess_word\">" . $this->solvedWord() . "</div>
                  <div id=\"select_letter\">
                    Enter A Letter:
                        <input type=\"text\" name=\"letter\" value=\"\" size=\"2\" maxlength=\"1\" />
                        <input type=\"submit\" name=\"submit\" value=\"Guess\" />
                  </div>";

                  if (!empty($this->letters))
                    echo "<div id=\"guessed_letters\">Letters Guessed: " . implode($this->letters, ", ") . "</div>";

            //display the difficulty drop down box
            echo "<div id=\"change_difficulty\">
                    Difficulty:
                        <select name=\"difficulty\"/>
                            <option value=\"1\">Easy</option>
                            <option value=\"2\""; if ($this->difficulty == "Medium") echo " selected=\"selected\""; echo ">Medium</option>
                            <option value=\"3\""; if ($this->difficulty == "Hard") echo " selected=\"selected\""; echo ">Hard</option>
                        </select>
                        <input type=\"submit\" name=\"change\" value=\"Change\" />
                  </div>";
        }
        else
        {
            //they've won the game
            if ($this->won)
                echo successMsg("Congratulations! You've won the game.<br/>
                                Your final score was: $this->score");
            else if ($this->health < 0)
            {
                echo errorMsg("Game Over! Good try.<br/>
                                Your final score was: $this->score");

                echo "<div id=\"picture\">" . $this->picture() . "</div>";
            }

            echo "<div id=\"start_game\"><input type=\"submit\" name=\"newgame\" value=\"New Game\" /></div>";
        }
    }

Now that we have different difficulty levels I think it’s only fitting that we award more points for harder words. Let’s change our guessLetter() function so it looks like this:

    /**
    * Purpose: guess a letter in this word
    * Preconditions: a game has started
    * Postconditions: the game data is updated
    **/
    function guessLetter($letter)
    {            

        if ($this->isOver())
            return;

        if (!is_string($letter) || strlen($letter) != 1 || !$this->isLetter($letter))
            return errorMsg("Oops! Please enter a letter.");

        //check if they've already guessed the letter
        if (in_array($letter, $this->letters))
            return errorMsg("Oops! You've already guessed this letter.");

        //only allow lowercase letters
        $letter = strtolower($letter);

        //if the word contains this letter
        if (!(strpos($this->wordList[$this->wordIndex], $letter) === false))
        {

            //factor in our new difficulty levels
            switch ($this->difficulty)
            {
                case "Easy": $multiplier = 1; break;
                case "Medium": $multiplier = 2; break;
                case "Hard": $multiplier = 3; break;
                default:
                    $multiplier = 1; break;
            }

            //increase their score based on how many guesses they've used so far
            if ($this->health > (100/ceil($this->guesses/5)))
                $this->setScore((5 * $multiplier));
            else if ($this->health > (100/ceil($this->guesses/4)))
                $this->setScore((4 * $multiplier));
            else if ($this->health > (100/ceil($this->guesses/3)))
                $this->setScore((3 * $multiplier));
            else if ($this->health > (100/ceil($this->guesses/2)))
                $this->setScore((2 * $multiplier));
            else
                $this->setScore((1 * $multiplier));

            //add the letter to the letters array
            array_push($this->letters, $letter);

            //if they've found all the letters in this word
            if (implode(array_intersect($this->wordLetters, $this->letters), "") == 
                str_replace($this->punctuation, "", strtolower($this->wordList[$this->wordIndex])))
                $this->won = true;
            else
                return successMsg("Good guess, that's correct!");
        }
        else //word doesn't contain the letter
        {
            //reduce their health
            $this->setHealth(ceil(100/$this->guesses) * -1);

            //add the letter to the letters array
            array_push($this->letters, $letter);

            if ($this->isOver())
                return;
            else
                return errorMsg("There are no letter $letter's in this word.");
        }
    }

The last modification we need to make to the game class is to update the playGame function so it correctly responds to the player changing the difficulty.

    /**
    * Purpose: set or retrieve maximum guesses before game over, or change the game difficulty
    * Preconditions: posted form data
    * Postconditions: game responds to the selections
    **/
    function playGame($_POST)
    {
        //player is changing the game difficulty
        if ($_POST['change']) 
            $this->changeDifficulty($_POST['difficulty']);

        //player pressed the button to start a new game
        if ($_POST['newgame'] || empty($this->wordList))
            $this->newGame();

        //player is trying to guess a letter
        if (!$this->isOver() && $_POST['letter'])
            echo $this->guessLetter($_POST['letter']);

        //display the game
        $this->displayGame();
    }

Updating The Index File

We’re almost done with the new game functionality! Now we need to update our index file so it connects to the database. When our hangman class is running it will need access to the database to load our word list so we’ll make sure to include the database connection file before we start the game object. Open index.php and add our database connection file right after the class includes.

<?php
/***
* File: index.php
* Author: design1online.com, LLC
* Created: 6.06.2011
* License: Public GNU
***/

//include the required files
require_once('oop/class.game.php');
require_once('oop/class.hangman.php');

//connect to the database
require_once('inc/database.php');

Working Version and Source Code

Not as hard as you thought it would be huh? In this tutorial we built on the code we had from our original OOP version of this game to add a difficulty level and updated it to use words from a database instead of from a text file.

Try the working version or Download the source code for this tutorial

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s