Multiplayer Tic Tac Toe Game in React Native for iOS and Android: Player Turns and Deployment
Tags: reactnative, android, ios, tictactoe
Published: 2019-07-24 17:13:51

---
canonical_url: https://www.pubnub.com/blog/multiplayer-mobile-tic-tac-toe-react-native-ios-android-part-two/
---

Welcome to Part Two of our tutorial series on building a mobile multiplayer tic tac toe game with React Native and PubNub. In this section, we will work on the actual game itself and testing the game using two methods. 

If you haven't already, check out and go through [Part One](https://dev.to/pubnub/multiplayer-tic-tac-toe-game-in-react-native-for-ios-and-android-lobby-and-joining-2mck) before working on this section, as we initialized the project and set up the lobby.

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

## Implementing the Game Component

From the project root directory, go to **src/components** and create a new file named **Game.js**. All the game logic will be inside of this file. Add the following code to the file:

```javascript
import React, { Component } from 'react';
import {
  StyleSheet,
  Text,
  View,
  TouchableHighlight,
  Alert
} from 'react-native';

import range from 'lodash';
const _ = range;

export default class Game extends Component {

  constructor(props) {
    super(props);	

    // Winning combinations
    this.possible_combinations = [
      [0, 3, 6],
      [1, 4, 7],
      [0, 1, 2],
      [3, 4, 5],
      [2, 5, 8],
      [6, 7, 8],
      [0, 4, 8],
      [2, 4, 6]
    ];

    // Corresponds to the square number on the table
    this.ids = [
      [0, 1, 2],
      [3, 4, 5],
      [6, 7, 8]
    ];

    // For the 3x3 table
    this.rows = [
      _.range(3).fill(''),
      _.range(3).fill(''),
      _.range(3).fill(''),
    ];

    this.state = {
      moves: _.range(9).fill(''),
      x_score: 0,
      o_score: 0,
    }
    this.turn = 'X' // Changes every time a player makes a move
    this.game_over = false; // Set to true when the game is over
    this.count = 0; // used to check if the game ends in a draw
  }
}
```

We do several things in the **base constructor**:

1.  First, we set up an array of possible combinations to win the game. If a player matches with any of the combinations, that player is the winner of the game.
2.  Second, we set up another array named **ids** that labels every square of the table with a unique id to check if a square has been occupied or not.
3.  Then, we set up _rows_ which contain three empty arrays with each size of three.
4.  After, for the state objects, we initialize _moves_ to be an array with empty string values and initialize the score for both players to 0\. The array _moves_ will be used to check if there is a winner, as we will see later on.
5.  Finally, we add three variables, _turn,_ _game_over_ and _count_ that will be useful throughout the game.

## Setting up the UI

Let's set up the UI for the component, which includes the table and the current score and username for each player. Also, include the styles.

```javascript
render() {
  return (
    <View style={styles.table_container}>
      <View style={styles.table}>
        {this.generateRows()}
      </View>

      <View style={styles.scores_container}>
        <View style={styles.score}>
          <Text style={styles.user_score}>{this.state.x_score}</Text>
          <Text style={styles.username}>{this.props.x_username} (X)</Text>
        </View>

        <View style={styles.score}>
          <Text style={styles.user_score}>{this.state.o_score}</Text>
          <Text style={styles.username}>{this.props.o_username} (O)</Text>
        </View>
      </View>				
    </View>
  );
 }
}

const styles = StyleSheet.create({
  table_container: {
    flex: 9
  },
  table: {
    flex: 7,
    flexDirection: 'column',
    color: 'black'
  },
  row: {
    flex: 1,
    flexDirection: 'row',
    borderBottomWidth: 1,
  },
  block: {
    flex: 1,
    borderRightWidth: 1,
    borderColor: '#000',
    alignItems: 'center',
    justifyContent: 'center'
  },
  block_text: {
    fontSize: 30,
    fontWeight: 'bold',
    color: 'black'
  },
  scores_container: {
    flex: 2,
    flexDirection: 'row',
    alignItems: 'center'
  },
  score: {
    flex: 1,
    alignItems: 'center',
  },
  user_score: {
    fontSize: 25,
    fontWeight: 'bold',
    color: 'black'
  },
  username: {
    fontSize: 20,
    color: 'black'
  }
});

```

