30 game scripts you can write in PHP, Part 2: Developing 10 intermediate scripts

PHP is an easy-to-use, easy-to-learn, widely accessible programming language. It's well suited to developing simple scripts 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 Part 1 of this "30 game scripts you can write in PHP" series, exploring 10 intermediate scripts that can be used in various types of games. These scripts are intended for three types of games: role-playing games, games of chance, and word games.

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.


developerWorks Contributing author
        level

25 November 2008

Also available in Russian Japanese Vietnamese

In Part 1 of this series, you created some beginner-level PHP scripts. Now it's time to up the ante with some more complexity that will further develop your PHP programming skills and enhance your status as a game master.

For the role-playing game scripts, you will learn how to put together a weapon damage calculator, a character sheet stat tracker, and a Non-Player Character (NPC) generator, while teaching you how to save information to a file and work with arrays in PHP. The scripts related to games of chance will help you practice Blackjack, learn how to count cards, and get a quick understanding of Bingo fundamentals while teaching you how to work the element of chance into your PHP scripts. Finally, the word-game scripts will help you solve the Jumble game, create simple substitution cyphers, and generate word-search diagrams while taking your PHP array-handling skills one step further.

We will blaze through these scripts quickly without discussing finding a host or the setting up a server. There are many Web hosts that offer PHP, and the XAMPP installer is easy to use if you want to set up your own. We won't spend a lot of time talking about PHP best practices or game-design techniques either. The scripts covered here are designed to be simple to understand, easy to use, and quick to learn.

The code archive for this article contains the full source code for each script we will discuss, and you can see the scripts in action by visiting Chaoticneutral.net.

Damage calculators

In Part 1, we built a basic die roller. Let's look at using that die roller to help put together a basic damage calculator.

If you've ever run or played a table-top role-playing game, you've probably seen the big charts of weapons included in the books. Usually, these charts include the name of the weapon and how much damage the weapon does. It might look something like what is shown in Table 1.

Table 1. Damage calculations
Weapon NameDamage
Little Stick1d6
Big Stick1d6+4
Chainsaw2d8

You should be able to take the simple roll function created in Part 1 of this "30 game scripts you can write in PHP" series and use it to calculate the damage for a base set of weapons. To do this, you'll create an array to hold information about the weapons.

For this example, an individual weapon has three basic characteristics: a Name, a Die Roll, and a Damage Bonus. Using these characteristics, you might build your weapons array, like that shown below.

Listing 1. Weapons array
$weapons = array (
    'littlestick' => array (
        'name' => 'Little Stick',
        'roll' => '1d6',
        'bonus' => '0',
    ),
    'bigstick' => array (
        'name' => 'Little Stick',
        'roll' => '1d6',
        'bonus' => '4',
    ),
    'chainsaw' => array (
        'name' => 'Little Stick',
        'roll' => '2d8',
        'bonus' => '0',
    ),
);

Using this array, you can build a table that shows the weapon, the damage the weapon can do, and the result of a damage roll.

Listing 2. Building the weapons table
foreach ($weapons as $weapon) {
    list($count, $sides) = explode('d', $weapon['roll']);
    $result = 0;
    for ($i = 0; $i < $count;$i++) {
        $result = $result + roll($sides);
    }
    echo "<tr><td>" . $weapon['name'] . "</td><td>" 
. $weapon['roll'];
    if ($weapon['bonus'] > 0) {
        echo "+" . $weapon['bonus'];
        $result = $result + $weapon['bonus'];
    }
    echo "</td><td>" . $result . "</td></tr>";
}

Using this script, you can create a basic weapons chart with an integrated damage calculator.


Stat tracking

Now that you can calculate damage, let's look at keeping track of some basic character stats, like you might use if you were running a game with several NPCs. We create a simple array to hold character information and save it (serialized) to a text file. (There are many ways to save this information to a database, and you should move in that direction after you're comfortable working with PHP.) For this example, you'll craft a basic zombie character from a game I wrote called Shambles. Start with an array to hold the character information.

Listing 3. Array for character information
$character = array(
    'name' => 'Fred The Zombie',
    'health' => '36',
    'gore' => '1',
    'clutch' => '5',
    'brawn' => '6',
    'sense' => '4',
    'flail' => '2',
    'chuck' => '3',
    'lurch' => '4',
);

You need to derive a filename to store this information. You really don't want to just use input as it's sent in, but you can use only the letters from the character name to create a file.

