Grégule et Dangereux le Jeu Vidéo

import { useState, useEffect } from 'react';

// Main App component for the Tic-Tac-Toe game.
export default function App() {
  // State for the board, an array of 9 elements (null, 'X', or 'O').
  const [board, setBoard] = useState(Array(9).fill(null));
  // State for the current player, true for 'X', false for 'O'.
  const [xIsNext, setXIsNext] = useState(true);
  // State for the winner, null if no winner yet.
  const [winner, setWinner] = useState(null);
  // State for the message to display.
  const [status, setStatus] = useState('');

  // useEffect hook to update the game status message.
  useEffect(() => {
    // Check for a winner every time the board state changes.
    const calculatedWinner = calculateWinner(board);
    if (calculatedWinner) {
      setWinner(calculatedWinner);
      setStatus(`Le joueur ${calculatedWinner} a gagné !`);
    } else if (board.every(Boolean)) {
      // Check for a draw if the board is full and there's no winner.
      setStatus('Match nul !');
    } else {
      // Announce the next player.
      setStatus(`Prochain joueur : ${xIsNext ? 'X' : 'O'}`);
    }
  }, [board, xIsNext]);

  // Handle a click on a square.
  const handleClick = (i) => {
    // If the square is already filled or there is a winner, do nothing.
    if (board[i] || winner) {
      return;
    }
    // Create a copy of the board to avoid mutating state directly.
    const nextBoard = board.slice();
    // Fill the square with the current player's symbol.
    nextBoard[i] = xIsNext ? 'X' : 'O';
    // Update the board and switch players.
    setBoard(nextBoard);
    setXIsNext(!xIsNext);
  };

  // Reset the game to its initial state.
  const handleReset = () => {
    setBoard(Array(9).fill(null));
    setXIsNext(true);
    setWinner(null);
    setStatus('');
  };

  // Function to calculate the winner based on winning lines.
  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;
  };

  // Component for a single square on the board.
  const Square = ({ value, onClick }) => {
    return (
      <button
        className="w-24 h-24 sm:w-28 sm:h-28 lg:w-32 lg:h-32 bg-slate-800 text-white text-5xl font-bold border-2 border-slate-700 rounded-lg shadow-lg hover:bg-slate-700 transition-colors duration-200 flex items-center justify-center disabled:cursor-not-allowed"
        onClick={onClick}
        disabled={value !== null}
      >
        {value}
      </button>
    );
  };

  return (
    <div className="flex flex-col items-center justify-center min-h-screen bg-gray-900 text-white font-sans p-4">
      <div className="text-4xl sm:text-5xl font-extrabold mb-8 text-transparent bg-clip-text bg-gradient-to-r from-green-400 to-blue-500">
        Jeu de Morpion
      </div>

      <div className="mb-6 text-xl sm:text-2xl font-semibold text-gray-300">
        {status}
      </div>

      <div className="grid grid-cols-3 gap-2 sm:gap-4 mb-8">
        {board.map((value, i) => (
          <Square key={i} value={value} onClick={() => handleClick(i)} />
        ))}
      </div>

      <button
        className="px-6 py-3 bg-gradient-to-r from-purple-600 to-indigo-600 text-white font-bold text-lg rounded-full shadow-lg hover:from-purple-700 hover:to-indigo-700 transition-transform duration-200 transform hover:scale-105 focus:outline-none focus:ring-4 focus:ring-purple-500 focus:ring-opacity-50"
        onClick={handleReset}
      >
        Recommencer la partie
      </button>
    </div>
  );
}

Jouez à Grégule et Dangereux le Jeu Vidéo

Partage avec tes ami(e)s