Open In App

Multiplayer Tic Tac Toe Game using React & Node

Last Updated : 15 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

This project is a Multiplayer Online Tic Tac Toe game developed using React & NodeJS . It allows multiple users to play the classic Tic Tac Toe game in real time. The application handles basic and advanced calculations related to the game logic and ensures a smooth gaming experience.

Output Preview: Let us have a look at how the final output will look like.

tic-tac-toe

Prerequisites:

Approach to Create Multiplayer Tic Tac Toe Game using React & Node :

  • State Management: Use React’s useState hook to manage the game state, including the board, current player, and error messages.
  • Game Logic: Implement functions to calculate the winner of the game and to handle making moves on the game board.
  • Rendering: Render the game board using a loop to generate the cells dynamically based on the current state of the board. Use CSS classes to highlight winning cells.
  • Player Turn: Keep track of whose turn it is using the playerTurn state variable and display the current player in the UI.
  • Resetting the Game: Implement a function to reset the game state when the “Reset Game” button is clicked.
  • Error Handling: Display error messages when users attempt to make invalid moves, such as selecting an already occupied cell or continuing to play after the game has been won.

Steps to Create the Node JS App and Installing Module:

Step 1: Initialize the project:

  • Create a new directory for your project and navigate to it in the terminal.
  • Run npm init to create a package.json file.
mkdir <<name of project>>
cd <<name of project>>
npm init -y

Step 2: Install following by running the following command in your project directory:

  • Express.js: This is a web application framework for Node.js, used for building web applications and APIs.
  • http: This is a core module in Node.js for handling HTTP requests and responses.
  • cors: This is a Node.js package for providing a Connect/Express middleware that can be used to enable CORS with various options.
  • socket.io: This is a library that enables real-time, bidirectional, and event-based communication between web clients and servers.
npm install express http cors socket.io

Project Structure(Backend):

Screenshot-2024-03-08-154539

Project Strucutre

The updated dependencies in package.json file will look like:

"dependencies": {
"cors": "^2.8.5",
"express": "^4.18.3",
"http": "^0.0.1-security",
"socket.io": "^4.7.4"
}

Example: Below is an example of creating a server of multiplayer game.

Javascript
const express = require("express");
const http = require("http");
const cors = require("cors");
const socketIO = require("socket.io");

const app = express();
const server = http.createServer(app);
const io = socketIO(server, {
  cors: {
    origin: "http://localhost:3000", // Update with your React app's URL
    methods: ["GET", "POST"],
  },
});

const port = 5000;

app.use(cors());

app.get("/game", (req, res) => {
  res.status(200).send("Tic Tac Toe Game Server");
});

io.on("connection", (socket) => {
  console.log("A user connected");

  socket.on("makeMove", (data) => {
    io.emit("moveMade", data);
  });

  socket.on("resetGame", (newGame) => {
    io.emit("gameReset", newGame);
  });

  socket.on("disconnect", () => {
    console.log("User disconnected");
  });
});

server.listen(port, () => {
  console.log(`Server running at http://localhost:${port}`);
});

S

node index.js

Steps to Create a Frontend Application:

Step 1: Create a new React app:

npx create-react-app <<Name_of_project>>
cd <<Name_of_project>>

Step 2: Install the required modules

npm install npm install socket.io-client

Project Structure(Backend):

Screenshot-2024-03-08-154605

Project Structure


Example: Below is an example of frontend of multiplayer online game.

HTML
//client/public/index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <!-- <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> -->

    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  </body>
</html>
CSS
/*index.css*/
body {
  font-family: 'Arial', sans-serif;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  background-color: #f0f0f0;
}

.app-container {
  text-align: center;
}

h1 {
  color: #333;
  margin-bottom: 20px;
}

.board {
  display: grid;
  grid-template-columns: repeat(3, 100px);
  grid-gap: 10px;
  margin-top: 20px;
  margin-left: 65px; 
}