Listing 4. Creating a file to store the information
$filename = substr(preg_replace("/[^a-z0-9]/", "", 
      strtolower($character['name'])), 0, 20);
file_put_contents($filename, serialize($character));

Note: When making this script for public consumption, you'll want to filter user input to keep malicious people from injecting bad JavaScript or garbage into the files. This is just an example for your personal use. Finally, you need to present the information in a form to allow for easy updating. It's just a matter of creating a form with default values in the input fields.

Listing 5. Presenting the information in a form
<input name='health' value='<?php echo $character['health'] ?>' />

Use this basic script (see the code archive) to keep track of several characters and NPCs.


NPC generator

When you're putting together a big game, it's useful to have a pool of disposable NPCs. This is especially helpful if you need a large number of thugs or cannon fodder. To put together the NPC generator, let's build an array that contains the rules for building a character, then use the Random Name Generator and Die Roller to pull together a bunch of characters all at once.

Using Fred the Zombie as an example, you can define the rules to create a character. Some stats are static (health always starts at 36), some stats are derived (gore is health/6) and some stats are the result of die rolls (clutch is 1d6 — one die with six sides). The rules look like those shown in Listing 6.

Listing 6. Defining rules to create a character
$rules = array(
    'health' => '36',
    'gore' => 'health/6',
    'clutch' => '1d6',
    'brawn' => '1d6',
    'sense' => '1d6',
    'flail' => '1d6',
    'chuck' => '1d6',
    'lurch' => '1d6',
);

Now you can write a little code to evaluate the rules.

Listing 7. Code for evaluating the rules
foreach ($rules as $stat=>$rule) {
    if (preg_match("/^[0-9]+$/", $rule)) {
        // This is only a number, and is therefore a static value
        $character[$stat] = $rule;
    } else if (preg_match("/^([0-9]+)d([0-9]+)/", $rule, $matches)) {
        // This is a die roll
        $val = 0;
        for ($n = 0;$n<$matches[1];$n++) {
            $val = $val + roll($matches[2]);
        }
        $character[$stat] = $val;
    } else if (preg_match("/^([a-z]+)\/([0-9]+)$/", $rule, $matches)) {
        // This is a derived value of some kind.
        $character[$stat] = $character[$matches[1]] / $matches[2];
    }
    echo $stat . ' : ' . $character[$stat] . "<br />\n";
}

Once you are properly evaluating your rules, you can pull in a random name using the Random Name Generator and make NPCs for days. Please review the script in the code archive for how this task is pulled together.


Odds calculator: Die roll

In Part 1, you put together a simple odds calculator to determine the chance of pulling a card of a specific face or suit from a given set of cards. Now let's build an odds calculator for die rolls. For this example, you will build a calculator that determines possible outcomes when rolling two six-sided dice (2d6). This is useful if you're using any RPG system that is d6-based, or playing a board game like Monopoly or Backgammon, or if you're trying to work out the odds in a dice game like Craps.

First, establish how possible outcomes are determined when rolling two dice. It will be helpful to think of rolling two differently colored dice, like a red die and a blue die. If the red die lands on a 1, there are six possible results, one for each side of the blue die. This means that the total number of possible die results are 6^2 or 36 outcomes. This may seem a little strange, since the highest value you can actually get is 12 (a red six and a blue six), but should make more sense if you realize you can get an 11 with two different outcomes (a red six and a blue five or a red five and a blue six).

Let's assume for this example that you want both sum (you rolled a 10) and face (you rolled a 6 and a 4) results and that there's no difference between a 6 on a red die and a 6 on a blue die. The important numbers are S (how many sides on the die) and N (how many die) — given N number of identical dice, the maximum number of outcomes is S^N — though some of these outcomes may be identical (a 6 and a 4, vs. a 4 and a 6).

Listing 8. Building an odds calculator for a die roll
$s = 6;
$n = 2;
$results = array(array());
for ($i = 0; $i < $n; $i ++) {
    $newresults = array();
    foreach ($results as $result) {
        for ($x = 0; $x < $s; $x++) {
            $newresults[] = array_merge($result, array($x+1));
          
        }
    }
    $results = $newresults;
}

This probably seems a little clunky. You can streamline this code considerably, but I've done things a little more explicitly here so you can understand how the arrays are being handled. Once you have built up your final array, you can iterate through the array and generate a list of sums and what their respective odds are. This code is included in the code archive.