Notice that for the username, we use _this.props_ since we get the value from **App.js**. As a refresher, here are the values that we'll be using from **App.js**.

```html
<Game 
  pubnub={this.pubnub}
  channel={this.channel} 
  username={this.state.username} 
  piece={this.state.piece}
  x_username={this.state.x_username}
  o_username={this.state.o_username}
  is_room_creator={this.state.is_room_creator}
  endGame={this.endGame}
/>
```

To build the tic tac toe table, we call the method **generateRows()**, which calls **generateBlocks()**. These methods are responsible for creating the table.


```javascript
generateRows = () => {
  return this.rows.map((row, col_index) => {
    return (
      <View style={styles.row} key={col_index}>
        {this.generateBlocks(row, col_index)}
      </View>
    );
  });
}

generateBlocks = (row, row_index) => {
  return row.map((block, col_index) => {
    let id = this.ids[row_index][col_index];
    return (
      <TouchableHighlight 
        key={col_index} 
        onPress={
          this.onMakeMove.bind(this, row_index, col_index)
        } 
        underlayColor={"#CCC"} 
        style={styles.block}>

        <Text style={styles.block_text}>
          {
            this.state.moves[id]
          }
        </Text>

      </TouchableHighlight>	
    );
  });
}
```

We use **TouchableHightlight** so players can touch any square on the table. In order for their corresponding piece to be placed on that square, set the block text to be the value of _this.state.moves[id]_, which will contain the correct piece for the player that made the move. This will be explained more below.

## Adding the Logic

Make a call to **onMakeMove()** with _row_index_, the row that the piece was placed on, and _col_index_, the column the piece was placed on, as the method arguments.

```javascript
onMakeMove(row_index, col_index) {
  let moves = this.state.moves;
  let id = this.ids[row_index][col_index];

  // Check that the square is empty
  if(!moves[id] && (this.turn === this.props.piece)){ 
    moves[id] = this.props.piece;

    this.setState({
      moves
    });

    // Change the turn so the next player can make a move
    this.turn = (this.turn === 'X') ? 'O' : 'X';

    // Publish the data to the game channel
    this.props.pubnub.publish({
      message: {
        row: row_index,
        col: col_index,
        piece: this.props.piece,
        is_room_creator: this.props.is_room_creator,
        turn: this.turn
      },
      channel: this.props.channel
    });
    this.updateScores.call(this, moves);
  }
}
```

We get the integer id of the square pressed by getting the value of _this.ids[row_index][col_index]._ The _if_ statement checks if the square the player touched is empty and if it's that player's current turn to place their piece. We ignore the touch if these two conditions are not met. If the conditions are met, we add the piece to the array _moves_ with _id_ as the index. 

For example, if the room creator makes a move on row 0 column 2 on the table, then _this.ids__[0][2]_ returns the integer 2.

```javascript
this.ids = [
  [0, 1, 2],
  [3, 4, 5],
  [6, 7, 8]
];
```

The piece _X_ is added to the array _moves_ on the second index: ["", "", "X", "", "", "", "", "", ""]. Later in **updateScores()**, we will see how _moves_ is used to check for a winner. 

Getting back to **onMakeMove()**, after we change the state for _moves_, we update _turns_ so the next player can make their move. We publish the data to the game channel, such as the piece that moved and the position it was placed at. The other player's table updates with the current data. Finally, make a call to **updateScores()** to check if there is a winner or if the game ended in a draw. 

Before we get to **updateScores()**, implement the channel listener in **componentDidMount()** to listen to incoming messages from the channel.

