Help with Build a Tic-Tac-Toe Game

index.html

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8" />
    <title>Tic-Tac-Toe</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.5/babel.min.js"></script>
    <script
      data-plugins="transform-modules-umd"
      type="text/babel"
      src="index.jsx"
    ></script>
    <link rel="stylesheet" href="styles.css" />
</head>

<body>
    <div id="root"></div>
    <script
      data-plugins="transform-modules-umd"
      type="text/babel"
      data-presets="react"
      data-type="module"
    >
      import { Board } from './index.jsx';
      ReactDOM.createRoot(document.getElementById('root')).render(<Board />);
    </script>
</body>

</html>
styles.css

#board {
  display: grid;
  grid-template-columns: repeat(3, 100px);
  grid-template-rows: repeat(3, 100px);
}
index.jsx


const {useState} = React;

export function Board() {

  const [squares,setSquares] = useState(Array(9).fill(""));

  const [toggle,setToggle] = useState(true);

  const [used,setUsed] = useState(Array(9).fill(false));

  const [winMessage,setWinMessage] = useState("");

  const handleReset = () => {
    setSquares(Array(9).fill(""));
    setUsed(Array(9).fill(false));
    setToggle(true);
    setWinMessage("");

  };

  const handleClick = (index) => {

    if (!used[index]){

      const newSquares = [...squares];
      newSquares[index] = toggle?"X":"O";

      setSquares(newSquares);
      setUsed((prevUsed)=>{
        const newUsed = [...prevUsed];
        newUsed[index] = true;
        return newUsed;
        });
      setToggle(!toggle);

      const winner = checkWin(newSquares);
      if (winner){
        setWinMessage(winner);
        setUsed(Array(9).fill(true));
        console.log(`Winner : ${winner}`);
        console.log("gameover");
      }

      };

    };

  const checkWin = (squares) => {

    //Check verticals//
    for (let i=0;i<3;i++){
      if ((squares[i])&&squares[i]===squares[i+3]&&squares[i+3]===squares[i+6]){
      
      console.log(`Vertical ${squares[i]}`);

      return squares[i];
      };
    };

    //Check horizontals//
    for (let i=0;i<9;i=i+3){
      if ((squares[i])&&squares[i]===squares[i+1]&&squares[i+1]===squares[i+2]){

        console.log(`Horizontal ${squares[i]}`);

        return squares[i];
      };
    };

    //Check left diagonal//
    if (squares[0] && (squares[0]===squares[4]&&squares[4]===squares[8])){

      console.log(`Left Diagonal ${squares[0]}`);

      return squares[0];
    };

    //Check right diagonal//
    if (squares[2] && (squares[2]===squares[4]&&squares[4]===squares[6])){

      console.log(`Right Diagonal ${squares[2]}`);

      return squares[2];
    };

    //Check draw//

    if (squares.every(item=>item)){

      console.log(`Draw`);

      return ("Draw");
    };


    console.log(`No win`);

    return null;
    
    }; 



  return (
    <div id="container">
      <div id = "board">
        {squares.map((item,index)=>(
          <button
          key={index}
          className="square"
          onClick={()=>handleClick(index)}
          >
          {item}
          </button>
        ))}
      </div>
      <div id="controls">
        <button id="reset" onClick={handleReset}>
        Reset
        </button>
          {
            winMessage?
            (winMessage==="Draw"?<p id="win-message">It's a draw!</p>
            :<p id="win-message">{winMessage} wins!</p>)
            :null
          }
      </div>
    </div>
  )

};

Challenge Link

Hello, I have some probelms passing test case 10

10. The game should display a message indicating the winner to be X or O.

I have passed all other test cases including test case 11

11. The game should display a message indicating a draw.

Which is included in the same logic for test case 10, so I am not sure where I am going wrong. I have used some console.log() statements to try and debug the code but I am not sure where I am going wrong.

Any help would be appreciated. Thank you.

P.S. I understand there have been some forum posts about a similar problem occuring as to mine, I have read through those forum posts and understood that it could be due to some browser issue. Even so, I just want to confirm that whether my code really has a problem.

The issue is with what you return from checkWin.
Right now you only return "X", "O", or "Draw".
The tests expect the full message string: "X wins!", "O wins!", or "Draw".

index.jsx

const {useState} = React;

export function Board() {

  const [squares,setSquares] = useState(Array(9).fill(""));

  const [toggle,setToggle] = useState(true);

  const [used,setUsed] = useState(Array(9).fill(false));

  const [winMessage,setWinMessage] = useState("");

  const handleReset = () => {
    setSquares(Array(9).fill(""));
    setUsed(Array(9).fill(false));
    setToggle(true);
    setWinMessage("");

  };

  const handleClick = (index) => {

    if (!used[index]){

      const newSquares = [...squares];
      newSquares[index] = toggle?"X":"O";

      setSquares(newSquares);
      setUsed((prevUsed)=>{
        const newUsed = [...prevUsed];
        newUsed[index] = true;
        return newUsed;
        });
      setToggle(!toggle);

      const winner = checkWin(newSquares);
      if (winner){
        
        setWinMessage(winner);
        setUsed(Array(9).fill(true));

        };
      };
    };

  const checkWin = (squares) => {

    //Check verticals//
    for (let i=0;i<3;i++){
      if ((squares[i])&&squares[i]===squares[i+3]&&squares[i+3]===squares[i+6]){
      
      // console.log(`Vertical ${squares[i]}`);

      return (`Winner : ${squares[i]}`);
      };
    };

    //Check horizontals//
    for (let i=0;i<9;i=i+3){
      if ((squares[i])&&squares[i]===squares[i+1]&&squares[i+1]===squares[i+2]){

        // console.log(`Horizontal ${squares[i]}`);

        return (`Winner : ${squares[i]}`);
      };
    };

    //Check left diagonal//
    if (squares[0] && (squares[0]===squares[4]&&squares[4]===squares[8])){

      // console.log(`Left Diagonal ${squares[0]}`);

      return (`Winner : ${squares[0]}`);
    };

    //Check right diagonal//
    if (squares[2] && (squares[2]===squares[4]&&squares[4]===squares[6])){

      // console.log(`Right Diagonal ${squares[2]}`);

      return (`Winner : ${squares[2]}`);
    };

    //Check draw//

    if (squares.every(item=>item)){

      // console.log(`Draw`);
      return (`Draw!`);
    };


    // console.log(`No win`);

    return null;
    
    }; 



  return (
    <div id="container">
      <div id = "board">
        {squares.map((item,index)=>(
          <button
          key={index}
          className="square"
          onClick={()=>handleClick(index)}
          >
          {item}
          </button>
        ))}
      </div>
      <div id="controls">
        <button id="reset" onClick={handleReset}>
        Reset
        </button>
          {winMessage&&<p>{winMessage}</p>}
      </div>
    </div>
  )

};

Thanks for the reply, here I have changed the checkWin function to return the whole string as you suggested, but it still does not pass the test case?

10. The game should display a message indicating the winner to be X or O.

I can think of another possibility where because I am passing the winner variable to the winMessage state, and the testcase is not accounting for that. Or perhaps the question wants me to useEffect to checkWin, which is why I am not passing the testcase. Not sure, any advice is appreciated.

Thanks for your help.

The tests expect checkWin itself to return the full string: "X wins!", "O wins!", or "Draw".

This isn’t the case. The test queries the DOM directly.

@chuqingyan123
However, it does expect the message element to display the winner in this format “Winner: X” (Just like the example app)

Got it, thanks. Once I followed the above format the test case passed. Thanks for you help.

Glad you got it! The instructions could be more clear here.

1 Like