Turns and Deployment: Multiplayer React Tic Tac Toe Game
Note: This is part two of building a multiplayer tic-tac-toe game in React Native using PubNub. If you have not completed part one of the tutorial, which focuses on environment setup, lobby creation, and inviting other players to join, please go back to part one of the tutorial. Remember, you can view the completed application on GitHub.
Welcome to Part Two of the tutorial series on building a mobile multiplayer tic-tac-toe game with React Native and PubNub. In this section, you will implement the core functionality of the game and play the game.
If you haven’t already, please check out and go through Part One before working on this section, as the project was initialized and the lobby was set up in that section.
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 this file. Add the following code to the file.
There are several things occurring in the base constructor.
An array of possible combinations to win the game is set up. If a player matches with any of the combinations, that player is the winner of the game.
Another array named
idsis initialized and labels every square of the table with a unique id to check if a square has been occupied or not.
rowscontains three empty arrays with each size of three.
For the state objects, the array
movesis initialized with empty string values and the score for both players to 0. The array
moveswill be used to check if there is a winner, discussed later on.
Finally, three variables are added,
countthat will be useful throughout the game.
Setting up the UI
The UI for the component includes the table and its styles, current score, and username for each player.
For the username,
this.props is used since the value is obtained from App.js. As a reminder from part one of the tutorial, here are the values that you’ll be using from App.js.
To build the tic-tac-toe game board,
generateRows is called, which calls
generateBlocks. These methods are responsible for creating the table.
TouchableHightlight is called 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 discussed in detail later on in the tutorial.
Adding the Logic
Create the function
row_index, the row that the piece was placed on, and
col_index, the column the piece was placed on, as the method arguments.
The integer ID of the square pressed is obtained by getting the value of
if statement checks if the square the player touched is empty and if it is the current player's turn to play. The touch is ignored if these two conditions are not met. If the conditions are met, the piece is added to the array
id as the index.
For example, if the room creator makes a move on row 0 column 2 on the table, then
this.ids returns the integer 2.
X is added to the array
moves on the second index:
[“”, “”, “X”, “”, “”, “”, “”, “”, “”]. Later in
updateScores, you will see how
moves is used to check for a winner.
onMakeMove, after the state for
moves is changed,
turns is updated so the next player can make their move. Data is published to the game channel, such as the piece that moved and its position. 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.
Implement the channel listener in
componentDidMount to listen to incoming messages from the channel.
Both players will receive this message since both are subscribed to the same channel. When the message is received, several of the same steps performed in
onMakeMove occur: update the table and check if there is a winner. However, the player that made the move should not repeat the steps again. Perform a
if statement to ensure that only the opposite player performs the steps. Do this with the conditional
if turn (which is either X or O) matches the player’s piece.
Next, implement the method
To determine if there is a winner, you'll need 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. You'll use
[a,b,c] to get each array of
possible_combinations. Then check if that matches any pattern in
For example, the room creator makes a winning move on row 2 column 2, with an
id of 8, on the table. The table will look as follows:
The winning moves are in positions 2, 5, and 8, according to the IDs. The array
moves is now:
[“O”, “”, “X”, “”, “O”, “X”, “”, “”, “X”]. In
updateScores, 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
if statement in the
for loop will be
[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 implementing that method, finish the rest of
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
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.
You check who has won the game and increment the winner’s current score by one point. Once the score has been updated,
game_over is set to
true and calls the method
Two different alerts are shown 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.
For the opponent, an alert is shown with a message telling them to wait for a new round, which will be decided by the room creator.
If the room creator decides to end the game, then the message
gameOver is published to the channel. Otherwise, the message
restart is published to the channel. These messages are handled inside of
If the room creator wants to play another round, then the table is reset. This occurs by setting the array
moves to its original state:
turn is set to
However, 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 you need to go back to App.js to implement this functionality.
The state values are reset back to the original state and
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 the game if they choose to do so.
The React Native multiplayer tic-tac-toe game is now ready to test and play!
Testing the App
Before running the app, you'll need to enable the Presence feature to detect the number of people in the channel. To turn it on, go to the PubNub Admin Dashboard and click on your application. Click on your Keyset and toggle the Presence function switch to enable it. A dialog will appear, asking you to confirm by typing in "ENABLE" in all caps. Do so and keep the default values the same.
To run the game, make sure you are in the project folder and enter the following command in the terminal:
This command will open the simulator and run the game. You can also run the game on the Android emulator by replacing
run-android (Note: Make sure you open an emulator first before running the command). Once the simulator opens, the lobby UI will be displayed.
You can test the game by running the React app version of the tic-tac-toe game used to test the simulator applications. The test React app is already connected to the React Native app used in this tutorial and the necessary logic is taken care of. All you need to do is insert the same Pub/Sub keys from the tutorial's React Native app.
Playing the Game
You 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 test React app from this repo. Once you open the project, go to the file Game.js and in the constructor, add the Pub/Sub keys you used for the React Native app. That’s all you have to edit for the test React project.
npm install command in the terminal to install the dependencies. To run the app, enter the
npm start command in the terminal.
The app will open at http://localhost:3000 with an empty table and two input fields.
Navigate 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.
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.
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 and the winning square's background color will turn 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.
If the room creator decides to play another round, both the simulator and the test React app tables 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 test React app browser resets the table. You can create a new channel in the simulator and in the React test app browser you can join that new channel.
You've successfully created a React Native multiplayer tic-tac-toe game using PubNub for real-time updates and user online detection. In Part one of the tutorial, you set up your application environment, created the lobby for players to join, and displayed dialogs for players to join other lobbies. In Part two of the tutorial, you completed the logic of players actually playing the game, winning conditions, and how players can choose to play once more or leave the lobby.
You can check out the completed version of the application on GitHub, as well as the React version of the application used to test the game created in this tutorial. If you would like to learn more about how to power your React applications with PubNub, be sure to check out PubNub's other React developer resources:
If you have any other questions or concerns, please feel free to reach out to email@example.com.