```javascript
componentDidMount() {
  // Listen for messages in the channel
  this.props.pubnub.getMessage(this.props.channel, (msg) => {
    // Add the other player's move to the table
    if(msg.message.turn === this.props.piece){
      let moves = this.state.moves;
      let id = this.ids[msg.message.row][msg.message.col];

      moves[id] = msg.message.piece;

      this.setState({
        moves
      });

      this.turn = msg.message.turn;
      this.updateScores.call(this, moves);
    }
  });
}
```

Both players will receive this message since both are subscribed to the same channel. When the message is received, we do several of the same steps we did for **onMakeMove()**, in particular, update the table and check if there's a winner. But, we don't want the player that made the move to do the steps over again. So, we do an _if_ statement to make sure that only the opposite player performs the steps. We do so by checking if _turn_ (which is either _X_ or _O_) matches the player's piece. 

Let's now implement the method **updateScores()**.

```javascript
updateScores = (moves) => {
 // Iterate the double array possible_combinations to check if there is a winner
 for (let i = 0; i < this.possible_combinations.length; i++) {
    const [a, b, c] = this.possible_combinations[i];
    if (moves[a] && moves[a] === moves[b] && moves[a] === moves[c]) {
      this.determineWinner(moves[a]);	
      break;
    }
  }
}
```

To determine if there is a winner, we have to iterate _possible_combinations_ to check if any of the combinations are present in the table. This is where the array _moves_comes in handy. We use_ [a,b,c]_ to get each array of _possible_combinations_. Then check if that matches any pattern in _moves_. To make this more clear, let's follow an example. 


Let's say the room creator makes a winning move on row 2 column 2, with an _id_ of 8, on the table. The table will look like the image below: 

<img class="size-full wp-image-43357 aligncenter" src="https://www.pubnub.com//wp-content/uploads/2019/07/winning-move-example.png" alt="Winning move example" width="235" height="415" />

As you can see, the winning moves are in positions 2, 5, and 8, according to their id's. The array _moves_ is now:  ["O", "", "X", "", "O", "X", "", "", "X"]. In **updateScores(),** we iterate through every possible winning combination. 

For this example, the winning combination is [2,5,8]. So when _[a,b,c]_ has the values of _[2,5,8]_, the _if_ statement in the _for_ loop with be true since  _[2,5,8]_ all have the same value of _X_. A call is made to **determineWinner()** to update the score of the winning player, which in this case, is the room creator. Before we get to that method, let's finish the rest of **updateScores()**. 

If no winner is found and the game ends in a draw, then neither player gets a point. To check if there is a draw, add the following code below the above _for_ loop.

```javascript
this.count++;
// Check if the game ends in a draw
if(this.count === 9){
  this.game_over = true;
  this.newGame();	
}
```

Every time a square is pressed on the table, **updateScores()** is called. If no winner is found, then _count_ increments by one. If the _count_ is equal to 9, the game ends in a draw. This occurs when a player makes a final move on the last square of the table that is not a winning move, so _count_ is incremented from 8 to 9\. The method **newGame()** is then called. 

We will get back to **newGame()** in a bit. Let's first implement **determineWinner()**.

```javascript
determineWinner = (winner) => {
  var pieces = {
    'X': this.state.x_score,
    'O': this.state.o_score
  }

  // Update score for the winner
  if(winner === 'X'){
    pieces['X'] += 1;
    this.setState({
      x_score: pieces['X']
    });
  }
  else{
    pieces['O'] += 1;
    this.setState({
      o_score: pieces['O']
    });
  }
  // End the game once there is a winner
  this.game_over = true;
  this.newGame();	
}
```

We check if the winner is the room creator or the opponent and we increment the winner's current score by 1 point. Once the score has been updated, we set _game_over_ to true and call the method **newGame()**.

