React 18 Tic-tac-toe game with hooks

// app.tsx

import "./styles.css";

import React, { useState, useEffect } from "react";

function App() {
  const [board, setBoard] = useState(Array(9).fill(null));
  const [currentPlayer, setCurrentPlayer] = useState("X");

  useEffect(() => {
    if (currentPlayer === "O") {
      makeAIMove();
    }
  }, [currentPlayer]);

  const makeAIMove = () => {
    const bestMove = findBestMove(board);
    if (bestMove !== -1) {
      const newBoard = board.slice();
      newBoard[bestMove] = currentPlayer;
      setBoard(newBoard);
      setCurrentPlayer("X");
    }
  };

  const handleClick = (index) => {
    if (board[index] === null && currentPlayer === "X") {
      const newBoard = board.slice();
      newBoard[index] = currentPlayer;
      setBoard(newBoard);
      setCurrentPlayer("O");
    }
  };

  const checkWinner = (currentBoard) => {
    const winningPatterns = [
      [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 (const pattern of winningPatterns) {
      const [a, b, c] = pattern;
      if (
        currentBoard[a] &&
        currentBoard[a] === currentBoard[b] &&
        currentBoard[a] === currentBoard[c]
      ) {
        return currentBoard[a];
      }
    }

    if (currentBoard.every((cell) => cell !== null)) {
      return "draw";
    }

    return null;
  };

  // Minimax algorithm
  const findBestMove = (currentBoard) => {
    let bestMove = -1;
    let bestScore = -Infinity;

    for (let i = 0; i < 9; i++) {
      if (currentBoard[i] === null) {
        const newBoard = currentBoard.slice();
        newBoard[i] = "O";
        const score = minimax(newBoard, 0, false);

        if (score > bestScore) {
          bestScore = score;
          bestMove = i;
        }
      }
    }

    return bestMove;
  };

  const minimax = (currentBoard, depth, isMaximizing) => {
    const winner = checkWinner(currentBoard);

    if (winner === "O") {
      return 1;
    } else if (winner === "X") {
      return -1;
    } else if (winner === "draw") {
      return 0;
    }

    if (isMaximizing) {
      let bestScore = -Infinity;

      for (let i = 0; i < 9; i++) {
        if (currentBoard[i] === null) {
          const newBoard = currentBoard.slice();
          newBoard[i] = "O";
          const score = minimax(newBoard, depth + 1, false);
          bestScore = Math.max(bestScore, score);
        }
      }

      return bestScore;
    } else {
      let bestScore = Infinity;

      for (let i = 0; i < 9; i++) {
        if (currentBoard[i] === null) {
          const newBoard = currentBoard.slice();
          newBoard[i] = "X";
          const score = minimax(newBoard, depth + 1, true);
          bestScore = Math.min(bestScore, score);
        }
      }

      return bestScore;
    }
  };

  const winner = checkWinner(board);

  return (
    <div className="App">
      <h1>Tic-Tac-Toe</h1>
      <div className="board">
        {board.map((cell, index) => (
          <div key={index} className="cell" onClick={() => handleClick(index)}>
            {cell}
          </div>
        ))}
      </div>
      {winner && (
        <div className="result">
          {winner === "draw" ? "It's a draw!" : `Player ${winner} wins!`}
        </div>
      )}
    </div>
  );
}

export default App;

styles.css

.App {
  text-align: center;
  margin-top: 50px;
}

h1 {
  font-size: 24px;
}

.board {
  display: grid;
  margin: 0 auto;
  grid-template-columns: repeat(3, 100px);
  grid-gap: 2px;
  margin-top: 20px;
  width: 300px;
}

.cell {
  width: 100px;
  height: 100px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  background-color: #f0f0f0;
  cursor: pointer;
  transition: background-color 0.3s ease-in-out;
}

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

.result {
  margin-top: 20px;
  font-size: 20px;
}

Working game here https://codesandbox.io/s/tic-tac-toe-h24cw7

In this code, the minimax function calculates the scores for each possible move recursively. It evaluates the current state of the game and returns a score based on whether the AI player (‘O’) or the human player (‘X’) has the advantage. The isMaximizing parameter indicates whether the function should maximize the AI’s score or minimize the human’s score.

The score values are as follows:

  • 1: AI wins.
  • -1: Human wins.
  • 0: It’s a draw.

The algorithm explores all possible moves, assigning scores based on the game outcome. It aims to find the move that leads to the best possible outcome for the AI, assuming optimal play from both players.

This implementation should give you a working AI opponent using the minimax algorithm in your tic-tac-toe game. Keep in mind that the minimax algorithm can become slow for more complex games due to the large number of possible moves. You can explore optimizations like alpha-beta pruning to improve performance.

In this code, we’re using React Hooks to manage the state of the game. The board array represents the game board, and the currentPlayer state keeps track of whose turn it is. The handleClick function updates the state when a cell is clicked. The checkWinner function checks for winning conditions and draws. The game’s state is reflected in the UI, and the result is displayed when the game ends.

Remember to style your app with CSS to make it visually appealing. This example provides the basic functionality of a tic-tac-toe game using React Hooks. You can enhance it further by adding features like a reset button, animations, and more polished UI elements.

Leave a Reply

Your email address will not be published. Required fields are marked *.

*
*
You may use these <abbr title="HyperText Markup Language">HTML</abbr> tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>