30 game scripts you can write in PHP, Part 1

Creating 10 fundamental scripts

Content series: This content is part # of # in the series: 30 game scripts you can write in PHP, Part 1 Stay tuned for additional content in this series. This content is part of the series: 30 game scripts you can write in PHP, Part 1 Stay tuned for additional content in this series.

As both a game master/storyteller and a developer, I frequently find myself writing little utilities and scripts to help me when running, planning, and playing games. Sometimes I need a quick idea. Other times, I just need a whole pile of names for Non-Player Characters (NPCs). Occasionally, I need to geek out on numbers, work out some odds, or integrate some word puzzles into a game. Many of these tasks become more manageable with a little bit of script work ahead of time.

This article will explore 10 fundamental scripts that can be used in various types of games. The code archive contains the full source for each script we will discuss.

Learn more. Develop more. Connect more. The new developerWorks Premium membership program provides an all-access pass to powerful development tools and resources, including 500 top technical titles (dozens specifically for open source developers) through Safari Books Online, deep discounts on premier developer events, video replays of recent O'Reilly conferences, and more. Sign up today.

We will blaze through these scripts pretty quickly. The topic of finding a host or setting up a server will not be covered. There are many Web hosting companies 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. These scripts are designed to be simple to understand, simple to use, and quick to pick up.

A basic die roller

Many games and game systems need dice. Let's start with something simple: rolling a single six-sided die. Essentially, there's no difference between rolling a six-sided die and picking a random number between 1 and 6. In PHP, this is simple: echo rand(1,6); .

In many cases, that would be more or less fine. But when we deal with games of chance, we want something a little better. PHP provides a better random number generator: mt_rand() . Without going into detail on the differences between the two, it is safe to assume that mt_rand is a faster and better random number generator: echo mt_rand(1,6); . We'll be happier overall if we put this in a function.

Listing 1. Using the mt_rand() random number-generator function

function roll () { return mt_rand(1,6); } echo roll();

Then we can pass the type of die we want to roll as a parameter to the function.

Listing 2. Passing the type of die as a parameter

function roll ($sides) { return mt_rand(1,$sides); } echo roll(6); // roll a six-sided die echo roll(10); // roll a ten-sided die echo roll(20); // roll a twenty-sided die

From here, we can go on to rolling multiple die at once, returning an array of results, or rolling multiple die of different kinds all at once, depending on our needs. But this simple script can be used for most tasks.

Random name generator

If you're running games, writing stories, or creating a bunch of characters all at once, sometimes it's hard to keep coming up with new names. Let's look at a simple random name generator you can use to solve this problem. Starting off, let's make two simple arrays — one with first names, one with last names.

Listing 3. Two simple arrays of first and last names

$male = array( "William", "Henry", "Filbert", "John", "Pat", ); $last = array( "Smith", "Jones", "Winkler", "Cooper", "Cline", );

Then we can just pick a random element from each array: echo $male[array_rand($male)] . ' ' . $last[array_rand($last)]; . To pull a bunch of names at once, we can simply shuffle the arrays and pull as many as we like.

Listing 4. Shuffling the name arrays

shuffle($male); shuffle($last); for ($i = 0; $i <= 3; $i++) { echo $male[$i] . ' ' . $last[$i]; }

Taking this basic concept, we can create text files to hold our first and last names. If we put one name per line in our text file, we can easily split the file contents on the newline character to build our source arrays.

Listing 5. Creating text files for our names