## Game Over
```javascript
new_game = () => {
  // Show this alert if the player is not the room creator
  if((this.props.is_room_creator === false) && this.game_over){
    Alert.alert('Game Over','Waiting for rematch...');
    this.turn = 'X'; // Set turn to X so opponent can't make a move
  }

  // Show this alert to the room creator
  else if(this.props.is_room_creator && this.game_over){
    Alert.alert(
      "Game Over!", 
      "Do you want to play another round?",
      [
        {
          text: "Nah", 
          onPress: () => {
            this.props.pubnub.publish({
              message: {
                gameOver: true
              },
              channel: this.props.channel
            });				
          },
          style: 'cancel'
        },
        {
          text: 'Yea', 
          onPress: () => {
            this.props.pubnub.publish({
              message: {
                reset: true
              },
              channel: this.props.channel
            });
          }  
        },
      ],
      { cancelable: false } 
    );
  }
}
```

We show two different alerts to the room creator and the opponent. The alert for the room creator has a message asking them if they want to play another round or exit the game. 

<img class="size-full wp-image-43443 aligncenter" src="https://www.pubnub.com//wp-content/uploads/2019/07/game-over-alert-room-creator.png" alt="Game over alert for the room creator" width="235" height="415" />

For the opponent, we show them an alert with a message telling them to wait for a new round, which will be decided by the room creator. 

<img class="size-full wp-image-43444 aligncenter" src="https://www.pubnub.com//wp-content/uploads/2019/07/game-over-opponent.png" alt="Game over alert for the opponent" width="235" height="415" />

If the room creator decides to end the game, then the message _gameOver_ is published to the channel; else the message _restart_ is published to the channel. We take care of these messages by adding some more logic inside of _getMessage()_  in **_componentDidMount()_**.

```javascript
componentDidMount() {
  // Listen for messages in the channel
  this.props.pubnub.getMessage(this.props.channel, (msg) => {
    ...

    if(msg.message.reset){
      this.setState({
        moves: _.range(9).fill('')
      });
      this.turn = 'X';
      this.game_over = false;
    }

    if(msg.message.gameOver){
      this.props.pubnub.unsubscribe({
        channels : [this.props.channel]
      });
      this.props.endGame();
    }
  }); 
}
```

If the room creator wants to play another round, then we reset the table as it was at the start of the game. We do so by setting the array _moves_ to its original state. We reset _turn_ to be _X_ again and reset _game_over_ to false. 

But if the room creator decides to end the game, then both players unsubscribe from the current channel and a call to the method **endGame()** is made. This method will end the game and take the players back to the lobby since _is_playing_ is reset to false. The method _endGame()_ is a prop from **App.js**, so we have to go back to **App.js** to implement it.

```javascript
// In App.js
endGame = () => {
  // Reset the state values
  this.setState({
    username: '',
    rival_username: '',
    is_playing: false,
    is_waiting: false,
    isDisabled: false
  });

  // Subscribe to gameLobby again on a new game
  this.channel = null;
  this.pubnub.subscribe({
    channels: ['gameLobby'],
    withPresence: true
  });
}
```

We reset all the state values back to its original state and reset the value of _channel_ to null since there will be a new _room_id_ when the _Create_ button is pressed again. Finally, subscribe again to the "_gameLobby"_  channel so the players can continue playing tic tac toe if they choose to. 

That is all we need for our React Native tic tac toe game to work! Now, it's time to test the app and play around with it.

## Testing the App

