Build a Tic-Tac-Toe Game - Tests 9, 10, and 11

Tell us what’s happening:

The game does everything it’s supposed to, including the following tests which fail:

  1. Clicking on a button.square element after the game has ended should result in no change.
  2. The game should display a message indicating the winner to be X or O.
  3. The game should display a message indicating a draw.

Your code so far

Tic-Tac-Toe


* {
  margin: 0;
  padding: 0;
}

body {
  box-sizing: border-box;
  font-family: Arial, sans-serif;
  width: 100vw;
  height: 100vh;
}

.board {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: 10%;
}

.row {
  display: flex;
  align-items: center;
}

.status {
  margin: 10px;
}

button.square {
  height: 50px;
  width: 50px;
  margin: 5px;
  background-color: white;
  border: solid grey 1px;
  border-radius: 5px;
  transition: 0.2s;
}

button.square:hover {
  cursor: pointer;
  border: solid black 1px;
}

button#reset {
  margin: 5px;
  padding: 10px;
  width: 120px;
  border: solid grey 1px;
  border-radius: 5px;
  transition: 0.2s;
}

button#reset:hover {
  cursor: pointer;
}

const { useState, useEffect } = React;

export function Board() {
  const [nextPlayer, setNextPlayer] = useState("X");
  const [marked, setMarked] = useState(0);
  const [playable, setPlayable] = useState(true);
  const [status, setStatus] = useState("Next player: X");
  const togglePlayer = () => {
    setNextPlayer(nextPlayer === "X" ? "O" : "X");
  }
  const [
    [b0, setB0],
    [b1, setB1],
    [b2, setB2],
    [b3, setB3],
    [b4, setB4],
    [b5, setB5],
    [b6, setB6],
    [b7, setB7],
    [b8, setB8]
  ] = [
        useState(""),
        useState(""),
        useState(""),
        useState(""),
        useState(""),
        useState(""),
        useState(""),
        useState(""),
        useState("")
      ];
  const setters = [setB0, setB1, setB2, setB3, setB4, setB5, setB6, setB7, setB8];
  const winningCombos = [
    [b0, b1, b2],
    [b3, b4, b5],
    [b6, b7, b8],
    [b0, b3, b6],
    [b1, b4, b7],
    [b2, b5, b8],
    [b0, b4, b8],
    [b2, b4, b6]
  ];
  const handleClickB0 = () => {
    if(b0 === "" && playable) {
      setMarked(marked+1);
      setB0(nextPlayer);
      togglePlayer();
    }
  };
  const handleClickB1 = () => {
    if(b1 === "" && playable) {
      setMarked(marked+1);
      setB1(nextPlayer);
      togglePlayer();
    }
  };
  const handleClickB2 = () => {
    if(b2 === "" && playable) {
      setMarked(marked+1);
      setB2(nextPlayer);
      togglePlayer();
    }
  };
  const handleClickB3 = () => {
    if(b3 === "" && playable) {
      setMarked(marked+1);
      setB3(nextPlayer);
      togglePlayer();
    }
  };
  const handleClickB4 = () => {
    if(b4 === "" && playable) {
      setMarked(marked+1);
      setB4(nextPlayer);
      togglePlayer();
    }
  };
  const handleClickB5 = () => {
    if(b5 === "" && playable) {
      setMarked(marked+1);
      setB5(nextPlayer);
      togglePlayer();
    }
  };
  const handleClickB6 = () => {
    if(b6 === "" && playable) {
      setMarked(marked+1);
      setB6(nextPlayer);
      togglePlayer();
    }
  };
  const handleClickB7 = () => {
    if(b7 === "" && playable) {
      setMarked(marked+1);
      setB7(nextPlayer);
      togglePlayer();
    }
  };
  const handleClickB8 = () => {
    if(b8 === "" && playable) {
      setMarked(marked+1);
      setB8(nextPlayer);
      togglePlayer();
    }
  };
  const checkWinner = () => {
    for(const combo of winningCombos) {
      if(combo.every(sq => sq === "X")) {
        setPlayable(false);
        setStatus("Winner: X");
        return;
      } else if(combo.every(sq => sq === "O")) {
        setPlayable(false);
        setStatus("Winner: O");
        return;
      }
    }
    if(marked === 9) {
      setPlayable(false);
      setStatus("It's a draw!");
      return;
    }
    setStatus(`Next player: ${nextPlayer}`);
  };
  useEffect(() => {
    checkWinner();
  }, [marked]);
  const handleReset = () => {
    setters.forEach(setB => setB(""));
    setMarked(0);
    setPlayable(true);
    setNextPlayer("X");
    setStatus("Next player: X");
  };
  return (
    <div className="board">
      <h1>Tic-Tac-Toe</h1>
      <p className="status">{status}</p>
      <div className="row">
        <button className="square" onClick={handleClickB0}>{b0}</button>
        <button className="square" onClick={handleClickB1}>{b1}</button>
        <button className="square" onClick={handleClickB2}>{b2}</button>
      </div>
      <div className="row">
        <button className="square" onClick={handleClickB3}>{b3}</button>
        <button className="square" onClick={handleClickB4}>{b4}</button>
        <button className="square" onClick={handleClickB5}>{b5}</button>
      </div>
      <div className="row">
        <button className="square" onClick={handleClickB6}>{b6}</button>
        <button className="square" onClick={handleClickB7}>{b7}</button>
        <button className="square" onClick={handleClickB8}>{b8}</button>
      </div>
      <button id="reset" onClick={handleReset}>Reset Game</button>
    </div>
  );
}

Your browser information:

User Agent is: Mozilla/5.0 (X11; Linux x86_64; rv:152.0) Gecko/20100101 Firefox/152.0

Challenge Information:

Build a Tic-Tac-Toe Game - Build a Tic-Tac-Toe Game

GitHub Link: freeCodeCamp/curriculum/challenges/english/blocks/lab-tic-tac-toe/67e3a6b7f60b4085588189e6.md at main · freeCodeCamp/freeCodeCamp · GitHub

While your code works but fails at some point, you should refactor the handler and ensure the test for winner is defined synchronously within the handler. So you don’t need the useEffect hook and the toggle function.

I hope this helps.