30 game scripts you can write in PHP, Part 3: Creating 10 advanced scripts

PHP is an easy to use, easy to learn, widely accessible programming language. It's well suited for developing simple scripts that you can use to help you in all kinds of games. Whether you play simple pen and paper games by yourself, complex tabletop role-playing games with a group of people, or online games of any kind, this series will have something for you. This article will build on Parts 1 and 2 of this "30 game scripts you can write in PHP" series, exploring 10 cool scripts that can be used in various types of games — from an inventory-management system and note-taking scripts to word-game scripts to image manipulation and scripts for games like keno.

Duane O'Brien, PHP developer, 自由职业者

Duane O'Brien has been a technological Swiss army knife since the Oregon Trail was text only. His favorite color is sushi. He has never been to the moon.



02 December 2008

Also available in Russian Japanese

In this article, we build an inventory-management system and note-taking scripts for your role-playing games, while working on the interactivity of your PHP scripts. We also build an image-based ID card generator and get your feet wet performing image manipulation with PHP. We will create a poker-hand evaluator, a slot-machine generator, and a casino bank, allowing you to practice some trickier PHP logic. We will take our word-game scripts one step further working with a difficult logic by building a complex anagram generator, a crypto solver, and more.

These scripts are a little harder than the scripts from Part 1 and Part 2 because they accomplish a lot more. The code archive for this article contains the full source code for each script we will discuss.

As in Part 1 and Part 2, we're going to be blazing through these scripts pretty fast. If you haven't run through Parts 1 and 2, you should do so before starting this article.

Basic inventory-management system

Having already put together a script to manage character stats, let's take the script one step further and add some basic inventory management. Using this script, we keep track of spent ammo, burnt torches, and found items (see Listing 1). Rather than try to lay out all the items you might ever find, we create two input fields — one for the name of the item and one for the item count: <input name='newitem' /> <input name='newcount' />. When the script is submitted, we want to add the new item to an inventory array and save the array along with the character sheet. Then we walk the inventory array and create new input fields to hold the items.

Lisiting 1. Adding some basic inventory management
foreach ($character['inventory'] as $name => $count) {
    echo $name . " : <input name='inventory[" . $name . "]' value='" .
$count . "' /><br />";
}

Those input names may look weird, but by structuring them in this fashion, PHP will create the "inventory" array automatically, complete with name-value pairs. See the script archive to see how this is all pulled together.


A simple note-taking script

It's helpful when playing an RPG to scribble down some notes. The notes are less helpful when you can't figure out where you wrote down the important parts. Let's put a script together that helps store and find game notes.

Taking a note requires only two form fields: <input name='title' /> <br /> <textarea name='body'> <br />.

As with the character sheet, the notes are added to an array, serialized, and stored in a text file. As you save the note, you'll analyze the words. The word count is also stored in the same text file. To make this easy, replace anything that isn't a letter or number and lowercase the whole note (only for the analysis): $analyze = preg_replace('/[^0-9a-z\s]/', strtolower($_POST['body']));. Then split on white spaces and push the note ID into the word-count array for each word.

Listing 2. Script that helps easily store and find game notes
$noteid = count($masternotes);
$masternotes[] = array('title' => htmlentities($_POST['title']), 
        'body' => htmlentities($_POST['body']));
$words = preg_split('/\s/', $note['body'] . ' ' . $note['title']);
foreach ($words as $word) {
    $word = preg_replace('/[^0-9a-z]/', '', strtolower($word));
    if (!in_array($word, $ignore_these_words)) {
        if (isset($counts[$word])) {
                $counts[$word][$noteid] = $noteid;
        } else {
            $counts[$word] = array($noteid => $noteid);
        }
    }
}

You can see how to retrieve individual notes, or notes by keyword, by trying out the test script or looking in the code archive.


ID-card generator

If you're playing a modern-era game, having a physical ID card for your character can impress your friends and add a sense of realism to a game. You're going to put together some PHP image-manipulation code that will allow you to create an ID-card image suitable for lamination. To start, we need a basic image to use as an ID blank. We will use this crude ID blank I pulled together for the example.

Figure 1. Example ID blank
Example ID blank

We need a basic form to collect the fields for the ID card (Name, Authorization, Home, Birth date, Hair and Eye color). Then we need to push those values as strings into the image. Some of this takes trial and error to get right, as you determine the right places to start and stop text: imagettftext ( $img , 40 , 0 , 600 , 200 , 0 , "tarzeau_-_OCR-A", $_POST['name'] );. If you've done everything correctly, you should be able to fill out the form and get something like Figure 2.

Figure 2. Example ID filled out
Example ID filled out

The font specified is included in the code archive, and the source image is 300DPI. Getting the font and ID card working can be tricky on some systems, but the source in the script archive calls out the problem areas.


Tile-based map generator

Early computer games used tiles to create game maps. The same basic tile approach is still used today in some circumstances. Let's put together a script to generate a basic map. This map won't be very big, but we can use the same principles to create much larger maps, of varying detail. To start, create a few different terrain types and throw together some really rudimentary map tiles to represent each terrain type.

Figure 3. Basic terrain types
Basic terrain types