Simple Blackjack dealer

In Part 1, you built a simple poker dealer. The dealer didn't actually play against you; it just dealt cards from the shoe, to let you practice various hands. Now let's build a Blackjack dealer that actually plays against you. You will build this dealer using the simple house rule that the dealer must draw to at least 17.

Portions of this code will look like the simple Poker dealer built previously. For Blackjack, you need to build in iterative drawing and hand evaluation. You'll want an array that assigns face values to their numeric descriptions. You can do this in the original $faces array if you change how the deck is built.

Listing 9. Modifying the $faces array for Blackjack
$faces = array('Two' => 2, ... ... ... 'King' => 10, 'Ace' => 11);

Then you need a basic function to evaluate a given hand. This function will look at the cards in the hand and return the total value of the hand.

Listing 10. Function for evaluating a given hand
function evaluateHand($hand) {
    global $faces;
    $value = 0;
    foreach ($hand as $card) {
        if ($value > 11 && $card['face'] == 'Ace') {
            $value = $value + 1;  // An ace can be 11 or 1
        } else {
            $value = intval($value) + intval($faces[$card['face']]);
        }
    }
    return $value;
}

Once you have a function in place to evaluate a single hand, it should be a simple matter to determine if your hand has gone over 21, or if the dealer should hit or stay. See the code archive for details on how this is all put together.


Card counter

When you're playing Blackjack, or trump-based games like Hearts, it can be useful to know how to count cards. In the case of Blackjack, you at least want to know how many aces, and face cards you have seen. For Hearts, you may want to know how many trump cards are still in play. You'll be building a rudimentary counter for Blackjack. You can take the same principles to build a card counter for a trump game.

You will build the deck for counting a bit differently. You don't need to know all the cards — just if they are cards you care about. For a single-deck shoe, it would look like that shown below.

Listing 11. Building a card counter for a single-deck shoe
$deck = array (
    'faces' => 16, // 10,J,Q,K * 4 suits
    'aces' => 4, // One per suit
    'other' => 32, // 52 - (16 + 4)
);

Next, build a simple form with three buttons — one for face cards, one for aces and one for other cards. By clicking the appropriate button each time you see a card of a specific type, you can adjust what's left in the deck.

Listing 12. A simple form with three buttons
if ($_POST['submit'] == 'faces') {
    $deck['faces']--;
}

Then you can calculate the odds of pulling a face, an ace, or a different card by looking at the deck after it's been modified.

Listing 13. Calculating the odds of pulling a face, an ace or different card
echo "Odds of pulling a face: " . $deck['faces'] . " out of " 
. $deck['faces'] + $deck['aces'] + $deck['other'];

Using this rudimentary card counter as a starting point, you can build something much more robust.


Bingo engine

Based on what we've done in this article, you could easily put together a Bingo engine to generate cards and draw numbers from a pool.

Bingo numbers run from 1 to 75, broken up into groups of 15 (1-15 assigned to B, 16-30 assigned to I, etc.). You can build and shuffle a set of virtual Bingo balls in much the same way you build and shuffle a virtual deck of cards. But you don't need to track faces and suits in quite the same way. First, build the base set of numbers.

Listing 14. Building the base set of numbers for Bingo
for ($i = 1; $i < 16; $i++) {
    $numbers['B'][] = $i;
    $numbers['I'][] = $i+15;
    $numbers['N'][] = $i+30;
    $numbers['G'][] = $i+45;
    $numbers['O'][] = $i+60;
}