Before running the app, we need to enable the [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-July-22) to detect the number of people in the channel. To turn it on, go to the [PubNub Admin Dashboard](https://admin.pubnub.com/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q3-DevTo-July-22) 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-43421 aligncenter" src="https://www.pubnub.com//wp-content/uploads/2019/07/enable-presence.png" alt="Enable presence" width="638" height="306" />  

To run the app, make sure you are in the project folder and enter the following command in the terminal:

```javascript
react-native run-ios
```

This command will open the simulator and run the app. You can also run the app on the Android emulator by replacing _run-ios_ with _run-android_ (**Note**: Make sure you open an emulator first before running the command). Once the simulator opens, the lobby UI will be displayed. 

<img class="size-full wp-image-43446 aligncenter" src="https://www.pubnub.com//wp-content/uploads/2019/07/lobby.png" alt="iOS lobby" width="235" height="415" />

We can test the app by either using [PubNub's Debug Console](https://www.pubnub.com/docs/console/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q3-DevTo-July-22) or by running the React app version of the tic tac toe game. The React app is already connected to the React Native app and the necessary logic is taken care of. All you need to do is insert the same Pub/Sub keys from the React Native app. 

This section will be broken into two parts to go into detail on how to test the app using the above two methods. For each section, there will be a video that goes over what we did in that section. If you prefer to watch the video instead of reading, go to the end of the section.

## Play the Game using the Debug Console

In this section, we will use the debug console to create a channel and the simulator to join that channel. Make sure you have the app opened in the simulator. 

To get into the debug console, go the [PubNub Admin Dashboard](https://admin.pubnub.com/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q3-DevTo-July-22) and click on **Debug Console** that's on the left sidebar. 

<img class="wp-image-43408 aligncenter" src="https://www.pubnub.com//wp-content/uploads/2019/07/debug_console.png" alt="debug console" width="224" height="209" />

On the top bar, select the app and the keys you are using for this project. 

<img class="size-full wp-image-43409 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/select_app_and_key.png" alt="Select app and key" width="638" height="306" />

You should now see a panel where you can enter the channel name. Change the default channel to "_gameLobby"_  and add the client. 

<img class="size-full wp-image-43419 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/default_channel_panel.png" alt="Debug Channel Panel" width="638" height="306" />

Once the client has been created, replace the default message with:

```javascript
{"is_room_creator":true, "username":"Player X"}
```

You can put any username, but for this example, we will be using **Player X** for the room creator and **Player O** for the opponent. Click the _Send_ button and you should see the message in the console. Here is a screenshot of what the debug console should look like: 

<img class="size-full wp-image-43266 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/debug-console-create-room-1.png" alt="Create a room in the debug console" width="638" height="306" />

Next, we have to create a new channel for the game. Go to the panel next to the current panel we used and change the default channel to _"tictactoe--gameChannel"_ (**Note**: While we chose a fixed channel name for the game, in the app if the player presses the _Create_ button, a random channel name will be generated). 

Add the client to connect to the new channel. Go back to the previous panel where we sent the first message and unsubscribe from that channel as we won't be using it anymore. 

<img class="size-full wp-image-43263 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/connect-to-game-channel-debug-console.png" alt="Connect to the game channel in the debug console" width="638" height="306" />

Now, go to the simulator and for the username type **Player O**. Click the "Join" button and enter the channel name "_gameChannel"_ (Note: "_tictactoe--"_ will be appended to the channel name, so you don't have to include that). 

Once you press **OK**, the lobby component will be replaced with the game component and the table UI will be displayed. Since we used the simulator to join the channel, you won't be able to do anything on the table since the room creator makes the first move. So, on the debug console, replace the default message with:

```javascript
{"row":0, "col":1, "piece":"X", "is_room_creator":true, "turn":"O"}
```

Make sure to capitalize both "X" and "O". Send the above message and you should see an "X" on the simulator's table. Here is a screenshot of the debug console and the simulator: 

<img class="size-full wp-image-43268 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/debug-console-simulator-gameplay.png" alt="Gameplay for the debug console and the simulator" width="638" height="306" />

Now you are able to make a move in the simulator. To continue playing, only change the values of **row** and **col** in the debug console, when it's the room creator's turn, to place "X" in different parts of the table. If a player has won that round, the score for the winning player will update in the simulator. An alert message in the simulator will notify the opponent to wait for a new round. 

<img class="size-full wp-image-43269 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/end-of-game-debug-console-simulator.png" alt="End of the game alert for the opponent" width="638" height="306" />

If you want to play another round, type the following message in the debug console:

```javascript
{"reset":true}
```

The above message will reset the table in the simulator for a new round. To end the game, type the following message instead:

```javascript
{"gameOver":true}
```

Ending the game will send the opponent back to the lobby. 

Check out the video below to review what we did for this section.

<a href="http://www.youtube.com/watch?feature=player_embedded&v=i0DEuosFV0k" target="_blank"><img class="size-full wp-image-43268 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/debug-console-simulator-gameplay.png" alt="Gameplay for the debug console and the simulator" width="638" height="280" />
</a> 


You might have noticed that there are a lot of manual steps when using the debug console. In the app itself, a lot of the manual steps we did are automated, like unsubscribing from "_gameLobby"_ or subscribing to a new channel for the game. We used the debug console because there might be some complications when running two simulators or emulators on your machine. The debug console is a better approach to simulate gameplay of the app. 

Instead of the debug console, you could also use the React version of the app to simulate gameplay. By using the React app, you don't have to worry above manually running commands since everything will be automated, as it should be in the app.

## Play the Game using the React App

We will use the simulator to create a channel and the React app to join that channel (**Note**: The React app is currently set up to only join channels and not create them). You can clone the React app from this [repo](https://github.com/ocastroa/react-tutorial-tic-tac-toe/). Once you open the project, go to the file _Game.js_ and in the constructor, add the **same** Pub/Sub keys you used for the React Native app. That's all you have to edit for the React project. 

<img class="wp-image-43465 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/replace_keys.png" alt="Replace pub/sub keys" width="421" height="350" />

Run the following command in the terminal to install the dependencies:

```javascript
npm install
```
To run the app, type the following command in the terminal:

```javascript
npm start
```

The app will open in http://localhost:3000 with an empty table and two input fields. 

<img class="wp-image-43246 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/empty-board-react-app-1024x491.png" alt="React app empty board" width="638" height="306" />

Go to the simulator and in the lobby, type **Player X** for the username and press the "Create" button to create a new room id. Go back to the browser and for the _username_ field, type **Player O**. In the _room name_ field, type the room id that was created in the simulator. 

<img class="size-full wp-image-43250 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/join-room-react-app.png" alt="join room React app" width="638" height="306" />

Once both fields are filled in, press the _Submit_ button and the game will start for both players. Since the simulator is the room creator, press any square on the table to place an **X**. You should see the **X** in both the simulator and in the browser. Try pressing another square in the simulator's table and you will notice that nothing happens as it's the opponent's turn to make a move. In the browser, press any square to place an **O**. 

<img class="size-full wp-image-43251 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/react-app-gameplay.png" alt="React/ React Native gameplay" width="638" height="306" />

Keep playing until someone wins or the game ends in a draw. If there is a winner, the winner will be announced in the browser along with the winning squares background color turned to green. In the simulator, the score for the winning player will update and an alert will ask the room creator if they want to play another round or exit the game. 

<img class="size-full wp-image-43252 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/game-over-react-app.png" alt="game over react app" width="638" height="306" /> 

If the room creator decides to play another round, both the simulator's and the browser's table will reset for the new round. If the room creator decides to exit the game, the room creator will be taken to the lobby while the browser resets the table. You can create a new channel in the simulator and in the browser you can join that new channel.

Check out the video below to review what we did for this section.

<a href="http://www.youtube.com/watch?feature=player_embedded&v=0W6OqKiP7GM" target="_blank"><img class="size-full wp-image-43251 aligncenter" src="https://www.pubnub.com/wp-content/uploads/2019/07/react-app-gameplay.png" alt="React/ React Native gameplay" width="638" height="280" />
</a> 
## What's Next

If you want to see more examples on how to use PubNub for a React Native project, check out all our [React Native tutorials](https://www.pubnub.com/blog/tag/reactnative/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q3-DevTo-July-22). Or if you want to see the different ways that PubNub is used to power multiplayer games, check out our [multiplayer gaming tutorials](https://www.pubnub.com/blog/tag/multiplayer-gaming/?utm_source=Syndication&utm_medium=DevTo&utm_campaign=SYN-CY19-Q3-DevTo-July-22).