$male = explode('

', file_get_contents('names.female.txt')); $last = explode('

', file_get_contents('names.last.txt'));

Build or find some good name files (a couple are included in the code archive), and we will never want for names again.

Scenario generator

Taking the same basic principles we used to make the name generator, we can make what's called a scenario generator. This is useful in role-playing games or other situations where we need to come up with a pseudo-random set of circumstances that can be used for role-play, improvisation, writing, etc. One of my favorite games, Paranoia, includes something called a "mission blender" in its GM Pack. The mission blender can be used to put together a full mission at the quick roll of a die. Let's put together our own scenario generator.

Take the following scenario: You wake up lost in the woods. You know you have to get to New York, but you don't know why. You can hear the barking of dogs and the unmistakable sound of hostile searchers nearby. You are cold, shivering, and without a weapon. Each sentence in that scenario introduces a specific aspect of the scenario:

"You wake up lost in the woods" — This establishes the setting.

"You know you have to get to New York" — This describes an objective.

"You can hear the barking of dogs" — This introduces an antagonist.

"You are cold, shivering, and without a weapon" — This adds a complication.

Just like we created text files for our first and last names, start by making a text file each for settings, objectives, antagonists, and complications. Sample files are included in the code archive. Once we have these files, the code to generate a scenario looks much the same as the code to generate names.

Listing 6. Generating a scenario

$settings = explode("

", file_get_contents('scenario.settings.txt')); $objectives = explode("

", file_get_contents('scenario.objectives.txt')); $antagonists = explode("

", file_get_contents('scenario.antagonists.txt')); $complications = explode("

", file_get_contents('scenario.complications.txt')); shuffle($settings); shuffle($objectives); shuffle($antagonists); shuffle($complications); echo $settings[0] . ' ' . $objectives[0] . ' ' . $antagonists[0] . ' ' . $complications[0] . "<br />

";

We can add elements to our scenarios by adding new text files, or we may wish to add multiple complications. The more we add to our base text files, the more varied our scenarios will be over time.

Deck builder and shuffler

If you play cards and are interested in working on any scripts that are card-related, we want to put together a deck builder with a built in shuffler. To start, let's build a basic deck of standard playing cards. We need to build up two arrays — one to hold suits and one to hold faces. This will allow flexibility later if we want to add new suits or card types.

Listing 7. Building a basic deck of playing cards

$suits = array ( "Spades", "Hearts", "Clubs", "Diamonds" ); $faces = array ( "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King", "Ace" );

Then build a deck array to hold all the card values. We can do this simply using a pair of foreach loops.

Listing 8. Building a deck array

$deck = array(); foreach ($suits as $suit) { foreach ($faces as $face) { $deck[] = array ("face"=>$face, "suit"=>$suit); } }

Once we have a deck array built, we can easily shuffle the deck and draw a random card.

Listing 9. Shuffling the deck and drawing a random card

shuffle($deck); $card = array_shift($deck); echo $card['face'] . ' of ' . $card['suit'];

From here, it's a short path to drawing hands of a set number of cards or building a multideck shoe.

Odds calculator: Card draw

Because we built the deck the way we did, keeping track of the face and suit individually for each card, we can make use of the deck programmatically to do something like calculate the odds of getting a specific card. Start by drawing out two hands of five cards each.

Listing 10. Drawing two hands of five cards each

$hands = array(1 => array(), 2=>array()); for ($i = 0; $i < 5; $i++) { $hands[1][] = implode(" of ", array_shift($deck)); $hands[2][] = implode(" of ", array_shift($deck)); }

Then we can look in the deck to see how many cards are left and what the odds are of drawing a specific card. How many cards are left is an easy one. That's just a count of how many elements there are in the $deck array. To get the odds of drawing a specific card, we need a function to walk through the whole deck and evaluate the remaining cards to see if they match.

Listing 11. Calculating the odds of drawing specific card

function calculate_odds($draw, $deck) { $remaining = count($deck); $odds = 0; foreach ($deck as $card) { if ( ($draw['face'] == $card['face'] && $draw['suit'] == $card['suit'] ) || ($draw['face'] == '' && $draw['suit'] == $card['suit'] ) || ($draw['face'] == $card['face'] && $draw['suit'] == '' ) ) { $odds++; } } return $odds . ' in ' $remaining; }

Now we can pick the card we are trying to draw. To make this easy, pass in an array that looks just like a card. We can look for a specific card.

Listing 12. Looking for a specific card

$draw = array('face' => 'Ace', 'suit' => 'Spades'); echo implode(" of ", $draw) . ' : ' . calculate_odds($draw, $deck);

Or we can look for a card of a given face or suit.

Listing 13. Looking for a card of a given face or suit

$draw = array('face' => '', 'suit' => 'Spades'); $draw = array('face' => 'Ace', 'suit' => '');

Simple poker dealer

Now that we've got a deck builder and something to help work out the odds of drawing specific cards, we can put together a really simple dealer to practice poker hands. For the purpose of this example, we build a dealer for five-card draw. The dealer will provide five cards from the deck. You specify which cards you want to discard by number, and the dealer will replace these cards with fresh ones from the deck. We won't bother putting in draw limits or house rules, though you may find that a rewarding personal exercise.

As shown in the previous section, generate and shuffle a deck, then create a single hand of five cards. Display these cards by their array index so that you can specify which cards to return. You might do it by using checkboxes to indicate which cards you are replacing.

Listing 14. Using checkboxes to indicate cards you are replacing

foreach ($hand as $index =>$card) { echo "<input type='checkbox' name='card[" . $index . "]'> " . $card['face'] . ' of ' . $card['suit'] . "<br />"; }

Then, evaluate the input array $_POST['card'] to see which cards have been checked for replacement.

Listing 15. Evaluating the input

$i = 0; while ($i < 5) { if (isset($_POST['card'][$i])) { $hand[$i] = array_shift($deck); } }

Using this script, you can try your hand — pun intended — at figuring out the best ways to handle a specific set of cards.

Hangman player

Hangman is essentially a word-guessing game. Given a word of a certain length, we have a limited number of letter guesses. If you guess a letter that appears in the word correctly, all occurrences of the letter are filled in. After a set number of wrong guesses (typically six), you've lost the game. To put together a crude game of hangman, we need to start with a word list. For now, let's make it a simple array.

Listing 16. Creating a word list

$words = array ( "giants", "triangle", "particle", "birdhouse", "minimum", "flood" );

Using techniques covered earlier, we can move these words to an external word list text file and import them as we like.

Once we've got a list of words, we need to pick one out at random, display a blank for each letter, and start taking guesses. We need to keep track of right and wrong guesses from guess to guess. We'll do this cheaply by just serializing the guess arrays and passing them along with each guess. If we wanted to keep people from cheating by viewing the page source, we'd want to do something a little more secure.

Build up arrays to hold our letters, and our right/wrong guesses. For right guesses, we'll fill an array with the letters as keys and periods as values.

Listing 17. Building arrays to hold letters and guesses

$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'); $right = array_fill_keys($letters, '.'); $wrong = array();

Now we need a little but of code to evaluate guesses and show the word as it progresses through the guessing game.

Listing 18. Evaluating guesses and displaying progress

if (stristr($word, $guess)) { $show = ''; $right[$guess] = $guess; $wordletters = str_split($word); foreach ($wordletters as $letter) { $show .= $right[$letter]; } } else { $show = ''; $wrong[$guess] = $guess; if (count($wrong) == 6) { $show = $word; } else { foreach ($wordletters as $letter) { $show .= $right[$letter]; } } }

In the source archive, we can see how we serialize the guess arrays and pass them from guess to guess.

Crossword helper

I know it's bad form, but sometimes when you're working on a crossword puzzle, you just get stuck trying to find a five-letter word that starts with C and ends with T. Using the same word list we put together for Hangman, we can easily search for words that fit a certain pattern. First, establish a way to pass in words. To make this easy, replace missing letters with periods: $guess = "c...t"; . Since regular expressions treat a period as a single character, we can easily walk the word list, looking for matches.

Listing 19. Walking the word list

foreach ($words as $word) { if (preg_match("/^" . $_POST['guess'] . "$/",$word)) { echo $word . "<br />

"; } }

Depending on the quality of our word list and the accuracy of our guess, we should be able to get a reasonable list of words to use for possible matches. You'll have to decide yourself if "chest" or "cheat" is a better match for "a five-letter word that means 'to not play by the rules.'"

Mad Libber

Mad Libs is a word game where the player takes a short story and replace key types of words with different words of the same type to create a new, sillier version of the same story. Take the following text: "I was walking in the park when I found a lake. I jumped in and swallowed too much water. I had to go to the hospital." Start by replacing the word types with different word tokens. Start and end each token with an underscore to prevent accidental string matches.

Listing 20. Replacing word types with word tokens

$text = "I was _VERB_ing in the _PLACE_ when I found a _NOUN_. I _VERB_ed in, and _VERB_ed too much _NOUN_. I had to go to the _PLACE_.";

Next, create a couple basic word lists. For this example, we won't get too fancy.

Listing 21. Creating a couple of basic word lists

$verbs = array('pump', 'jump', 'walk', 'swallow', 'crawl', 'wail', 'roll'); $places = array('park', 'hospital', 'arctic', 'ocean', 'grocery', 'basement', 'attic', 'sewer'); $nouns = array('water', 'lake', 'spit', 'foot', 'worm', 'dirt', 'river', 'wankel rotary engine');

Now we can repeatedly evaluate the text to replace the tokens as needed.

Listing 22. Evaluating the text

while (preg_match("/(_VERB_)|(_PLACE_)|(_NOUN_)/", $text, $matches)) { switch ($matches[0]) { case '_VERB_' : shuffle($verbs); $text = preg_replace($matches[0], current($verbs), $text, 1); break; case '_PLACE_' : shuffle($places); $text = preg_replace($matches[0], current($places), $text, 1); break; case '_NOUN_' : shuffle($nouns); $text = preg_replace($matches[0], current($nouns), $text, 1); break; } } echo $text;

Obviously, this is a simple and crude example. The more precise our word lists, and the more time we put into our base text, the better our results will be. We've already used text files to create lists of names and basic word lists. Using the same principle, we can create lists of words broken up by type and use them to create more diverse Mad Libs.

Lotto picker

Picking the right six numbers in a lotto is, to say the least, statistically improbable. Nevertheless, many people still pay to play, and if you like numbers, it can be entertaining to see the trends. Let's throw together a script that lets us keep track of winning numbers and provides the six least-picked numbers in our list.

(Disclaimer: This will not help you win the lotto, so please don't spend your money on tickets. This is just for fun.)

We save winning lotto picks in a text file. We separate individual numbers by comma and put each set of numbers on its own line. When we get the file contents, split on newlines, and split each line on commas, we get something that looks like Listing 23.

Listing 23. Saving winning lotto picks in a text file

$picks = array( array('6', '10', '18', '21', '34', '40'), array('2', '8', '13', '22', '30', '39'), array('3', '9', '14', '25', '31', '35'), array('11', '12', '16', '24', '36', '37'), array('4', '7', '17', '26', '32', '33') );

Obviously, that's not much of a base file for drawing stats. But it's a start, and enough to illustrate the principles.

Set a base array to hold the pick range. For example, if we pick numbers from 1 to 40 (e.g., $numbers = array_fill(1,40,0); , then walk through our picks, incrementing appropriate matching values.

Listing 24. Walking through our picks

foreach ($picks as $pick) { foreach ($pick as $number) { $numbers[$number]++; } }

Finally, sort the numbers based on value. This should put the least-picked numbers at the front of the array.

Listing 25. Sorting the numbers based on value

asort($numbers); $pick = array_slice($numbers,0,6,true); echo implode(',', array_keys($pick));

By regularly adding actual lotto picks to the text file containing our list of picks, we can trend number-picking over the long term. It's interesting to see how often some numbers appear.

Summary

This article is a resource for you to get a jump into using PHP to help enrich your gaming experience. In Part 2 of this "30 game scripts you can write in PHP" series, we build on the code found here and go into more involved scripts that can give you even more benefit.

Sign up for developerWorks Premium

Downloadable resources

Related topics