.cell {
  width: 100px;
  height: 100px;
  border: 2px solid #333;
  font-size: 2em;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  background-color: #fff;
  transition: background-color 0.3s;
}

.cell:hover {
  background-color: #f0f0f0;
}

.current-player {
  margin-top: 20px;
  font-size: 1.5em;
  color: #333;
}

.reset-button {
  margin-top: 10px;
  padding: 10px 20px;
  font-size: 1em;
  cursor: pointer;
  background-color: #4caf50;
  color: #fff;
  border: none;
  border-radius: 5px;
  transition: background-color 0.3s;
}

.reset-button:hover {
  background-color: #45a049;
}

.error-message {
  color: #ff0000;
  margin-top: 10px;
  font-size: 1.2em;
}

.winner {
  background-color: #4caf50;
  color: #fff;
}
Javascript
//client/src/index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';


const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
Javascript
//client/src/App.js
import React, { useState, useEffect } from "react";
import "./index.css";
import { io } from "socket.io-client";

const socket = io("http://localhost:5000");

const App = () => {
  const [game, setGame] = useState({
    board: Array(9).fill(null),
    currentPlayer: "X",
  });
  const [errorMessage, setErrorMessage] = useState("");
  const [playerTurn, setPlayerTurn] = useState("Player A");

  useEffect(() => {
    socket.on("moveMade", (data) => {
      setGame(data.updatedGame);
      setPlayerTurn(data.updatedGame.currentPlayer);
      setErrorMessage("");
    });

    socket.on("gameReset", (newGame) => {
      setGame(newGame);
      setPlayerTurn("Player A");
      setErrorMessage("");
    });

    socket.on("connect_error", (error) => {
      console.error("WebSocket connection error:", error.message);
    });

    socket.on("disconnect", () => {
      console.log("Disconnected from server");
    });

    return () => {
      socket.off("moveMade");
      socket.off("gameReset");
      socket.off("connect_error");
      socket.off("disconnect");
    };
  }, []);

  const calculateWinner = (squares) => {
    const lines = [
      [0, 1, 2],
      [3, 4, 5],
      [6, 7, 8],
      [0, 3, 6],
      [1, 4, 7],
      [2, 5, 8],
      [0, 4, 8],
      [2, 4, 6],
    ];

    for (let i = 0; i < lines.length; i++) {
      const [a, b, c] = lines[i];
      if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
        return squares[a];
      }
    }

    return null;
  };

  const makeMove = (index) => {
    const squares = [...game.board];

    if (calculateWinner(squares) || squares[index]) {
      setErrorMessage("Invalid move. Please try again.");
      return;
    }

    squares[index] = game.currentPlayer;

    const updatedGame = {
      ...game,
      board: squares,
      currentPlayer: game.currentPlayer === "X" ? "O" : "X",
    };

    socket.emit("makeMove", { index, updatedGame });
  };

  const resetGame = () => {
    const newGame = {
      board: Array(9).fill(null),
      currentPlayer: "X",
    };

    socket.emit("resetGame", newGame);
  };

  const winner = calculateWinner(game.board);

  return (
    <div className="app-container">
      <h1>Welcome to Tic Tac Toe Game</h1>
      <div>
        <div className="board">
          {game.board.map((cell, index) => (
            <div
              key={index}
              className={`cell ${winner && winner === cell ? "winner" : ""}`}
              onClick={() => makeMove(index)}
            >
              {cell}
            </div>
          ))}
        </div>
        <p className="current-player">
          {winner
            ? `Player ${winner} wins!`
            : `Current Player: ${playerTurn}`}
        </p>
        <button className="reset-button" onClick={resetGame}>
          Reset Game
        </button>
      </div>
      {errorMessage && (
        <p className="error-message">{errorMessage}</p>
      )}
    </div>
  );
};

export default App;

Start your application using the following command.

npm start

Output:

game

Tic Tac Toe multiplayer game



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads