Obey the State!
When you hear the term State Machine, you may think of totalitarian societies as depicted in George Orwell’s 1984 run by ruthless dictators. However, in programming, state machines are quite benign and helpful in solving certain kinds of programming problems. Chapter 10 of Learning PHP Design Patterns, explains the State design pattern in full detail. In this post I want to introduce the concept of a state machine, but we will not see a true State design pattern just yet. Here, we’ll just look at a state machine and how it can be used in OOP programming in general. As you will see, we can use a state machine in standard OOP programming without a full State design pattern. In Part III, we’ll examine a full State Design Pattern.
Generally speaking in programming, a state is the current value of a variable. A state machine (or finite state machine) is a system where each state waits for a transition to another state. (In Learning PHP Design Patterns, I used the example of a light having states of “on” or “off,” with the transition through the trigger of a light switch.) In the game of Rock, paper, scissors, lizard, Spock (R-P-S-L-S), each of the five gestures represents a state, and the transition occurs each time the players throw a gesture. (See PHP Game Making Part I.) To get started, play the revised version of the game and download the files for the State Machine version of the game:
A State Class
To get started, begin with an interface that encompasses all of the different states (moves) in R-P-S-L-S. The following listing, IPlayer, has methods for each move:
interface IPlayer
{
public function rockMove();
public function spockMove();
public function paperMove();
public function lizardMove();
public function scissorsMove();
}
?>
|
Note that all of the methods are public. This allows access to them through different implementations.
What we want to do with each method is to generate outcomes for all possible moves. Given that a player (human) is pitted against a computer that randomly makes moves, each state class with have outcomes for each of the methods based on the combination of what the player has done and what the computer will do. Take, for example, the Rock class. Each of the
class Rock implements IPlayer
{
public function rockMove()
{
return "Tie";
}
public function spockMove()
{
return "Computer wins!";
}
public function paperMove()
{
return "Computer wins!";
}
public function lizardMove()
{
return "Player wins!";
}
public function scissorsMove()
{
return "Player wins!";
}
}
?>
|
Essentially, you have self-aware state classes. For example, the Rock class is aware that if the opposition makes a Rock move, the result is a tie. Likewise, if the opposition chooses either a Lizard or Scissors move, Rock wins; but if the opposition makes either Paper or Spock moves, Rock loses. There are no conditional statements. That’s important as you will see when we move on the the State design pattern in Part III (or you saw in Chapter 10 of Learning PHP Design Patterns.
The State Machine
To understand a State Machine, it helps to use a statechart. A statechart identifies the different states in a system and the triggers that transition from one state to another. In the case of an R-P-S-L-S) finite state system, you have a triggering state (chant->throw) and the five individual states. Figure 1 shows the five states and the triggering state. Note that all changes from one state to another go through the trigger and none directly to another. (e.g., A Lizard state cannot go directly to a Paper state; it must go through the trigger.)

Figure 1: Statechart of the RPSLS State Machine
Importantly, the State Machine represents what actually happens in a game of R-P-S-L-S. Players enter the “chant” trigger and then throw a gesture. So we may actually refer to the RPSLS as a “state” used to transition between the five outcome states. The task now, is to implement these states. (Click below to continue.)
Rewind the UI, Client and Computer Classes and the Variable Variable
Now that the emphasis has switched from an algorithm in modular arithmetic (that didn’t work anyway with PHP) to a state machine, we’ll need to rethink the player UI. Instead of passing a value of 0-4, we’ll pass the name of the state class from the HTML form:
|
As you can see, the only change we had to make was to the value attributes. However, that sets up a very important system that we can use with the state machine. Now, the player’s choice on the form is sent to the Client class. So let’s look at the changes in the Client:
//Client.php
function __autoload($class_name)
{
include $class_name . '.php';
}
class Client
{
private $move;
private $outcome;
private $playerClass;
public function __construct()
{
if(isset($_POST['go']))
{
$this->move=$_POST['move'];
echo "Player move: $this->move
|
As you can see, the requests in the client are quite different. It now uses the $_POST[‘move’] sent from the HTML form to create an instance of the five state classes in the line:
$this->playerClass = new $this->move();
The class instance is based on the actual string named in the value attribute of the HTML form passed. For example, if the player chooses “Rock” the value “Rock” is sent to the client. Using what is called a variable variable, the string name is used as the class name. (Chapter 6, page 117 under “Dynamic Object Instantiation” in Learning PHP Design Patterns has more details on this process.) In effect, we can pass object from HTML to PHP.
Next, the Client creates an instance of Computer object, and then makes a call to the Computer class method, computerMakeMove(). However, it now places an instance of the player’s move in the request. To see what’s going on, take a look at the revised Computer class.
//Computer.php
class Computer
{
private $throw;
private $computerMove;
private $playerMove;
public function computerMakeMove(IPlayer $player)
{
$this->playerMove=$player;
$this->throw=rand(0,4);
switch($this->throw)
{
case 0:
echo "Computer move: Rock
|
The computerMakeMove() method uses type hinting that forces an implementation of IPlayer to be used as an argument. All of the five moves that can be made and passed through HTML are PHP classes. Each class represents a state with the consequences of the other player’s move in its methods.
Type hinting is one way that PHP can be programmed to the interface and not the implementation; an essential design pattern principle.
So now, the RPSLS selection made in the HTML form is passed as an argment in the computer’s move. By calling the computer’s move as a method of the player’s move, the correct outcome will always be displayed. For example, if the player (human) selects “Lizard”, the Client passes that move as an implementation of the Lizard class to the computerMakeMove(“Lizard.instance”) method. When the Computer object randomly selects a move, it uses the Lizard class object to invoke the appropriate move by the Computer expressed as one of the five methods from the IPlayer interface. So if a “2” is randomly generated, the literal call is Lizard->paperMove(). If we look at what happens when the players “throws” a Lizard and the computer chooses Paper (paperMove()), we find that the player wins:
//Snippet from Lizard class public function paperMove() { return "Player wins!"; } |
While this may seem a bit complex at first, thinking in terms of a state machine actually does help in conceptualizing certain problems—especially in game programming where the states are always changing. Rather than having to use a whole complex mess of conditional statements to sort out what happens next, the state machine shows how classes set up as states can always have the responses (or outcomes) depending on the state or interacting state.
What About Remote Human vs. Human?
The R-P-S-L-S game is simple and is wholly dependent on chance; however, it can serve as a prototype game for ones where simultaneous movement is made. Against a random computer move is relatively easy because the computer won’t cheat (it can’t!). When two remote players get together, we’re in for a different story.
In the next installment (Part III of PHP Game Making), we’ll see what a full State design pattern looks like in a game context. If you want to get a preview, see Chapter 10 in Learning PHP Design Patterns. The main feature of a true State design pattern (in addition to the state classes) is the Context class. This class keeps track of a current state, and we’ll see how we might be able to use it with the R-P-S-L-S game.
Copyright © 2013 William Sanders. All Rights Reserved.
0 Responses to “PHP Game Making Part II: The State Machine”