Now that we have basic terrain types, you can imagine what comes next: We create an array of map tiles for each row. But we will get poor results by just picking the terrain types randomly and shoving them into arrays.

Let's make a map that's 20 tiles in size. When picking a terrain type, consider the surrounding tiles when making decisions. Look at the four closest-known tiles: the last tile in the current row and the three tiles directly above the current tile. We can set up complex rules to govern how terrain is generated, but for now, start with something simple: Add the terrain types of the four closest tiles to the array before picking them randomly. The more times we add the previous terrain types, the more likely we will get homogeneous regions. The result is something like Listing 3.

Listing 3. An array of map tiles
$map = array();
$terrain = array ('plains', 'forest', 'swamp', 'hills', 'mountain', 'water');
for ($row = 0; $row < 20; $row++) {
    $map[] = array();
    for ($column = 0; $column < 20; $column++) {
        $pool = $terrain;
        if (isset($map[$row-1])) {
            if (isset($map[$row-1][$column-1])) {
                $pool[] = $map[$row-1][$column-1];
                $pool[] = $map[$row-1][$column-1];
            }
            $pool[] = $map[$row-1][$column];
            $pool[] = $map[$row-1][$column];
            if (isset($map[$row-1][$column+1])) {
                $pool[] = $map[$row-1][$column+1];
                  $pool[] = $map[$row-1][$column+1];
            }
        }
        if (isset($map[$row][$column-1])) {
                $pool[] = $map[$row][$column-1];
                $pool[] = $map[$row][$column-1];
        }
        shuffle($pool);
        $map[$row][$column] = $pool[0];
    }
}

Once we have a map array, we iterate through each row and column, including an image for each terrain type. Try out the test script to see what kinds of maps this script will generate. The sky is the limit with this script. We can give individual terrain types different weights by pushing them into the pool multiple times and create more complex rules about what terrain types can touch. Don't be afraid to experiment.


Poker-hand evaluator

We've already written a poker dealer and a blackjack dealer. Let's go one step further and write a hand evaluator for poker. This is more complicated, and working how to sort hands of the same type could take some time to work out. But it's pretty easy to have the script look at five cards and tell you if there is a straight, a flush, etc.

When looking at the cards in the hand, there are several easy rules to apply:

  1. If the suit of the current card is not the same as the suit of the last card, there is no flush.
  2. If the face of any card matches the face of any previous card, there is no straight.
  3. If no cards match, and if the $highcard minus $lowcard is exactly five, there is a straight.

Once these simple rules are in place, apply a simple check to work out the matches:

  1. If there are five different faces, there are no matches.
  2. If there are four different faces, there is one pair.
  3. If there are three different faces, there is either two pair or three of a kind.
  4. If there are two different faces, there is either a full house or four of a kind.
  5. If there is only one face, you have cheated.

See how these rules are put in play by reviewing the sample script in the archive.


Slot machine

Modern slot machines are complicated beasts with video screens, multiple paylines, more buttons, and fewer levers to pull. For this example, we simulate a simple three-wheel slot machine with one payline. Let's start by identifying what faces are on the wheel.

Listing 4. Indentifying faces on the wheel
$faces = array ('Cherry', 'Bar', 'Double Bar', 'Triple Bar', 'Diamond', 'Seven');

Next, establish what the winning results are. There can be many variations of winning results, but for the example, let's say a winning result is three matching results on the payline. We need to set up some association to the winning result and the payout. You might do something like Listing 5.

Listing 5. Winning results and payouts
$payouts = array (
    'Bar|Bar|Bar' => '5',
    'Double Bar|Double Bar|Double Bar' => '10',
    'Triple Bar|Triple Bar|Triple Bar' => '15',
    'Cherry|Cherry|Cherry' => '20',
    'Seven|Seven|Seven' => '70',
    'Diamond|Diamond|Diamond' => '100',
);

When you spin the wheels, the first and third wheels spin in one direction, while the second wheel spins the other direction. With this in mind, let's set up the wheels.

Listing 6. Setting up the wheels
$wheel1 = array();
foreach ($faces as $face) {
    $wheel1[] = $face;
}
$wheel2 = array_reverse($wheel1);
$wheel3 = $wheel1;

In the code, we reversed the array to set up $wheel2. We want to keep track of the wheel positions from play to play, but the rest of the exercise is pretty simple. Each wheel must go around at least once, no more than 10 times. Use modulo to simulate rolling the wheel: $result1 = $wheel1[rand(count($wheel1), 10*count($wheel1)) % count($wheel1)];. Finally, look up the result in $payouts.

Listing 7. Looking up the results in $payouts
if (isset($payouts[$result1.'|'.$result2.'|'.$result3])) {
    // give the payout
}

Please note: This is really rudimentary. Actual slot machines are far more complicated, and balancing the payout is tricky. This is just a learning exercise. Work with the code in the script archive and see if you can tweak the payouts to be something a little more reasonable.


Keno

Keno is a cross between bingo and a lottery. Twenty numbers, from 1 to 80, are selected at random, with players placing bets on which numbers will be drawn (by picking up to 15 numbers). Keno payouts vary widely, but setting up a script to simulate playing Keno is pretty simple, especially since we've already done very similar things in this series. For starters, you need an array of numbers from 1 to 80.

