Angular-UI Tutorial: Unique Filter on Object Arrays

Comments 2 Link

This plunker will show you how to use the unique filter included in the Angular UI utilities to strip out duplicates in an object array. Enjoy!

Angular Js

Leave a comment Standard
AngularJS

I wrote a few plunkers this past month that I thought I would share for anyone whose interested in dabbling in Angular JS. Enjoy!

FREE PHP Tool: Class File Generator

Comment 1 Standard

Want to quickly generate a class file for a table in your database? This script takes your connection information, the name of the table, the name of your class and gives you a formatted class file you can download and use in a snap. Features include the ability to add get/set methods for each field, a simple debugger, and a print object function. It’s available under the GNU public license.

Download the Class File Generator

The Source Code

<?php
/*********
* File: index.php
* Date: 4.5.2012
* Author: design1online.com, LLC
* Purpose: generate a oop class file for a mysql database table
*********/
DEFINE("AUTHOR", "Design1online.com, LLC");

if ($_POST['generate'])
{
    $fields = array();
    $keys = array();

    if (!$_POST['host'])
        $_POST['host'] = "localhost";

    //connect to the database
    mysql_connect ($_POST['host'], $_POST['username'], $_POST['pass'])
        or die ('Cannot connect to the database: ' . mysql_error());

    mysql_select_db ($_POST['db'])
        or die ('Cannot select database: ' . mysql_error()); 

    $table = mysql_query("SHOW columns FROM {$_POST['table']}")
        or die ('Cannot load table: ' . mysql_error());

    //load the table fields and keys
    while ($schema = mysql_fetch_assoc($table))
    {

        $field = array(
            "name" => $schema['Field'],
            "type" => $schema['Type']
        );

        if ($schema['Key'])
            array_push($keys, $field);

        array_push($fields, $field);
    }

    $filename = ($_POST['name']) ? $_POST['name'] : $_POST['table'];

    //build the file
    $file = "/**********
* File: $filename.class.php
* Author: " . AUTHOR . "
* Created: " . date('n.j.Y g:ia') . "
* Free PHP Class Generator: http://design1online.com/phpclassfilegenerator
**********/

class $filename";

    if ($_POST['extends'])
        $file .= " extends {$_POST['extends']}";

    $file .= " {\n\n";

    if ($_POST['vars'])
    {
        foreach ($fields as $index => $field)
            $file .= "\tvar $" . $field['name'] . ";\n";
    }

    if ($_POST['debug'])
        $file .= "\tvar \$debugger = false;\n";

    $file .= "\n\t/*********
\t* Purpose: Default constructor
\t* Preconditions: None
\t* Postconditions: None
\t*********/
\tfunction $filename(";

    foreach ($keys as $index => $field)
        $keynames[] = $field['name'];

    //build the constructor parameters
    if ($_POST['constructor'])
        $file .= "$" . implode(", $", $keynames);

    $file .= ") {\n";

    if ($_POST['debug'])
        $file .= "\n\t\tif (\$this->debugger)\n\t\t\techo \"DEBUGGER: initializing $filename object\";\n";

    //build the constructor
    if ($_POST['constructor'])
    {
        foreach ($keys as $index => $field)
        {
            $file .= "\n\t\tif (!$" . $field['name'];

            if (strpos($field['type'], "int") !== false || strpos($field['type'], "decimal") !== false
                || strpos($field['type'], "float") !== false || strpos($field['type'], "double") !== false
                || strpos($field['type'], "real") !== false || strpos($field['type'], "bit") !== false
                || strpos($field['type'], "bit") !== false || strpos($field['type'], "bool") !== false
                || strpos($field['type'], "serial") !== false)
                    $file .= " || !is_numeric($" . $field['name'] . ")";

            $file .= ")\n\t\t\treturn;\n";
        }

        $file .= "\n\t\t\$result = mysql_query(\"SELECT * FROM {$_POST['table']} WHERE ";

        $append = false;

        foreach ($keys as $index => $field)
        {    
            if ($append)
                $file .= " AND ";

            $file .= "{$field['name']} = '$" . $field['name'] . "'";

            if (!$append)
                $append = true;
        }

        $file .= "\")\n\t\t\tor die('Cannot init constructor in $filename.class.php: ' . mysql_error());\n\n\t\t\$row = mysql_fetch_assoc(\$result);\n";

        foreach ($fields as $index => $value)
        {
            $file .= "\n\t\t\$this->{$value['name']} = ";

            if (strpos($value['type'], "varchar") !== false || strpos($value['type'], "text") !== false
                || strpos($value['type'], "blob") !== false || strpos($value['type'], "enum") !== false
                || strpos($value['type'], "set") !== false || strpos($value['type'], "binary") !== false)
                    $file .= "stripslashes(html_entity_decode(";

            $file .= "\$row['{$value['name']}']";

            if (strpos($value['type'], "varchar") !== false || strpos($value['type'], "text") !== false
                || strpos($value['type'], "blob") !== false || strpos($value['type'], "enum") !== false
                || strpos($value['type'], "set") !== false || strpos($value['type'], "binary") !== false)
                    $file .= ", ENT_QUOTES))";

            $file .= ";";
        }
    }

    $file .= "\n\n\t} //end default constructor";

    //check to see if they want the print function
    if ($_POST['print'])
    {
        $file .= "\n\n\t/*********
\t* Purpose: Display the object
\t* Preconditions: None
\t* Postconditions: None
\t*********/
\tfunction print() {";

        if ($_POST['debug'])
            $file .= "\n\n\t\tif (\$this->debugger)\n\t\t\techo \"DEBUGGER: printing $filename object\";";

        $file .= "\n\n\t\techo \"<b>Displaying $filename Object</b><br/><br/>";

        foreach ($fields as $index => $value)
            $file .= "\n\t\t\t{$value['name']} = \$this->{$value['name']}<br/>";

        $file .= "\";\n\n\t} //end print function";
    }

    //check to see if they want the get methods
    if ($_POST['get'])
    {
        foreach ($fields as $index => $value)
        {
            $file .= "\n\n\t/*********
\t* Purpose: Get the value from the {$value['name']} field
\t* Preconditions: None
\t* Postconditions: {$value['name']} field value returned
\t*********/
\tfunction get" . ucFirst($value['name']) . "() {";

            if ($_POST['debug'])
                $file .= "\n\n\t\tif (\$this->debugger)\n\t\t\techo \"DEBUGGER: calling {$filename}->get" . ucFirst($value['name']) . " method\";\n";

            $file .= "\n\t\treturn \$this->{$value['name']};\n\t}";

        }
    }

    //check to see if they want the set methods
    if ($_POST['set'])
    {
        foreach ($fields as $index => $value)
        {
            $file .= "\n\n\t/*********
\t* Purpose: Set the value of the {$value['name']} field
\t* Preconditions: None
\t* Postconditions: object and table data set to \$value
\t*********/
\tfunction set" . ucFirst($value['name']) . "(\$value) {";

            if ($_POST['debug'])
                $file .= "\n\n\t\tif (\$this->debugger)\n\t\t\techo \"DEBUGGER: calling {$filename}->get" . ucFirst($value['name']) . " method\";";

            $file .= "\n\n\t\tif (!\$value)\n\t\t\treturn \"Invalid value\";

\t\tmysql_query(\"UPDATE {$_POST['table']} SET {$value['name']}='\$value' WHERE ";

            $append = false;

            foreach ($keys as $index => $field)
            {    
                if ($append)
                    $file .= " AND ";

                $file .= "{$field['name']} = '\$this->" . $field['name'] . "'";

                if (!$append)
                    $append = true;
            }

            $file .= "\")\n\t\t\tor die('Cannot set value for {$value['name']}: ' . mysql_error());\n\n\t\t\$this->{$value['name']} = \$value;\n\t}";

        }
    }

    $file .= "\n\n} //end $filename.class.php";

    //setup the headers so you can download the file
    header('Content-type: text');
    header("Content-Disposition: attachment; filename=$filename.class.php");
    echo $file;
}

if (!$_POST['generate'])
{
    echo "
    <center>
    <h1>Class File Generator</h1>
    <form action=\"#\" method=\"post\">
        <table cellpadding=\"2\" cellspacing=\"2\" width=\"80%\">
            <tr>
                <th>MySQL Connection</th>
                <th>Class Options</th>
            </tr>
            <tr>
                <td width=\"50%\" valign=\"top\">
                    <p>Host: <input type=\"text\" name=\"host\" value=\"{$_POST['host']}\" /></p>
                    <p>Username: <input type=\"text\" name=\"username\" value=\"{$_POST['username']}\" /></p>
                    <p>Password: <input type=\"password\" name=\"pass\" value=\"{$_POST['pass']}\" /></p>
                    <p>Database: <input type=\"text\" name=\"db\" value=\"{$_POST['db']}\" /></p>
                    <p>Table: <input type=\"text\" name=\"table\" value=\"{$_POST['table']}\" /></p>
                    <p align=\"center\">
                        <input type=\"submit\" name=\"generate\" value=\"Generate Class\" />
                    </p>
                </td>
                <td>
                    <p>
                        Class Name: <input type=\"text\" name=\"name\" value=\"{$_POST['name']}\" />
                        <br/><i>tablename used by default</i>
                    </p>
                    <p>
                        Extends Class: <input type=\"text\" name=\"extends\" value=\"{$_POST['extends']}\" />
                        <i>(optional)</i>
                    </p>
                    <blockquote>
                        <p><input type=\"checkbox\" name=\"vars\" checked=\"checked\" />Include variable declarations</p>
                        <p><input type=\"checkbox\" name=\"constructor\" checked=\"checked\" />Include default constructor</p>
                        <p><input type=\"checkbox\" name=\"get\" />Include get functions for all fields</p>
                        <p><input type=\"checkbox\" name=\"set\" />Include set functions for all fields</p>
                        <p><input type=\"checkbox\" name=\"print\" />Include print function</p>
                        <p><input type=\"checkbox\" name=\"debug\" />Include debugger</p>
                    </blockquote>
                </td>
            </tr>
        </table>
        <p>
            <a href=\"http://design1online.com/downloads/classfilegenerator.zip\">Download Source Code</a><br/>
                <b>GNU Public License</b>
        </p>
        <p>
            <a href=\"http://design1online.com\">by design1online.com, LLC</a>
        </p>
    </form>
    </center>";

PHP Tutorial: 2 Player Tic-Tac-Toe Game (no database required)

Comments 9 Standard

In the first part of my games tutorial series we created a PHP Hangman game and then in the second installment we converted Hangman to use AJAX. Now it’s time to follow the same approach, only with Tic Tac Toe.

The Beauty of Classes

One of the things I like about classes is how easy it is to use and reuse them without writing a lot of code to go with it. So to start our Tic Tac Toe game we’re going to copy all the files from our original PHP Hangman game. We won’t need to change anything in the game class but we will need to make some modifications to the other class. First we’ll remove all references to letters, then we’ll remove any functions we don’t need anymore. Finally we change the main functions to display a Tic Tac Toe board instead of our hangman pictures, and finish it off by changing the logic behind how you win a game and how you make a move in the game.

Our new Tic Tac Toe game will need a player (or turn) variable so we know whose turn it is to place an X or an O. Then we’re going to add another variable to keep track of how many guesses have been made — that makes it easier to figure out if there’s a tie game instead of looping through the whole array each time someone guesses. We’ll also need a new array called Board to store all of the X and O’s placed on our game board.

The Index File

We’re going to keep things as short and easy as possible. The only changes we have to make to our index file is to load a tic tac toe game instead of a hangman game. Then we change the page title and we’re nearly done!

<?php
/***
* File: index.php
* Author: design1online.com, LLC
* Created: 1.31.2012
* License: Public GNU
* Description: PHP/MySQL Version of 2 Player Tic Tac Toe
***/
require_once('oop/class.game.php');
require_once('oop/class.tictactoe.php');

//this will store their information as they refresh the page
session_start();

//if they haven't started a game yet let's load one
if (!$_SESSION['game']['tictactoe'])
    $_SESSION['game']['tictactoe'] = new tictactoe();

?>
<html>
    <head>
        <title>Tic Tac Toe</title>
        <link rel="stylesheet" type="text/css" href="inc/style.css" />
    </head>
    <body>
        <div id="content">
        <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="POST">
        <h2>Let's Play Tic Tac Toe!</h2>
        <?php
            $_SESSION['game']['tictactoe']->playGame($_POST);
        ?>
        </form>
        </div>
    </body>
</html>

The Updated Class File

Here’s the class file with our updated game logic. Can you see how similar it is to our hangman class file? What’s changed and what looks the same?

<?php
/***
* File: oop/class.tictactoe.php
* Author: design1online.com, LLC
* Created: 1.31.2012
* License: Public GNU
* Description: tic tac toe game
***/

class tictactoe extends game
{
    var $player = "X";            //whose turn is
    var $board = array();        //the tic tac toe board
    var $totalMoves = 0;        //how many moves have been made so far        

    /**
    * Purpose: default constructor
    * Preconditions: none
    * Postconditions: parent object started
    **/
    function tictactoe()
    {
        /**
        * instantiate the parent game class so this class
        * inherits all of the game class's attributes 
        * and methods
        **/
        game::start();
    }

    /**
    * Purpose: start a new tic tac toe game
    * Preconditions: maximum number of guesses
    * Postconditions: game is ready to be displayed
    **/
    function newGame()
    {
        //setup the game
        $this->start();

        //reset the player
        $this->player = "X";
        $this->totalMoves = 0;

        //clear out the board
        $this->board = array();
    }

    /**
    * Purpose: run the game until it's tied or someone has won
    * Preconditions: all $_POST content
    * Postconditions: game is in play
    **/
    function playGame($postdata)
    {
        if (!$this->isOver() && $postdata['move'])
            $this->move($postdata);

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

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

    /**
    * 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=\"board\">";

            for ($x = 0; $x < 3; $x++)
            {
                for ($y = 0; $y < 3; $y++)
                {
                    echo "<div class=\"board_cell\">";

                    //check to see if that position is already filled
                    if ($this->board[$x][$y])
                        echo "<img src=\"images/{$this->board[$x][$y]}.jpg\" alt=\"{$this->board[$x][$y]}\" title=\"{$this->board[$x][$y]}\" />";
                    else
                    {
                        //let them choose to put an x or o there
                        echo "<select name=\"{$x}_{$y}\">
                                <option value=\"\"></option>
                                <option value=\"{$this->player}\">{$this->player}</option>
                            </select>";
                    }

                    echo "</div>";
                }

                echo "<div class=\"break\"></div>";
            }

            echo "
                <p align=\"center\">
                    <input type=\"submit\" name=\"move\" value=\"Take Turn\" /><br/>
                    <b>It's player {$this->player}'s turn.</b></p>
            </div>";
        }
        else
        {

            //someone won the game or there was a tie
            if ($this->isOver() != "Tie")
                echo successMsg("Congratulations player " . $this->isOver() . ", you've won the game!");
            else if ($this->isOver() == "Tie")
                echo errorMsg("Whoops! Looks like you've had a tie game. Want to try again?");

            session_destroy(); 

            echo "<p align=\"center\"><input type=\"submit\" name=\"newgame\" value=\"New Game\" /></p>";
        }
    }

    /**
    * Purpose: trying to place an X or O on the board
    * Preconditions: the position they want to make their move
    * Postconditions: the game data is updated
    **/
    function move($postdata)
    {            

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

        //remove duplicate entries on the board    
        $postdata = array_unique($postdata);

        foreach ($postdata as $key => $value)
        {
            if ($value == $this->player)
            {    
                //update the board in that position with the player's X or O 
                $coords = explode("_", $key);
                $this->board[$coords[0]][$coords[1]] = $this->player;

                //change the turn to the next player
                if ($this->player == "X")
                    $this->player = "O";
                else
                    $this->player = "X";

                $this->totalMoves++;
            }
        }

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

    /**
    * Purpose: check for a winner
    * Preconditions: none
    * Postconditions: return the winner if found
    **/
    function isOver()
    {

        //top row
        if ($this->board[0][0] && $this->board[0][0] == $this->board[0][1] && $this->board[0][1] == $this->board[0][2])
            return $this->board[0][0];

        //middle row
        if ($this->board[1][0] && $this->board[1][0] == $this->board[1][1] && $this->board[1][1] == $this->board[1][2])
            return $this->board[1][0];

        //bottom row
        if ($this->board[2][0] && $this->board[2][0] == $this->board[2][1] && $this->board[2][1] == $this->board[2][2])
            return $this->board[2][0];

        //first column
        if ($this->board[0][0] && $this->board[0][0] == $this->board[1][0] && $this->board[1][0] == $this->board[2][0])
            return $this->board[0][0];

        //second column
        if ($this->board[0][1] && $this->board[0][1] == $this->board[1][1] && $this->board[1][1] == $this->board[2][1])
            return $this->board[0][1];

        //third column
        if ($this->board[0][2] && $this->board[0][2] == $this->board[1][2] && $this->board[1][2] == $this->board[2][2])
            return $this->board[0][2];

        //diagonal 1
        if ($this->board[0][0] && $this->board[0][0] == $this->board[1][1] && $this->board[1][1] == $this->board[2][2])
            return $this->board[0][0];

        //diagonal 2
        if ($this->board[0][2] && $this->board[0][2] == $this->board[1][1] && $this->board[1][1] == $this->board[2][0])
            return $this->board[0][2];

        if ($this->totalMoves >= 9)
            return "Tie";
    }
}

Try the working example or Download the source code.

Learn how to create an AJAX version of this game.

ML Text Based Game: Fantasy Quest

Comments 2 Standard

Haha I just found this old game I wrote in ML for a programming languages class in college. It takes about 20 minutes to play. Maybe it’ll be useful to someone out there?

Code

/*
  First, text descriptions of all the places in
  the game.
*/
description(valley,
  'You find yourself in a pleasant valley, with a trail ahead.').
description(path,
  'You are on a path, with ravines on both sides.').
description(cliff,
  'You are teetering on the edge of a cliff.').
description(fork,
  'There is a fork in the path.').
description(maze(_),
  'You are in a maze of twisty trails, all alike.').
description(gate,
  'You see a large locked gate before you.').
description(dungeon(0),
  'You wake up with an aching head. Finally you manage to look around. You are in a small dungeon 

room. The walls are slimy and damp.').
description(dungeon(1),
  'The wall appears slimy.').
description(dungeon(2),
  'You see a small window above you, but there is little light coming through.').
description(dungeon(3),
  'The sound of water can be heard.').
description(dungeon(4),
  'The wall is slimy to the touch.').
description(dungeondoor, 'A guard walks by and grins at you with broken teeth.').
description(alley1, 'You find yourself in a rotting, fetid alley.').
description(alley2, 'The smell grows fainter. You see shorter buildings and people ahead').
description(town(0), 'You find yourself in town outside of the alley.').
description(town(1), 'You find yourself on a fairly busy street.').
description(town(2), 'A colorful marketplace looms ahead.').
description(town(3), 'You wander past the marketplace and towards the service district').
description(town(4), 'A weaver tries to get you to buy and other hawkers preach about their wares').
description(town(5), 'The streets grow narrow.').
description(town(6), 'A blacksmith sits before his forge. He asks you if you\'ve seen his missing 

horseshoe and offers a reward if you find it.').
description(town(7), 'You can see the alley in the distance ahead of you.').
description(town(8), 'The marketplace is full of many exotic fruits and vegtables').
description(town(9), 'Past the market place houses line the streets.').
description(town(10), 'Houses grow sparce and delaphidated out here.').
description(town(11), 'Small boys play in sewage.').
description(town(12), 'A drunken man staggers towards you. You see the tavern ahead.').
description(town(13), 'You wander out of town and into the countryside.').
description(tavern, 'You enter the tavern you were in last night. The barkeep look as you oddly.').

/*
  report prints the description of your current
  location.
*/
report :-
  at(you,X),
  description(X,Y),
  write(Y), nl.

/*
  These connect predicates establish the map.
  The meaning of connect(X,Dir,Y) is that if you
  are at X and you move in direction Dir, you
  get to Y.  Recognized directions are
  forward, right, and left.
*/
connect(valley,forward,path). %outside of the town
connect(path,right,cliff).
connect(path,left,cliff).
connect(path,forward,fork).
connect(fork,left,maze(0)).
connect(fork,right,gate).
connect(gate,forward,mountaintop).
connect(gate,left,valley).
connect(gate,right,cliff).
connect(maze(0),left,maze(1)). %the maze
connect(maze(1),right,maze(2)).
connect(maze(2),left,fork).
connect(maze(0),right,maze(3)).
connect(maze(_),_,maze(0)).
connect(dungeon(0),forward,dungeon(1)). %the dungeon
connect(dungeon(0),left,dungeon(1)).
connect(dungeon(1),forward,dungeon(2)).
connect(dungeon(1),left,dungeon(2)).
connect(dungeon(2),left,dungeon(3)).
connect(dungeon(3),right,dungeon(2)).
connect(dungeon(3),forward,dungeon(4)).
connect(dungeon(4),forward,dungeondoor).
connect(dungeon(4),right,dungeon(1)).
connect(dungeon(4),left,dungeon(3)).
connect(dungeondoor,forward,alley(1)).
connect(dungeondoor,right,dungeon(4)).
connect(dungeondoor,left,dungeon(4)).
connect(alley(1),forward,alley(2)). %the alley outside the dungeon
connect(alley(2),forward,town(0)).
connect(town(0),right,town(1)). %the town
connect(town(0),forward,town(7)).
connect(town(1),right,town(2)).
connect(town(2),right,town(8)).
connect(town(2),right,town(3)).
connect(town(8),forward,town(9)).
connect(town(8),right,town(2)).
connect(town(8),left,town(2)).
connect(town(9),right,town(12)).
connect(town(10),left,town(10)).
connect(town(12),forward,tavern).
connect(town(12),right,town(9)).
connect(town(12),left,town(9)).
connect(town(10),forward,town(11)).
connect(town(10),right,town(9)).
connect(town(10),left,town(9)).
connect(town(11),right,town(13)).
connect(town(11),forward,town(13)).
connect(town(11),left,town(10)).
connect(town(13),forward,valley).
connect(town(13),right,town(11)).
connect(town(13),left,town(11)).
connect(town(3),forward,town(4)).
connect(town(4),left,town(5)).
connect(town(5),forward,town(6)).
connect(town(6),right,town(7)).
connect(town(7),forward,town(0)).
connect(tavern,forward,town(12)). %tavern always takes you back to
connect(tavern,right,town(12)).   %town no matter where you turn
connect(tavern,left,town(12)).

/*
  Don't move past the gate if its locked
*/
move(Dir) :-
  at(locked, Loc),
  at(you,Loc),
  at(locked, Loc),
  report.

/*
  move(Dir) moves you in direction Dir, then
  prints the description of your new location.
*/
move(Dir) :-
  at(you,Loc),
  connect(Loc,Dir,Next),
  retract(at(you,Loc)),
  assert(at(you,Next)),
  report.

/*
  But if the argument was not a legal direction,
  print an error message and don't move.
*/
move(_) :-
  write('You can\'t go that way.\n'),
  report.

/*
  Shorthand for moves.
*/
forward :- move(forward).
left :- move(left).
right :- move(right).

/*
  Displays a message if there is an item at this location
*/
item :-
  isat(Item, Loc),
  at(you, Loc),
  write('there is '),
  write(Item),
  write(' on the ground!\n'). 

/*
  But if there is no item in the same place,
  nothing happens.
*/
item.

/*
  Picks up the item at that location
*/
pickup :-
  at(you, Loc), /* you are at this location */
  isat(Item, Loc), /* the item is at this location */
  assert(has(you, Item)), /* you pick up the item */
  retractall(isat(Item, Loc)), /* remove this item from that location */
  write('You picked up the '),
  write(Item),
  write('! \n').

/*
  Or there is nothing there
*/
pickup :-
  write('There is nothing here!\n').

/*
  Drop the item you're carrying
*/
drop :-
  at(you, Loc),
  has(you, Item),
  assert(isat(Item, Loc)),
  retract(has(you, Item)),
  write('You dropped the '),
  write(Item),
  write('.\n').

/*
  Or they don't have an item to drop
*/
drop :-
  write('You are not carrying any items!\n').

/*
  You have a sword and the ogre attacks, you kill it!
*/
ogre :-
  at(ogre,Loc),
  at(you,Loc),
  has(you, sword),
  write('An ogre attacks you! You slice off his head just before he can suck your brains out!\n').

/*
  If you and the ogre are at the same place, and
  you don't have a sword then it
  kills you.
*/
ogre :-
  at(ogre,Loc),
  at(you,Loc),
  write('An ogre attacks you and sucks your brain out through '),
  write('your eye sockets. Your body lies broken on the ground.\n'),
  retract(at(you,Loc)),
  assert(at(you,done)).

/*
  But if you and the ogre are not in the same place,
  nothing happens.
*/
ogre.

/*
  They have a coin and are at the dungeon door
*/
guard :-
  at(you, dungeondoor),
  has(you, coin),
  write('"You there!" you yell at the guard.\n'),
  write('The guard\'s smile fades. "What do you want?" comes his grim reply. \n'),
  write('"I think I\'ve found something!" you say, flashing the coin. "Open the door."\n'),
  write('The guard\'s brows furrowed through the slats in the door window. Finally he grumbled '),
  write('and keys jingled as he toyed with his belt. The door opened with a creak and he pushed'),
  write('his bulk through.\n With one hand you '),
  write('punch the guard in the face, knocking him out and escaping the dungeon. \n'),
  move(forward).

/*
  You approach the guard and don't have a coin
*/
guard :-
  at(you, dungeondoor),
  write('"You\'re going to hang tomorrow pig." The guard says, his spitle hitting you in the eye. 

Slowly he moves off. You wonder if he\'s a bit daft and move off.\n\n'),
  move(left).

/*
  Not at the guard, nothing happens.
*/
guard.

/*
  If you have a horseshoe the blacksmith gives you a sword!
*/
blacksmith :-
  at(you, town(6)),
  has(you, horseshoe),
  write('"You found it!" The black smith exclaims when you show him his lost horseshoe. "Here is 

your reward." He hands you a fine crafted sword.\n'),
  retract(has(you, horseshoe)), % he takes the horseshoe back
  assert(has(you, sword)). %you take your reward
/*
  You don't have the horseshoe, the blacksmith says nothing.
*/
blacksmith.

/*
  You have the coin still, and you walk into the tavern and buy a drink
*/
tavern :-
  at(you, tavern),
  has(you, coin),
  write('"Give me a drink." You tell the bartender, ignoring his weary looks, and handing over the 

coin you found in the dungeon.\n'),
  retract(has(you, coin)). % you give him the coin

/*
  You don't have the coin so you leave without a drink.
*/
tavern.

/*
  They walk through the gate with the key
*/

treasure :-
  at(treasure,Loc),
  at(you,Loc),
  has(you, key),
  write('The key you carry is hit by lightening and you die.\n'),
  write('You were so close!\n'),
  retract(at(you,Loc)),
  assert(at(you,done)).
/*
  If you and the treasure are at the same place and they
  don't have a key then they win.
*/
treasure :-
  at(treasure,Loc),
  at(you,Loc),
  write('You find a beaten old chest. Curious you wander over to open it...'),
  write('Colin\'s decapitated head rolls out at you, his eyes empty and staring.\n'),
  retract(at(you,Loc)),
  assert(at(you,done)).
/*
  But if you and the treasure are not in the same
  place, nothing happens.
*/
treasure.

/*
  If you are at the cliff, you fall off and die.
*/
cliff :-
  at(you,cliff),
  write('You fall off and die.\n'),
  retract(at(you,cliff)),
  assert(at(you,done)).
/*
  But if you are not at the cliff nothing happens.
*/
cliff.

/*
  Have the key with you to open the gate
*/
locked :-
  at(locked,Loc),
  at(you,Loc),
  has(you, key),
  retract(at(locked, Loc)), /* unlock the gate */
  write('You unlocked the gate with your key.\n').
/*
 At the gate but don't have a key
*/
locked :-
  at(locked,Loc),
  at(you,Loc),
  write('The gate is locked!\n').
/*
  Not at the gate, nothing happens
*/
locked.

/*
  Main loop.  Stop if player won or lost.
*/
main :-
  at(you,done),
  write('You wake up screaming in bed! Good thing it was all just a dream!!\n').
/*
  Main loop.  Not done, so get a move from the user
  and make it.  Then run all our special behaviors.
  Then repeat.
*/
main :-
  write('\nNext move -- '),
  read(Move),
  call(Move),
  guard,
  blacksmith,
  tavern,
  item,
  ogre,
  treasure,
  cliff,
  locked,
  main.

/*
  This is the starting point for the game.  We
  assert the initial conditions, print an initial
  report, then start the main loop.
*/
go :-
  retractall(at(_,_)), % clean up from previous runs
  retractall(has(_,_)), % remove all items you had before
  retractall(isat(_,_)), % remove empty locations of old items
  assert(at(you,dungeon(0))),
  assert(at(ogre,maze(3))),
  assert(at(treasure,mountaintop)),
  assert(at(locked, gate)),
  assert(at(lockeddoor, dungeondoor)),
  assert(isat(key, path)),
  assert(isat(coin, dungeon(3))),
  assert(isat(horseshoe, town(1))),
  write('\n\n Your name is James Pattern. Last night your friend Colin asked you to the Inn for a 

drink. He talked excitedly of treasure on a mountain top and ogres out to get him. You laugh at him 

good naturedly and think he had a bit too much to drink.\n On your way home later that night you 

hear the scuffling of feet. You spin but see nothing in the dark alley behind you. You continue on 

until something sharp hits you over the head.... You wake up with an aching head and a groan. 

Finally you manage to look around.\n\n'),
  write('WELCOME TO FANTASY QUEST!!!'),
  write('\n\n'),
  write('Legal moves are left, right, forward, pickup, and drop.\n'),
  write('End each move with a period.\n\n'),
  report,
  main.

Explanation

The main loop of this program is located at the very bottom of the script. It starts out by removing all of the locations, item locations, and items the player had on their character before they started playing. Once that has been done it sets up the game by placing all the items and obstacles in the appropriate places. After the game is setup its ready for the user to begin playing. It introduces them to the game by writing text to the screen to detail where the player is.

Legal Moves

Movement: Forward, Left, Right – This moves the character on the game map. To do this, the program first calls the move(Dir) and then checks the game map to see if there are any connections between the player’s current position and a positing direction they are trying to move. If there is a position available it retracts the players old location and asserts their new location to the new position on the map. If a player has entered a movement that is not connected to another part of the map then the game notifies them that they cannot move that direction.

Pickup – This move allows players to pick up items they will encounter as they move through the game map. A player can pick up as many items as they like. Once a player has picked up an item it will remain with them as they navigate the map until they decide to drop it. The pickup code first checks to see if there is an item at the same location the character is located. If there is, the game retracts the item from its location and asserts that the player now has that item in their possession. If the player tries call pickup and there is no item at that location it gives them an error message.

Drop – This allows the players to drop items they have picked up in the game. A player can drop as many items as they have. Once they don’t have any more items it will notify them. However, a player cannot specify which item they mean to pickup or drop. In order to drop an item, the code first checks to see if they have any items. If they have an item it retracts the item as being in their possession and then asserts that the item is now available at the location where it has been dropped. This way if they character was to return to that location at a later time they could again pick up the item.

Item Notification

In order for a player to notice that there are items around the map an item call was put into the code. This checks to see if there are any items at that particular location and then lists any items that it finds. This way the player is notified of the item being available for them to pickup as they play.

Gameplay

You were at the tavern having drinks with a friend. He tells you that he was trying to find treasure but was chased away by an ogre. You have a few drinks with him and then laugh it off and leave. As you’re leaving someone hits you over the head.

Dungeon & Coin – You wake up in a dungeon that is patrolled by a guard. In order to escape from the dungeon you must first find a coin in your cell. Once you have the coin you can go up to the door of the cell. You tell the guard you found something great and flash the coin to catch his interest. He, being a dull witted fellow, falls for it and opens the door. You knock him out and are free.

Blacksmith & Horseshoe – Beyond the dungeon you find yourself in an alley. Outside of the alley you wander back into town. Along the way you stumble upon an expensive looking horseshoe and decide (or not) to pick it up. As you continue around town you’ll find a blacksmith whose looking for a horseshoe he lost and is offering a great reward for it. If you have the horseshoe the blacksmith gives you your reward: a sword. The horseshoe is retracted from your possession and a sword is added to them.

Tavern & Coin – As you continue through town you may eventually wander back into the tavern you met your friend at the night before. If you happen to still have a coin you will buy yourself a drink, otherwise you’ll leave dejectedly.

Gate & Key – As you continue out of town you’ll find yourself in a valley along a path. On the path you will find a key and decide (or not) to pick it up. You will need this key in order to unlock a gate that lies at the base of a mountaintop. Without this key you cannot pass through the gate. However, once the gate is unlocked all is not clear. You must first drop the key before passing through otherwise you’ll be struck by lightning.

Ogre & Sword – Outside of the town you may find yourself stuck in an endless maze. Somewhere hidden in that maze lies an evil ogre waiting to suck your brains out. If you happen to have a sword with you, all is fair and well as you cut off its head before it can touch you. If you weren’t so lucky as to help the blacksmith out you will find yourself dead and buried.

Cliffside – There are other treacherous obstacles outside of the town. If you don’t watch your step you can fall down the cliff and die!

Treasure – Beyond the gate, and the treacherous lightning attracting key you will find the treasure your friend only dreamed about.

Game Over – That’s it! I hope you’ll play!

Pits Of Doom Lesson 10: Member Interaction

Comments 5 Standard

In the last lesson we added the ability to fight monsters on our map and added a graphic library to display our maps and updated the map editor so we can easily customize our maps and how they look from here on out. In this lesson we’ll discuss member interaction, how to build a chat room, and how to display other member’s characters on the map when they’re logged into the game.

Lesson Concepts

No matter what you may think there is no such thing as “real time” as a result of how the Internet works. Let’s take visiting your favorite website for example.

On your computer you open a browser, type in the domain name and hit enter or press the go button. When this happens your computer sends a packet of information out to your Internet connection (or wireless router) and then waits for a response. Your request is passed from place to place until it finds a server that’s waiting and listening for your requests. When the server gets your request it processes it and  sends back a result to your computer. So when you see the google homepage you’ve actually sent a request, the google server has heard your request, generated a response, and then your computer hears the response and displays the information from the google server on your browser screen. This is also why people who have dial up experience slow Internet.

So the question becomes, how do you hide or overcome delays that result from Internet communications? Some features of our Pits of Doom game are inherently perfect for covering up this problem. Take a chat room for instance. When you use a chat room you expect there will be a delay before you get your response. In this case the delay is to some degree expected by the user.

Now take our map. If we see another member on the same map level we don’t expect it to jump from place to place as it moves around. Instead it should seamlessly navigate around the map even if we’re not moving ourselves.

Creating The Chat Room

The concept behind a chat room is simple. Every time someone posts a message we store it to the database. In the chat screen we always select the last few messages inserted into the database. Finally we update the chat display every couple of seconds so it appears as if people are talking simultaneously.

Let’s start by making a table for the chat messages. We’ll need a way to identify which message belongs to which member, a place to store their text, and then the date/time they posted the message to make searching and sorting easier if we ever want to reference a particular day/time in the chat log.

CREATE  TABLE  chatroom (
  id INT NOT  NULL  AUTO_INCREMENT,
  mid INT NOT  NULL,
  message MEDIUMTEXT NOT  NULL,
  date DATETIME NOT  NULL,
  PRIMARY  KEY (id),
  UNIQUE (id)

There are two fields in this table we haven’t talked about before. The first one is mediumtext. This is one of three text options available in MySQL. There’s tinytext, mediumtext, text, and longtext. These store varying amounts of data respectively. For everything we’re doing with this game we’ll only need a mediumtext or a text field type as those are the most common for the amount of data we’re going to store. So the question is, why not always use the text or longtext options? MySQL has to allocate space for the information that would fit in the different sized text fields. By using the smallest one we need we’ll increase our database speeds and cut down on any potentially wasted space.

Implementing The Chat Room

This is a fairly simple process. We have to tie the message box to the chat message submit button. When someone presses the submit button and they have a message we’re going to add that message to the chat table.

How we display the chat messages is a bit more complicated. We have two options:

  1. Use PHP to automatically refresh the screen periodically.
  2. Use Ajax to re-generate the screen without refreshing the whole page (best solution).

Since this is a PHP/MySQL game we’re going to go with the first PHP 0ption so  you don’t have to know any Ajax. If you want the Ajax version you can contact me and purchase it as an addon for $25. Our script to display the most recent chat room messages looks like this:

<?php
/****************
* File: chattext.php
* Date: 6.17.2011
* Author: design1online.com, LLC
* Purpose: display chat text messages
*****************/
require_once('../oop/mysqlobj.php');
require_once('functions.php');
require_once('dbconnect.php');

$MAX_MESSAGES = 25;
$REFRESH_SECONDS = 5;

echo "
<html>
<head>
<meta http-equiv=\"Content-Type\" content=\"text/html\" />
<link rel=\"stylesheet\" type=\"text/css\" href=\"../css/default.css\" />
<meta http-equiv=\"refresh\" content=\"$REFRESH_SECONDS\">
</head>
<body class=\"chat\">";

$loop = mysql_query("
    SELECT
        C.mid,
        M.username,
        C.message,
        C.date
    FROM
        chatroom C
    INNER JOIN members M ON M.id = C.mid
    ORDER BY
        C.id DESC
    LIMIT $MAX_MESSAGES")
    or die ('Cannot load chat room messages ' . mysql_error());

while ($row = mysql_fetch_assoc($loop))
    echo "<span class=\"datetime\">" . date('g:ia', strtotime($row['date'])) . "</span>" . htmlentities($row['username']) . ": " . htmlentities($row['message']) . "<br/>";

echo "</body>
    </html>";

Displaying Other Members

Not only do we want to talk to other members we want to see them on the map. In order to do this we’ll need to check to see if the online flag is set to true. Whenever we login using our login form we automatically set this value to true. The only problem we have now is how to determine if they’re still playing or not.

Therefore we’re going to create a new script called online.php and call it every time someone moves their character around the board. We’ll use this script to automatically logout anyone who hasn’t been active recently and restore the online status to anyone whose currently still playing. So in a nutshell online.php has to do two things:

  1. Remove the online status from any player whose last login is more than 20 minutes ago
  2. Update the member’s last login date to the current date and time any time they interact with the game
/*******
* Check to see if there is another character in this spot
*******/
function hasMember($x, $y, $z, $mapid)
{
    global $_SESSION;

    $result = $_SESSION['database']->query("SELECT M.id FROM characters C
            INNER JOIN members M ON M.id = C.memberid
            WHERE C.active=1 AND M.online=1 AND C.x='$x' AND C.y='$y' and C.z='$z'
            AND C.mapid = '$mapid' LIMIT 1");

    $row = mysql_fetch_assoc($result);

    return $row['id'];
}

Once we’ve established a way to determine which members are still online and which members are currently offline we can modify our displayMap function to show other members currently on the map if there is no monster present. In order to do this we first have to find all active characters (remember they can have more than one character eventually) then check to make sure the owners of those characters are online. Once we know they’re online we can check the character’s coordinates to figure out whether or not they are on a specific map location.

Game Files

This lesson we implemented a chat room, created a script to update whether or not a member is currently logged in and wrapped it up by displaying those member’s characters on our map.

Play The Working Version!

Sign into Pits of Doom using the login below or create an account today! To really see this lesson in action it’s best if you open two different browsers and login to each of them (using the same browser with two windows won’t work!) to see other character’s that are current logged into the game.

Account 1

Account 2

Username: test

Password: test

Username: test2

Password: testing

Download The Source Code

Use the view source utility or download a .zip archive.

Lesson Concepts

Member interaction is a huge part of an online game with any kind of social or interactive quality about it. In pits of doom we’d like to be able to trade/buy weapons and fight other members so it’s important that they be able to communicate and see other characters on the game world. In the next lesson we’ll look at updating our chat room so you can click on another member’s name to view more about their characters and send them private messages.

Go Back to Lesson 9 or Continue to Lesson 11

PHP Tutorial: Free OOP Hangman Game (no database required)

Comments 8 Standard

At this year’s Siminar I’m presenting a live webinar on “Learning OOP” to many young female gamers. Part of that presentation includes a OOP hangman game they can easily install and incorporate on their existing websites. There is no database required, the only software requirements are php version 4 or higher. To get this game running all you have to do is upload the files to a folder on your website then edit the config/wordlist.txt file.

Getting Started

If you take a minute to think about it most games have a few things in common. So we’re going to start by making a class that has all the attributes and methods use in any generic game. For instance, most games keep track of the score, the player’s health, whether or not they’ve won the game and if they’ve had a game over. These are things we don’t want to write over and over again with every game we make so we’ll make one generic class for methods and attributes most games have in common.

Now we can use this generic class to extend any other game we make in the future. Extending a class means one class, the child, will inherit all of the attributes and methods of the other class, the parent. Our generic game class is a parent class of a specific game class for our hangman game. For more information on classes or if you want a refresher course on OOP programming I suggest you read this tutorial.

The Game Class

Our game class contains all of our generic attributes and methods. These are things we expect any game we make might have so we can use this game class over and over again. Because this game class will be extended by other classes it’s known as a parent class.

<?php

class game {

    var $health;    //int - player's health
    var $over;        //bool - toggle game over
    var $score;        //int - player's score
    var $won;        //bool - toggle game won

    /**
    * Purpose: setup game environment variables
    * Preconditions: turn the debugger on (optional)
    * Postconditions: the game environment is ready to start a game
    **/
    function start()
    {
        $this->score = 0;
        $this->health = 100;
        $this->over = false;
        $this->won = false;
    }

    /**
    * Purpose: end the game
    * Preconditions: turns on the game over flag
    * Postconditions: game over flag is true
    **/
    function end()
    {
        $this->over = true;
    }

    /**
    * Purpose: change or retrieve the player's score
    * Preconditions: amount (optional)
    * Postconditions: returns the player's updated score
    **/
    function setScore($amount = 0)
    {
        return $this->score += $amount;
    }

    /**
    * Purpose: change or retrieve the player's health
    * Preconditions: amount (optional)
    * Postconditions: returns the player's updated health
    **/
    function setHealth($amount = 0)
    {            
        return ceil($this->health += $amount);
    }

    /**
    * Purpose: return bool to indiciate whether or not the game is over
    * Preconditions: none
    * Postconditions: returns true or flase
    **/
    function isOver()
    {
        if ($this->won)
            return true;

        if ($this->over)
            return true;

        if ($this->health < 0)
            return true;

        return false;
    }

} //end game class

/**
* Purpose: display a formatted debug message
* Preconditions: the object or message to display
* Postconditions: returns the player's updated score
**/
function debug($object = NULL, $msg = "")
{
    if (isset($object) || isset($msg))
    {
        echo "<pre>";

        if (isset($object))
            print_r($object);

        if (isset($msg))
            echo "\n\t$msg\n";

        echo "\n</pre>";
    }
}

/**
* Purpose: return a formatted error message
* Preconditions: the message to format
* Postconditions: formatted message is returned
**/
function errorMsg($msg)
{
    return "
$msg
"; } /** * Purpose: return a formatted success message * Preconditions: the message to format * Postconditions: formatted message is returned **/ function successMsg($msg) {     return "
$msg
"; }

The Hangman Class

This is the most complicated part of our hangman game. The hangman class will drive all of our game setup, loading, and respond to the user’s entry choices. We’ve extended it so that it will inherit all of the attributes and methods we’ve created in our game class. This way we don’t have to write those attributes and methods over and over again each time we create a new game.

Overall this file is split into three main parts. The loading of the game is done by the newGame() method. This loads our word list from a separate file and then randomly selects a word for the player to guess. It also resets the player’s score and letter guesses in case they play again once the first game is over.

The second part of the file displays or drives the game in the playGame() method. It decides whether or not to start a new game, respond to a letter that was guessed, show error messages and check to see if they guess the right word or if they’ve run out of guesses.

The last part two methods in the file are helper methods. isLetter() checks to make sure they’ve entered a valid letter in the alphabet and wprdToArray converts our word to an array so we can compare it with the letters they’ve guessed so far.

<?php

class hangman extends game
{
    var $guesses;                //int - maximum guesses per word
    var $letters = array();        //array - letters guessed so far
    var $wordIndex;                //int - index of the current word
    var $wordLetters = array();    //array - array of the letters in the word
    var $wordList = array();    //array - list of words they can guess

    var $alphabet = array(        //array - all letters in the alphabet
        "a", "b", "c", "d", "e", "f", "g", "h",
        "i", "j", "k", "l", "m", "n", "o", "p",
        "q", "r", "s", "t", "u", "v", "w", "x",
        "y", "z");

    var $punctuation = array(    //array - punctuation marks in the word list
        " ", "\,", "\'", "\"", "\/", "\\", "\[",
        "\]", "\+", "\-", "\_", "\=", "\!", "\~",
        "\~", "\@", "\.", "\?", "\$", "\#", "\%",
        "\^", "\&", "\*", "\(", "\)", "\{", "\}",
        "\|", "\:", "\;");

    /**
    * Purpose: default constructor
    * Preconditions: none
    * Postconditions: parent object started
    **/
    function __construct()
    {
        /**
        * instantiate the parent game class so this class
        * inherits all of the game class's attributes
        * and methods
        **/
        game::start();
    }

    /**
    * Purpose: start a new hangman game
    * Preconditions: maximum number of guesses
    * Postconditions: game is ready to be displayed
    **/
    function newGame($max_guesses = 5)
    {
        //setup the game
        $this->start();

        //make sure we clear out the last letters they guessed
        $this->letters = array();

        //set how many guesses they get before it's a game over
        if ($max_guesses)
            $this->setGuesses($max_guesses);

        //pick a word for them to try and guess
        $this->setWord();
    }

    /**
    * Purpose: set or retrieve maximum guesses before game over
    * Preconditions:
    * Postconditions:
    **/
    function playGame($game)
    {
        //player pressed the button to start a new game
        if (isset($game['newgame']) || empty($this->wordList))
            $this->newGame();

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

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

    /**
    * Purpose: set or retrieve maximum guesses they can make
    * Preconditions: amount of guesses (optional)
    * Postconditions: guesses has been updated
    **/
    function setGuesses($amount = 0)
    {        
        $this->guesses += $amount;
    }

    /**
    * 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 "
" . $this->picture() . "
                 
" . $this->solvedWord() . "
                 
                    Enter A Letter:                                                                    
";                   if (!empty($this->letters))                     echo "
Letters Guessed: " . implode($this->letters, ", ") . "
";         }         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 "
" . $this->picture() . "
";             }             echo "
";         }     }     /**     * 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))         {             //increase their score based on how many guesses they've used so far             if ($this->health > (100/ceil($this->guesses/5)))                 $this->setScore(5);             else if ($this->health > (100/ceil($this->guesses/4)))                 $this->setScore(4);             else if ($this->health > (100/ceil($this->guesses/3)))                 $this->setScore(3);             else if ($this->health > (100/ceil($this->guesses/2)))                 $this->setScore(2);             else                 $this->setScore(1);             //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.");         }     }     /**     * Purpose: pick a random word to try and solve     * Preconditions: none     * Postconditions: if the word exists a word index has been set     **/     function setWord()     {         //if the word list is empty we need to load it first         if (empty($this->wordList))             $this->loadWords();         //reset the word index to a new word         if (!empty($this->wordList))             $this->wordIndex = rand(0, count($this->wordList)-1);         //convert the string to an array we can use         $this->wordToArray();     }     /**     * Purpose: load the words from the config file into an array     * Preconditions: filename to load the words from (optional)     * Postconditions: the word list has been loaded     **/     function loadWords($filename = "config/wordlist.txt")     {         if (file_exists($filename))         {             $fstream = fopen($filename, "r");             while ($word = fscanf($fstream, "%s %s %s %s %s %s %s %s %s %s\n")) {                 $phrase = "";                 if (is_string($word[0]))                 {                     foreach ($word as $value)                         $phrase .= $value . " ";                     array_push($this->wordList, trim($phrase));                 }             }         }     }     /**     * Purpose: return the image that should be displayed with this number of wrong guesses     * Preconditions: none     * Postconditions: picture returned     **/     function picture()     {         $count = 1;         for ($i = 100; $i >= 0; $i-= ceil(100/$this->guesses))         {             if ($this->health == $i)             {                 if (file_exists("images/" . ($count-1) . ".jpg"))                     return "<img src=\"images/" . ($count-1) . ".jpg\" alt=\"Hangman\" title=\"Hangman\" />";                 else                     return "ERROR: images/" . ($count-1) . ".jpg is missing from the hangman images folder.";             }             $count++;         }         return "<img src=\"images/" . ($count-1) . ".jpg\" alt=\"Hangman\" title=\"Hangman\" />";     }     /**     * Purpose: display the part of the word they've solved so far     * Preconditions: a word has been set using setWord()     * Postconditions: the letters they've guessed correctly show up     **/     function solvedWord()     {         $result = "";         for ($i = 0; $i < count($this->wordLetters); $i++)         {             $found = false;             foreach($this->letters as $letter) {                 if ($letter == $this->wordLetters[$i])                 {                     $result .= $this->wordLetters[$i]; //they've guessed this letter                     $found = true;                 }             }             if (!$found && $this->isLetter($this->wordLetters[$i]))                 $result .= "_"; //they haven't guessed this letter             else if (!$found) //this is a space or non-alpha character             {                 //make spaces more noticable                 if ($this->wordLetters[$i] == " ")                     $result .= "&nbsp;&nbsp;&nbsp;";                 else                     $result .= $this->wordLetters[$i];             }         }         return $result;     }     /**     * Purpose: convert the selected word to an array     * Preconditions: a word has been selected     * Postconditions: wordLetters now contains an array representation of the     *    selected word     **/     function wordToArray()     {         $this->wordLetters = array();         for ($i = 0; $i < strlen($this->wordList[$this->wordIndex]); $i++)             array_push($this->wordLetters, $this->wordList[$this->wordIndex][$i]);     }     /**     * Purpose: check to see if this value is a letter     * Preconditions: value to check     * Postconditions: returns true if letter found     **/     function isLetter($value)     {         if (in_array($value, $this->alphabet))             return true;         return false;     } }

Playing The Game

Now that we have our classes in place we need to create a file that instantiates, or creates, the hangman object. We’ll save our object to a session so we don’t loose the player’s data as they refresh the page. Finally we call the hangman’s playGame() method passing it the $_POST data so we know how to respond to the buttons they’ve clicked.

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

//this will keep the game data as they refresh the page
session_start();

//if they haven't started a game yet let's load one
if (!isset($_SESSION['game']['hangman'])) {
    $_SESSION['game']['hangman'] = new hangman();
}
?>
<html>
    <head>
        <title>Hangman</title>
        <link rel="stylesheet" type="text/css" href="inc/style.css" />
    </head>
    <body>
        
        " method="POST">         

Let's Play Hangman!

        playGame($_POST); }       ?>              
    </body> </html>

Conclusion

Try the working version! In this tutorial we created two classes, a game class and a hangman class. Because all games have several aspects in common we’ve extended our game class to make our hangman class. This way we can use the attributes and methods of the game class for other games we might make in the future. When our classes were done we created a final file to load and store the game data in a session so we don’t loose it as the page reloads and then told the game to display itself by calling the playGame() method.

Download The Source

Get the .zip archive