Build a Multiplayer Tic Tac Toe Game in React
Tags: react, tictactoe, pubnub
Published: 2019-08-05 18:40:36

---
canonical_url: https://www.pubnub.com/blog/build-a-multiplayer-tic-tac-toe-game-in-react/
---

Tic tac toe is a quintessential childhood game. All it requires is something to write on and something to write with. But what if you want to play with someone that's in another location? In this case, you would need to use an application that connects you and another player to the game. 

The application needs to provide a realtime experience so every move you make is seen instantly by the other player and vice versa. If the application does not provide this experience, then you and many people, won't probably use it anymore. 

So how does a developer provide a [<span style="font-weight: 400;">connected experience</span>](https://www.pubnub.com/blog/connected-shared-experiences-a-developers-guide/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q3-DevTo-August-5) where the players can play tic tac toe, or any game, no matter where they are in the world?

## Realtime Multiplayer Game Concepts

There are several ways to provide the realtime infrastructure for multiplayer games. You can go the route of [building your own infrastructure](https://www.mobileappdaily.com/why-scaling-is-challenge-for-developing-realtime-features-app) from the ground up by using technologies and open-source protocols like [Socket.IO](https://www.pubnub.com/learn/glossary/what-is-socketio/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q3-DevTo-August-5), [SignalR](https://www.pubnub.com/learn/glossary/what-is-signalr/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q3-DevTo-August-5), or [WebSockets](https://www.pubnub.com/learn/glossary/what-is-websocket/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q3-DevTo-August-5). 

While this may seem like an appealing route to take, you will encounter several issues; one such issue being [scalability](https://www.mobileappdaily.com/why-scaling-is-challenge-for-developing-realtime-features-app). It's not hard to handle 100 users, but how do you handle 100,000+ users? Besides infrastructure issues, you still have to worry about maintaining your game. 

At the end of the day, the only thing that matters is providing a great experience for the players of your game. But how do you do solve the infrastructure problem? This is where PubNub comes in. 

PubNub provides the realtime infrastructure to power any application through its global [Data Stream Network](https://www.pubnub.com/products/global-data-stream-network/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q3-DevTo-August-5).  With over [70+ SDKs,](https://www.pubnub.com/docs/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q3-DevTo-August-5) including the most popular programming languages, PubNub simplifies sending and receiving messages to any device in **under 100 ms**. It is secure, scalable and reliable so you don't have to worry about creating and maintaining your own infrastructure. 

In order to show how easy it is to develop a multiplayer game using PubNub, we will build a simple React tic tac toe game using the [PubNub React SDK](https://www.pubnub.com/docs/react-native-javascript/pubnub-javascript-sdk/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q3-DevTo-August-5). In this game, two players will connect to a unique game channel where they will play against each other. Every move a player makes will be published to the channel to update the other player's board in realtime.

You can check out the complete project in the [GitHub repository](https://github.com/ocastroa/react-tictactoe).

## App Overview

Here is what our app will look like once we finish. [Click here to try out our live version of the game](https://ocastroa.github.io/react-tictactoe/). 

<img class="size-full wp-image-44294 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/React-tic-tac-toe-overview.png" alt="Screen shot of the React Tic Tac Toe Game" width="757" height="252" />

Players first join the lobby where they can create a channel or join a channel. If the player creates a channel, they get a _room id_ to share with another player. The player that created the channel becomes _Player X_ and will make the first move when the game starts. 

<img class=" wp-image-44295 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/create-room.gif" alt="Create a room channel" width="408" height="333" />

The player that joins a channel with the _room id_ they were given becomes _Player O_. Players can only join channels when there is one other person in the channel. If there is more than one person, then a game is in progress for that channel and the player won't be able to join. The game starts once there are two players in the channel. 

<img class="size-full wp-image-44296 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/join-channel.gif" alt="Join the room channel" width="408" height="333" />

At the end of the game, the winner's score is incremented by one point. If the game ends in a tie, then neither player is awarded a point. A modal is displayed to _Player X_ asking them to start a new round or to end the game. If _Player X_ continues the game, the board resets for the new round. Otherwise, the game ends and both players go back to the lobby. 

<img class="size-full wp-image-44297 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/exit-to-lobby-1.gif" alt="Exit to lobby" width="408" height="333" />

## Set Up the Lobby

Before we set up the lobby, sign up for a [free PubNub account](https://dashboard.pubnub.com/signup/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q3-DevTo-August-5). You can get your unique pub/sub keys in [PubNub Admin Dashboard](https://admin.pubnub.com/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q3-DevTo-August-5)

Once you get your keys, insert them into the constructor of **App.js**.

```javascript
// App.js
import React, { Component } from 'react';
import Game from './Game';
import Board from './Board';
import PubNubReact from 'pubnub-react';
import Swal from "sweetalert2";  
import shortid  from 'shortid';
import './Game.css';

class App extends Component {
  constructor(props) {  
    super(props);
    // REPLACE with your keys
    this.pubnub = new PubNubReact({
      publishKey: "YOUR_PUBLISH_KEY_HERE", 
      subscribeKey: "YOUR_SUBSCRIBE_KEY_HERE"    
    });

    this.state = {
      piece: '', // X or O
      isPlaying: false, // Set to true when 2 players are in a channel
      isRoomCreator: false,
      isDisabled: false,
      myTurn: false,
    };

    this.lobbyChannel = null; // Lobby channel
    this.gameChannel = null; // Game channel
    this.roomId = null; // Unique id when player creates a room   
    this.pubnub.init(this); // Initialize PubNub
  }  

  render() {
    return ();
    }
  }

  export default App;
```

Also in the constructor, the state objects and variables are initialized. We will go over the objects and the variables when they come up throughout the file. Finally, we initialized PubNub at the end of the constructor. 

Inside of the **render** method and inside the **return** statement, we add the markup for the Lobby component.

```html
return (  
    <div> 
      <div className="title">
        <p> React Tic Tac Toe </p>
      </div>

      {
        !this.state.isPlaying &&
        <div className="game">
          <div className="board">
            <Board
                squares={0}
                onClick={index => null}
              />  

            <div className="button-container">
              <button 
                className="create-button "
                disabled={this.state.isDisabled}
                onClick={(e) => this.onPressCreate()}
                > Create 
              </button>
              <button 
                className="join-button"
                onClick={(e) => this.onPressJoin()}
                > Join 
              </button>
            </div>                        

          </div>
        </div>
      }

      {
        this.state.isPlaying &&
        <Game 
          pubnub={this.pubnub}
          gameChannel={this.gameChannel} 
          piece={this.state.piece}
          isRoomCreator={this.state.isRoomCreator}
          myTurn={this.state.myTurn}
          xUsername={this.state.xUsername}
          oUsername={this.state.oUsername}
          endGame={this.endGame}
        />
      }
    </div>
);  
```

The Lobby component consists of a title, an empty tic tac toe board (nothing happens if the player presses the squares) and the '_Create_' and '_Join_' buttons. This component is displayed only if the state value _isPlaying_ is false. If it's set to true, then the game has begun and the component is changed to the Game component, which we will go over in the second part of the tutorial. 

The Board component is part of the Lobby component as well. Within the [Board](https://github.com/ocastroa/react-tictactoe/blob/master/src/Board.js) component is the [Square](https://github.com/ocastroa/react-tictactoe/blob/master/src/Square.js) component. We won't go into detail for these two components in order to focus on the Lobby and Game components. 

When the player presses the 'Create' button, the button is disabled so the player can't create multiple channels. The 'Join' button is not disabled, just in case the player decides to join a channel instead. Once the 'Create' button is pressed, the method **onPressCreate()** is called.

### Create a Channel

The first thing we do in **onPressCreate()** is generate a random string id that's truncated to 5 characters. We do so by using [**shortid()**](https://www.npmjs.com/package/shortid). We append the string to '_tictactoelobby--_', which will be the unique lobby channel players subscribe to.

```javascript
// Create a room channel
onPressCreate = (e) => {
  // Create a random name for the channel
  this.roomId = shortid.generate().substring(0,5);
  this.lobbyChannel = 'tictactoelobby--' + this.roomId; // Lobby channel name

  this.pubnub.subscribe({
    channels: [this.lobbyChannel],
    withPresence: true // Checks the number of people in the channel
  });
}
```

In order to prevent more than two players from joining a given channel, we use [PubNub Presence](https://www.pubnub.com/docs/react-native-javascript/presence/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q3-DevTo-August-5). Later on, we will look at the logic for checking the occupancy of the channel. 

Once the player subscribes to the lobby channel, a modal is displayed with the room id so another player can join that channel. 

<img class="size-full wp-image-44298 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/share-roomid-1.png" alt="Share the room id" width="311" height="197" />

This modal, and all the modals used in this app, are created by [**SweetAlert2**](https://sweetalert2.github.io/#download) to replace JavaScript's default alert() popup boxes.

```javascript
// Inside of onPressCreate()
// Modal
Swal.fire({
  position: 'top',
  allowOutsideClick: false,
  title: 'Share this room ID with your friend',
  text: this.roomId,
  width: 275,
  padding: '0.7em',
  // Custom CSS to change the size of the modal
  customClass: {
      heightAuto: false,
      title: 'title-class',
      popup: 'popup-class',
      confirmButton: 'button-class'
  }
})
```

At the end of **onPressCreate()**, we change the state values to reflect the new state of the app.

```javascript
this.setState({
  piece: 'X',
  isRoomCreator: true,
  isDisabled: true, // Disable the 'Create' button
  myTurn: true, // Player X makes the 1st move
});
```

Once the player creates a room, they have to wait for another player to join that room. Let's look at the logic for joining a room.

### Join a Channel

When a player presses the 'Join' button, a call to **onPressJoin()** is called. A modal is displayed to the player asking them to enter the _room id_ in the input field. 

<img class="size-full wp-image-44299 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/enter-roomid.png" alt="Enter the room id" width="311" height="197" />

If the player types in the _room id_ and presses the 'OK' button, then **joinRoom(value)** is called where **value** is the _room id_. This method is not called if the input field is empty or if the player presses the 'Cancel' button.

```javascript
// The 'Join' button was pressed
onPressJoin = (e) => {
  Swal.fire({
    position: 'top',
    input: 'text',
    allowOutsideClick: false,
    inputPlaceholder: 'Enter the room id',
    showCancelButton: true,
    confirmButtonColor: 'rgb(208,33,41)',
    confirmButtonText: 'OK',
    width: 275,
    padding: '0.7em',
    customClass: {
      heightAuto: false,
      popup: 'popup-class',
      confirmButton: 'join-button-class',
      cancelButton: 'join-button-class'
    } 
  }).then((result) => {
    // Check if the user typed a value in the input field
    if(result.value){
      this.joinRoom(result.value);
    }
  })
}
```

The first thing we do in **joinRoom()** is append **value** to  '_tictactoelobby--_', similar to what we did in **onPressCreate()**.

```javascript
// Join a room channel
joinRoom = (value) => {
  this.roomId = value;
  this.lobbyChannel = 'tictactoelobby--' + this.roomId;
}
```

Before the player subscribes to the lobby channel, we have to check the total occupancy of the channel by using [hereNow()](https://www.pubnub.com/docs/react-native-javascript/api-reference-presence#here_now/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q3-DevTo-August-5).  If the total occupancy is less than 2, the player can successfully subscribe to the lobby channel.

```javascript
// Check the number of people in the channel
this.pubnub.hereNow({
  channels: [this.lobbyChannel], 
}).then((response) => { 
    if(response.totalOccupancy < 2){
      this.pubnub.subscribe({
        channels: [this.lobbyChannel],
        withPresence: true
      });

      this.setState({
        piece: 'O', // Player O
      });  

      this.pubnub.publish({
        message: {
          notRoomCreator: true,
        },
        channel: this.lobbyChannel
      });
    } 
}).catch((error) => { 
  console.log(error);
});
```

After the player subscribes to the lobby channel, the state value of _piece_ is changed to 'O' and a message is published to that lobby channel. This message notifies the _Player X_ that another player has joined the channel. We set up the message listener in **componentDidUpdate()**, which we will get to shortly. 

If the total occupancy is greater than 2, then a game is in progress and the player attempting to join the channel will be denied access. The following code is below the if statement in **hereNow()**.

```javascript
// Below the if statement in hereNow()
else{
  // Game in progress
  Swal.fire({
    position: 'top',
    allowOutsideClick: false,
    title: 'Error',
    text: 'Game in progress. Try another room.',
    width: 275,
    padding: '0.7em',
    customClass: {
        heightAuto: false,
        title: 'title-class',
        popup: 'popup-class',
        confirmButton: 'button-class'
    }
  })
}
```

Let's now take a look at **componentDidUpdate()**.

### Start the Game

In **componentDidUpdate()**, we check if the player is connected to a channel, that is, check that _this.lobbyChannel_ is not **null**. If it's not **null**, we set up a listener that listens to all messages that arrive on the channel.

```javascript
componentDidUpdate() {
  // Check that the player is connected to a channel
  if(this.lobbyChannel != null){
    this.pubnub.getMessage(this.lobbyChannel, (msg) => {
      // Start the game once an opponent joins the channel
      if(msg.message.notRoomCreator){
        // Create a different channel for the game
        this.gameChannel = 'tictactoegame--' + this.roomId;

        this.pubnub.subscribe({
          channels: [this.gameChannel]
        });
      }
    }); 
  }
}
```

We check if the message arrived is _msg.message.notRoomCreator_, which is published by the player that joins the channel. If so, we create a new channel, '_tictactoegame--_', with the _room id_ appended to the string. The game channel is used to publish all the moves made by the players which will update their boards. 

Finally, after subscribing to the game channel, the state value of _isPlaying_ is set to true. Doing so will replace the lobby component with the game component.

```javascript
this.setState({
   isPlaying: true
 });  

 // Close the modals if they are opened
 Swal.close();
}
```

Once the game component is shown, we want to close all the modals, if opened, from the Lobby component by doing _Swal.close()_. 

Now that we have two players connected to a unique game channel, they can begin playing tic tac toe! In the next section, we will implement the UI and logic for the game component.

## Build Game Features

The first thing we do in **Game.js** is set up the **base constructor**:

```javascript
// Game.js
import React from 'react';
import Board from './Board';
import Swal from "sweetalert2";  

class Game extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      squares: Array(9).fill(''), // 3x3 board
      xScore: 0,
      oScore: 0,
      whosTurn: this.props.myTurn // Player X goes first
    };

    this.turn = 'X';
    this.gameOver = false;
    this.counter = 0; // Game ends in a tie when counter is 9
  }

  render() { 
    return (); 
  } 
 } 
export default Game;
```

For the state objects, we initialize the array _squares_ property, which is used to store the player's positions in the board. This will be explained furthermore below. We also set the players score to 0 and set the value of _whosTurn_ to _myTurn_, which is initialized to **true** for _Player X_ and **false** for _Player O_. 

The value of the variables _turn_ and _counter_ will change throughout the progression of the game. At the end of the game, _gameOver_ is set to **true**.

### Add the UI

Next, let's set up the markup for the Game component inside of the **render** method.

```javascript
render() {
  let status;
  // Change to current player's turn
  status = `${this.state.whosTurn ? "Your turn" : "Opponent's turn"}`;

  return (
    <div className="game">
      <div className="board">
        <Board
            squares={this.state.squares}
            onClick={index => this.onMakeMove(index)}
          />  
          <p className="status-info">{status}</p>
      </div>

      <div className="scores-container">
        <div>
          <p>Player X: {this.state.xScore} </p>
        </div>

        <div>
          <p>Player O: {this.state.oScore} </p>
        </div>
      </div>   
    </div>
  );
}
```
We show the value of _status_ in the UI to let the players know if it's their turn to make a move or if the other player's turn. The boolean value of the state _whosTurn_ is updated every time a move is made. The rest of the UI consists of the Board component and the player's score.

### Add the Logic

When the player makes a move on the board, a call to **onMakeMove(****index)** is made where **index** is the position the piece is placed on the board. The board has 3 rows and 3 columns, so 9 squares total. Each square has its own unique **index** value, starting with the value 0 and ending with the value 8.

```javascript
onMakeMove = (index) =>{
  const squares = this.state.squares;

  // Check if the square is empty and if it's the player's turn to make a move
  if(!squares[index] && (this.turn === this.props.piece)){ 
    squares[index] = this.props.piece;

    this.setState({
      squares: squares,
      whosTurn: !this.state.whosTurn 
    });

    // Other player's turn to make a move
    this.turn = (this.turn === 'X') ? 'O' : 'X';

    // Publish move to the channel
    this.props.pubnub.publish({
      message: {
        index: index,
        piece: this.props.piece,
        turn: this.turn
      },
      channel: this.props.gameChannel
    });  

    // Check if there is a winner
    this.checkForWinner(squares)
  }
}
```

After getting the state of the array _squares_, a conditional statement is used to check if the square the player touched is empty and if it's their turn to make a move. If one or both conditions are not met, then the player's piece is not placed on the square. Otherwise, the player's piece is added to the array _squares_ in the index the piece was placed on. 

For example, if _Player X_ makes a move in row 0, column 2 and the conditional statement is true, then _squares[2]_ will have the value of "X".

<img class=" wp-image-44300 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/squares-example.png" alt="Example with the squares array" width="239" height="244" />

Next, the state is changed to reflect the new state of the game and _turn_ is updated so the other player can make their move. In order for the other player's board to update with the current data, we publish the data to the game channel. All of this is happening in realtime, so both players will immediately see their boards update as soon as a valid move is made. The last thing to do in this method is to call **checkForWinner(squares)** to check if there is a winner. 

Before we do that, let's take a look at **componentDidMount****() **where we set up the listener for new messages that arrive in the game channel.

```javascript
componentDidMount(){
  this.props.pubnub.getMessage(this.props.gameChannel, (msg) => {
    // Update other player's board
    if(msg.message.turn === this.props.piece){
      this.publishMove(msg.message.index, msg.message.piece);
    }
  });
}
```

Since both players are connected to the same game channel, they will both receive this message. The method **publishMove(index, piece)** is called, where **index** is the position that piece was placed and **piece** is the piece of the player that made the move. This method updates the board with the current move and checks if there is a winner. To prevent the player that made the current move to have to redo this process again, the _if_ statement checks if the player's piece matches the value of _turn_. If so, their board is updated.

```javascript
// Opponent's move is published to the board
publishMove = (index, piece) => {
  const squares = this.state.squares;

  squares[index] = piece;
  this.turn = (squares[index] === 'X')? 'O' : 'X';

  this.setState({
    squares: squares,
    whosTurn: !this.state.whosTurn
  });

  this.checkForWinner(squares)
}
```

The logic of updating the board is the same as **onMakeMove()**. Let's now go over **checkForWinner()**.

```javascript
checkForWinner = (squares) => {
  // Possible winning combinations
  const possibleCombinations = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8],
    [2, 4, 6],
  ];

  // Iterate every combination to see if there is a match
  for (let i = 0; i < possibleCombinations.length; i += 1) {
    const [a, b, c] = possibleCombinations[i];
    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
      this.announceWinner(squares[a]);
      return;
    }
  }
}
```

All the winning combinations are in the double array _possibleCombinations_, where every array is a possible combination to winning the game. Every array in _possibleCombinations_ is checked against the array _squares_. If there is a match, then there is a winner. Let's follow an example to make this more clear. 

Say that Player X makes a winning move in row 2 column 0\. The _index_ of that position is 6\. The board now looks like this: 

<img class="size-full wp-image-44301 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/winning-move-example-1.png" alt="Example of a winning move" width="239" height="244" />

The winning combination for _Player X_ is [2,4,6]. The array _squares_ is updated to: ["O", "", "X", "O", "X", "", "X", "", ""]. 

In the _for_ loop, when _[a,b,c]_ has the values of [2,4,6], the _if_ statement in the _for_ loop with be true since [2,4,6]all have the same value of _X_. The score of the winner needs to be updated, so **a****nnounceWinner()** is called to award the winning player. 

If the game ends in a tie, there is no winner for that round. To check for tie games, we use a counter that increments by one every time a move is made on the board.

```javascript
// Below the for loop in checkForWinner()
// Check if the game ends in a draw
this.counter++;
// The board is filled up and there is no winner
if(this.counter === 9){
  this.gameOver = true;
  this.newRound(null);
}
```
If the counter reaches 9, then the game ends in a draw because the player did not make a winning move in the last square of the board. When this happens, the method **newRound()** is called with a **null** argument since there is no winner. 

Before we go to this method, let's go back to **a****nnounceWinner()**.

```javascript
// Update score for the winner
announceWinner = (winner) => {
  let pieces = {
    'X': this.state.xScore,
    'O': this.state.oScore
  }

  if(winner === 'X'){
    pieces['X'] += 1;
    this.setState({
      xScore: pieces['X']
    });
  }
  else{
    pieces['O'] += 1;
    this.setState({
      oScore: pieces['O']
    });
  }
  // End the game once there is a winner
  this.gameOver = true;
  this.newRound(winner);	
}
```

The parameter of this method is _winner_, which is the player that won the game. We check if the winner is 'X' or 'O' and increment the winner's score by one point. Since the game is over, the variable _gameOver_ is set to true and the method **newRound()** is called.

### Start a New Round

_Player X_ has the option to play another round or to end the game and go back to the lobby. 

<img class="size-full wp-image-44321 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/playero-endgame-modal.png" alt="Endgame modal for Player O" width="311" height="197" />

The other player has told to wait until _Player X_ decides what to do. 

<img class=" wp-image-44322 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/playerx-endgame-modal.png" alt="Endgame modal for Player X" width="311" height="197" />

Once the _Player X_ decides what to do, a message is published to the game channel to let the other player know. The UI is then updated.

```javascript
newRound = (winner) => {
  // Announce the winner or announce a tie game
  let title = (winner === null) ? 'Tie game!' : `Player ${winner} won!`;
  // Show this to Player O
  if((this.props.isRoomCreator === false) && this.gameOver){
    Swal.fire({  
      position: 'top',
      allowOutsideClick: false,
      title: title,
      text: 'Waiting for a new round...',
      confirmButtonColor: 'rgb(208,33,41)',
      width: 275,
      customClass: {
          heightAuto: false,
          title: 'title-class',
          popup: 'popup-class',
          confirmButton: 'button-class',
      } ,
    });
    this.turn = 'X'; // Set turn to X so Player O can't make a move 
  } 

  // Show this to Player X
  else if(this.props.isRoomCreator && this.gameOver){
    Swal.fire({      
      position: 'top',
      allowOutsideClick: false,
      title: title,
      text: 'Continue Playing?',
      showCancelButton: true,
      confirmButtonColor: 'rgb(208,33,41)',
      cancelButtonColor: '#aaa',
      cancelButtonText: 'Nope',
      confirmButtonText: 'Yea!',
      width: 275,
      customClass: {
          heightAuto: false,
          title: 'title-class',
          popup: 'popup-class',
          confirmButton: 'button-class',
          cancelButton: 'button-class'
      } ,
    }).then((result) => {
      // Start a new round
      if (result.value) {
        this.props.pubnub.publish({
          message: {
            reset: true
          },
          channel: this.props.gameChannel
        });
      }

      else{
        // End the game
        this.props.pubnub.publish({
          message: {
            endGame: true
          },
          channel: this.props.gameChannel
        });
      }
    })      
  }
 }
```

If the message is _reset_, then all the state values and variables, except the score for the players, are reset to their initial values. Any modals that are still open are closed and a new round starts for both players. 

For the message _endGame_, all the modals are closed and the method **endGame()** is called. This method is in **App.js**.

```javascript
// Reset everything
endGame = () => {
  this.setState({
    piece: '',
    isPlaying: false,
    isRoomCreator: false,
    isDisabled: false,
    myTurn: false,
  });

  this.lobbyChannel = null;
  this.gameChannel = null;
  this.roomId = null;  

  this.pubnub.unsubscribe({
    channels : [this.lobbyChannel, this.gameChannel]
  });
}
```

All the state values and variables are reset to their initial values. The channel names are reset to null because a new name is generated every time a player creates a room. Since the channel names won't be useful anymore, the players unsubscribe from both the lobby and the game channel. The value of _isPlaying_ is reset to false, so the game component will be replaced with the lobby component. 

The last method to include in **App.js** is **componentWillUnmount()**, which unsubscribes the players from both channels.

```javascript
componentWillUnmount() {
  this.pubnub.unsubscribe({
    channels : [this.lobbyChannel, this.gameChannel]
  });
}
```

This is all we need to do for the game to work! You can get the CSS file for the game in the [repo](https://github.com/ocastroa/react-tictactoe/blob/master/src/Game.css). Now, let's get the game up and running.

## Run the Game

There are a couple of small steps we need to do before running the game. First, we need to enable the [PubNub Presence feature](https://www.pubnub.com/docs/react-native-javascript/api-reference-presence/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q3-DevTo-August-5) because we use it to get the number of people in the channel (we used _withPresence_ when subscribing to the lobby channel). Go to the [PubNub Admin Dashboard](https://admin.pubnub.com/#/login/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q3-DevTo-August-5) and click on your application. Click on **Keyset** and scroll down to **Application add-ons**. Toggle the **Presence** switch to **on**. Keep the default values the same. 

<img class="size-full wp-image-44305 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/enable-presence-1.png" alt="Enable presence in PubNub Admin Dashboard" width="757" height="252" />

To install the three dependencies used in the app and to run the app, you can run the script **dependencies.sh** that's in the root directory of the app.

```shell
# dependencies.sh
npm install --save pubnub pubnub-react
npm install --save shortid
npm install --save sweetalert2

npm start
```

In the terminal, go to the app's root directory and type the following command to make the script <span style="font-weight: 400;">executable</span>:

```shell
chmod +x dependencies.sh
```

Run the script with this command:

```shell
./dependencies.sh
```

The app will open in http://localhost:3000 with the lobby component displaying.

<img class="size-full wp-image-44306 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/run-the-app.png" alt="Run the React app locally" width="408" height="333" />

Open another tab, or preferably window, and copy and paste http://localhost:3000. In one window, create a channel by clicking the 'Create' button. A modal will pop up displaying the _room id_. Copy and paste that id. Go to the other window and click the 'Join' button. When the modal pops up, type the _room id_ in the input field and press the 'Okay' button. 

<img class=" wp-image-44318 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/create-join-lobby.png" alt="Create and join the channel" width="757" height="252" />

Once the players are connected, the game will start. The window you used to create the channel makes the first move. Press any square on the board and see as the piece **X** is displayed on the board in realtime for both windows. If you try to press another square in the same board, nothing will happen because it's no longer your turn to make a move. In the other window, press any square on the board and the piece **O** is placed in the square. 

<img class="size-full wp-image-44319 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/place-piece-on-board.png" alt="Place the piece on the board" width="757" height="252" />

Keep on playing until there is a winner or a tie. A modal is then displayed announcing the winner of the round, or announcing that the game ended in a tie. In the same modal, _Player X_ will have to decide whether to continue playing or to exit the game. The modal for _Player O_ will tell them to wait for a new round. 

<img class="size-full wp-image-44320 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/end-of-game.png" alt="End of game modals" width="757" height="252" />

Everything, except the score, is reset if _Player X_ continues the game. Otherwise, both players are taken back to the lobby where they can create or join new channels. Check out this [video](https://www.youtube.com/embed/CegmJ6BXhbc) for a game demo. 


## Create a Native Mobile Version

Now that you've got your game working beautifully in web browsers, let's take it mobile! Check out how to build a [Multiplayer Tic Tac Toe Game in React Native for Android and iOS](https://www.pubnub.com/blog/multiplayer-mobile-tic-tac-toe-react-native-ios-android-part-one/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q3-DevTo-August-5). And if you want to build more realtime games and want to know how PubNub can help you with that, check out the [multiplayer gaming tutorials](https://www.pubnub.com/blog/tag/multiplayer-gaming/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q3-DevTo-August-5).