Run Multiple Tic-Tac-Toe Game Instances (no database required)

Someone asked me about this in a comment recently so this post is specifically for you but I’m sure there are other people who will benefit from it as well. If you haven’t read the tic-tac-toe game tutorial yet then you’ll need to start there. This tutorial assumes you’ve already completed that and now you want to run two or more tic-tac-toe boards on the page simultaneously.

Or if you want to skip this tutorial you can try the working example and

Concepts

The neat thing about classes is that you can use them to create multiple instances with varying states and data even though they all have the same methods and properties. Think about a monster RPG game. Each monster has a different name and breed and picture and strength — but they all have those things in common even though the values are different. Our tic tac toe class works the same way, we can create multiple instances of the game and they’ll each have different states — whose turn it is, which places on the board are filled — but they’ll all work the same way. In order to run multiple instances of our tic tac toe games we’re going to have to update our games so they can be uniquely identified by an instance number. The reason for this is so that when you play the game on instance 1 we know you want to update the state of instance 1 when you submit the form. If we didn’t give each game it’s own instance identifier then every time you made a move on one board it would reflect that move on all instances of the board at the same time.

Creating The Instance Form

Let’s change our index file so that we’re allowing the user to choose how many instances of the game they want to play. We start by removing where we created a new game from the top of the file. Since we want multiple instances we need to create the new game for every instance we have and since we don’t know how many instances we have at this point it needs to move down in our code base. Now once the user has selected how many instances of the game they want to play we need to dynamically generate that many. Using a for loop we loop through the number of instances they selected and create a game for each of those instances in our new $_SESSION[‘game’] array. Before this was only a single game, making it an array means we now have multiple games in our game session variable. Finally we create a new game instance and tell it to start playing. This is what our new index file looks like.

 0) {
 $_SESSION['instances'] = $_POST['instances'];
 }
}

?>

 
 Tic Tac Toe - Multiple Instances
 
 
 
 

Let's Play Tic Tac Toe!

How many games would you like to instantiate?

"; for ($i = 1; $i <= $_SESSION['instances']; $i++) { //if they haven't started a game yet let's load one if (!isset($_SESSION['game'][$i]['tictactoe'])) { $_SESSION['game'][$i]['tictactoe'] = new tictactoe($i); } echo ""; //play the game passing it the game data for that instance $_SESSION['game'][$i]['tictactoe']->playGame($_POST); echo ""; } echo " "; } ?>

Adding Instances To The Class

The first thing we need to do is add an instance identifier to our tictactoe class. Since we need to know which game the player is trying to play adding a $this->instance value to our class will let us keep track of which game is which. Once that’s done we update our checks for $_POST data to make sure we’re using the correct data for the correct instance. Voila! Our games now only update when they see $_POST data that pertains to them. Our new tictactoe class file looks like this:

instance = $instance;
 game::start();
 
 $this->newBoard();
 }
 
 /**
 * Purpose: start a new tic tac toe game
 * Preconditions: none
 * Postconditions: game is ready to be displayed
 **/
 function newGame()
 {
 //setup the game
 $this->start();
 
 //reset the player
 $this->player = "X";
 $this->totalMoves = 0;
 
 //reset the board
 $this->newBoard();
 }
 
 function newBoard() {
 
 //clear out the board
 $this->board = array();
 
 //create the board
 for ($x = 0; $x <= 2; $x++)
 {
 for ($y = 0; $y <= 2; $y++)
 {
 $this->board[$x][$y] = null;
 }
 }
 }
 
 /**
 * Purpose: run the game until it's tied or someone has won
 * Preconditions: all $_POST content for this game
 * Postconditions: game is in play
 **/
 function playGame($gamedata)
 {
 if (!$this->isOver() && isset($gamedata[$this->instance . 'move'])) {
 $this->move($gamedata);
 }
 
 //player pressed the button to start a new game
 if (isset($gamedata[$this->instance . '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 "
"; for ($x = 0; $x < 3; $x++) { for ($y = 0; $y < 3; $y++) { echo "
"; //check to see if that position is already filled if ($this->board[$x][$y]) echo "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 ""; } echo "
"; } echo "
"; } echo "

instance}move\" value=\"Take Turn\" />
It's player {$this->player}'s turn.

"; } 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?"); echo "

instance}newgame\" value=\"New Game\" />

"; } } /** * 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($gamedata) { if ($this->isOver()) return; //remove duplicate entries on the board $gamedata = array_unique($gamedata); foreach ($gamedata as $key => $value) { if ($value == $this->player) { //update the board in that position with the player's X or O $coords = explode("_", $key); //make sure we use the data from the right instance if ($coords[0] == $this->instance) { $this->board[$coords[1]][$coords[2]] = $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"; } }

Conclusion

Try the working example! In this tutorial we talked about instances and how you can use a class to dynamically create multiple instances each of which keep track of their own state and values. If you’ve been following my game tutorials then I hope you’re starting to see how powerful classes are and how iterative improvements can be used to enhance your gameplay, functionality and user experience.

You may also like...

Leave a Reply