Next, you can shuffle each subarray and make cards at the same time. (Don't forget: The very middle space is usually a free spot.) While you're there, create one master array you can use like a deck.

Listing 15. Creating a master array
$letters = array ('B','I','N','G','O');
foreach ($letters as $letter) {
    shuffle($numbers[$letter]);
    $chunks = array_chunk($numbers[$letter], 5);
    $cards[$i][$letter] = $chunks[0];
    if ($letter == 'N') {
        $cards[$i][$letter][2] = '  '; // Free Space
    }
    shuffle($balls);
}

Now you're all set! Hand out your cards, and start drawing virtual Bingo balls from the master set, much like you did when you were drawing cards earlier. A fully functioning version of this script can be found in the code archive.


Jumble helper

The Jumble is that word puzzle found in most newspapers where you have a few words that have been all mixed up. You have to make real words out of the nonsense, then use certain letters to make a new phrase. Starting with the crossword helper created in Part 1, let's put together a quick script to help you with your Jumble.

To start, open up your word list. You want to iterate through the list, breaking each word into a sorted array of letters, then joining the array again. For example, this would change the word "COLOR" into the string "CLOOR"; this string represents all the letters from the word "COLOR," but in a predictable (and more importantly, matchable) order. This string is then used as the key for an array of matching words. It needs to be an array because "sword" and "words" have the same exact letters in different orders. In the end, it looks something like that shown below.

Listing 16. Script for a Jumble
foreach ($words as $word) {
    $arr = str_split($word);
    sort($arr);
    $key = implode('', $arr);
    if (isset($lookup[$key])) {
        array_push($lookup[$key], $word);
    } else {
        $lookup[$key] = array($word);
    }
}

Once you've gone this far, all you need to do is treat your Jumble just like any other word, look up the value for the corresponding key and output any results.

Listing 17. Finishing the Jumble script
$arr = str_split($_POST['jumble']);
sort($arr);
$search = implode('', $arr);

if (isset($lookup[$search])) {
    foreach ($lookup[$search] as $word) {
        echo $word;
    }
}

You can see how this is all drawn together in the code archive.


Substitution cyphers

A substitution cypher is a simple encryption technique where every letter of the alphabet is substituted with another letter from the alphabet. By the standards of modern-day encryption techniques, substitution cyphers are child's play. But simple substitution cyphers can be fun to play with, especially in an RPG context by handing someone an encoded message that they can work out during game play.

Start by stealing some code from the hangman generator from Part 1— namely, the letter array. Make a second copy of the array, shuffle the copy, and build an associative array, where the original letter gets a shuffled value.

Listing 18. Modifying the letter array
$letters = array('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');
$code = $letters;
shuffle($code);
$key = array_combine($letters, $code);

Next, take input from $_POST, break the message up into letters, and output the value of each letter in the key.

Listing 19. Splitting and outputting letters
if (!empty($_POST)) {

    $messageletters = str_split(strtolower($_POST['message']));
    $show = '';
    foreach ($messageletters as $letter) {
        $show .= @$key[$letter];
    }
}

Using the @ symbol before $key[$letter] tells PHP to ignore any errors that occur during encoding. If the message has punctuation, numbers or spaces, PHP will complain that there is no corresponding value in the $key array. That's just fine. The resultant encoded text will be stripped of spaces, numbers and punctuation, making it (marginally) more difficult to decode.


Word-search generator

Word search is probably the earliest letter-based puzzle we all learned. Using the techniques already put to use, and including some logic from the crossword helper, you can make a pretty basic word-search generator. To keep things simple, you'll make a word search that is 10x10 letters in size, with 10 words hidden inside (five vertical and five horizontal).

The first part, putting in the horizontal words, is pretty easy — build an array of arrays, 10 elements each, filled with periods. Then in every other array, push in the letters for individual words. To put in the vertical words, you need to walk all the arrays simultaneously, using the crossword helper code to look for possible matching words. Perhaps the trickiest bit is the piece shown in Listing 20 that fills in words or random letters on vertical lines, where the word is shorter than the whole line.

Listing 20. Filling in words or random letters on vertical lines
$wordletters = str_split($word);
for ($c = 0; $c < $height; $c++) {
    if ( $grid[$c][$i] == '.' && ($c < $x || empty($wordletters))) {
        $grid[$c][$i] = $letters[rand(0,25)];
    } else  {
        if ($grid[$c][$i] == '.') {
            $grid[$c][$i] = array_shift($wordletters);
        } else if ($c >= $x) {
            array_shift($wordletters);
        }
    }
}

The code archive contains a number of comments to help elucidate this whole process. There's plenty of room for performance improvement in that code, but this should give you a general idea for how to approach the problem. Try out the full script from the code archive to see the results.


Summary

This article shows how to take some of the basic scripts written in Part 1 of this "30 game scripts you can write in PHP" series and build on them to provide more robust scripts. In Part 3, you go even further by building on what you've done here to offer more robust and useful tools.


Download

DescriptionNameSize
10 intermediate PHP scriptsos-php-gamescripts2-php10games2.zip385KB

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=352374
ArticleTitle=30 game scripts you can write in PHP, Part 2: Developing 10 intermediate scripts
publish-date=11252008