Listing 8. An array of numbers from 1 to 80
$balls = array();
for ($i = 1; $i <= 80; $i++) {
    $balls[] = $i;
}

shuffle($balls);

Enter your guesses in a single text field, separated by a comma. Then simply slice out 20 numbers from the $balls array, and check the guesses against the drawn numbers. This is done easily and can be seen in the code archive. The script could be improved by displaying the picks in a Keno-style board. Try this when you are comfortable with the script.


Cryptogram helper

Crypto is a game run in most newspapers, where the goal is to decode a given phrase that has been encoded using a substitution cypher, like the one we wrote in Part 2. Let's write something that uses basic frequency analysis to decode an encoded message. You could use the same approach to help solve Crypto problems.

Frequency analysis is just a fancy way of saying "counting letters." Take a sample of unencoded text, count how many times specific letters appear in the text, and sort the letters in order from most to least frequent. Then do the same thing for the encoded text. When done, put the two lists of letters next to each other, and the result is a reasonable guess at a decoding key. This works best with large blocks of text, but the approach can still be used when dealing with shorter phrases. Start with two text boxes: one for unencoded text and one for the Crypto. Count the letters in the unencoded text and sort the array results.

Listing 9. Counting the letters in the unencoded text and sorting the array results
$unencoded = str_split(preg_replace('/[^a-z]/', '', strtolower($_POST['unencoded'])));
foreach ($unencoded as $letter) {
    $lettercount[$letter]++;
}

arsort($lettercount);

Then, do the same with the encoded text. Once we have the arrays sorted by letter frequency, create a map of letters and use them to try decoding the text. If you're especially lucky, the text will decode the first time, but don't count on it. The result will probably be partially decoded text — a good starting point for working out the puzzle. Check out the script sample for more details. There's plenty of room for improving this script.


Mastermind

Mastermind is a board game (based on an older game) where the player must guess the order and color of pegs set by the codemaker. In this version, the script will be the codemaker, and you will make a series of guesses. The script will tell you how many guesses are the right color, but wrong position, and how many guesses are exactly correct. Mastermind uses six different-colored pegs, and you may repeat colors. Start by establishing the code shown below.

Listing 10. Array of six different-colored pegs
$pegs = array ('R','O','Y','G','B','V');
$code = array();
for ($i = 0; $i < 4; $i++) {
    $code[] = $pegs[rand(0,5)];
}

Next, we need something to analyze the guess (see Listing 11). Guesses are entered in a single text field, such as OOGBVR.

Listing 11. Analyzing the guess
$guess = str_split($_POST['guess']);
if ($guess == $code) {
    // the code has been guessed.
} else {
    for ($i = 0;$i < 4;$i++) {
        if ($guess[$i] == $code[$i]) {
            // A correct guess
        } else {
            // Keep track of the guessed colors, for output later
        }
    }
}

See how it is all pulled together in the sample script. This is a fun little game that can be modified in many ways.


Word chains

You have probably played the game before where you take a single word and change just one letter to make a different word. Usually this game is played with the intent to change from one word to another, like from the word BIKE to the word FATE using the following word chain: BIKE, LIKE, LAKE, LATE, FATE. Let's create a script to generate word chains from a single word, using some of the code we created for the crossword helper (see Listing 12). Given a single word, like BIKE, we can create four words with missing letters(.IKE, B.KE, BI.E and BIK.) that can be passed through the crossword helper to find possible matches.

Listing 12. Generating word chains from a single word
$letters = str_split($link);
foreach ($letters as $key => $letter) {
    foreach ($words as $word) {
        if ($key == 0) {
            $guess =  '.' . substr($link, 1);
        } else if ($key+1 == count($letters)) {
            $guess =  substr($link, 0, $key) . '.';
        } else {
            $guess =  substr($link, 0, $key) . '.' . substr($link, $key+1);
        }
        if (preg_match("/^" . $guess . "$/",$word)) {
            $matches[] = $word;
        }
    }
}

Once a match is found, we do the same thing with the match (making sure not to duplicate words). Continue this same logic as long as you keep finding matches. We can even branch the solutions for every possible match. Doing so would be a useful exercise, and a helpful lesson in runaway recursion. Once you're comfortable with the sample script, you should give this a try.


Summary

I hope this "30 game scripts you can write in PHP" series put you on the road to creating interesting game scripts with PHP. Regardless of how you like to play, you can enrich the gaming experience using the simple utilities presented in this series. PHP makes a great language choice for these tasks, and if you've followed along here, you've no doubt improved your PHP programming skills while having fun.


Download

DescriptionNameSize
10 advanced PHP scriptsos-php-gamescripts3.zip844KB

Resources

Learn

Get products and technologies

  • Innovate your next open source development project with IBM trial software, available for download or on DVD.
  • Download IBM product evaluation versions, and get your hands on application development tools and middleware products from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Open source on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source
ArticleID=354916
ArticleTitle=30 game scripts you can write in PHP, Part 3: Creating 10 advanced scripts
publish-date=12022008