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

Tell us what’s happening:

hello, the game is working well as expected, but i can’t pass test #11. i need help

Your code so far

<!-- file: 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>
/* file: styles.css */
.container {
  display: grid;
  grid-template-columns: 70px 70px 70px;
  gap: 5px;
}

button {
  height: 70px;
  border-radius: 3px;
  border: 1px solid transparent;
  background-color: rgb(200, 200, 200);
  font-size: 1.4rem;
}

button:hover {
  background-color: rgb(210, 210, 210)
}

#reset {
  width: 220px;
  height: 50px;
  margin-top: 10px;
  background-color: rgb(210, 210, 210);
  font-size: 1.1rem;
}

:disabled {
  color: black; 
}

p {
  font-size: 1.2rem;
}
/* file: index.jsx */
const { useState, useRef, useEffect } = React;
const rounds = [
  [0, 1, 2], [3, 4, 5], [6, 7, 8],
  [0, 3, 6], [1, 4, 7], [2, 5, 8],
  [0, 4, 8], [2, 4, 6]
]
export function Board() {
  const [game, setGame] = useState({
    players: new Array(9).fill(0),
    progress: 'Next player: X',
    played: 'X',
    winner: false
  });
  const container = useRef(null)

  const handleClick = (e) => {
    if (!game.winner) {
      //set players
      if (e.target.textContent === '') {
        let player = game.played;
        setGame((prev) => ({
          ...prev, played: player === 'X' ? 'O' : 'X', progress: `Next player: ${player == 'O' ? 'X' : 'O'}`
        }))
        //check winner
        rounds.forEach((round, ind) => {
          if (round.includes(Number(e.target.value))) {
            const position = round.indexOf(Number(e.target.value))
            round[position] = game.played;
            if (round.join('').match(/[X]{3}|[O]{3}/g)) {
              const won = round.join('').includes('X');
              setGame((prev) => ({
                ...prev, progress: `Winner: ${won ? 'X' : 'O'}`, winner: true
              }))
            }
          }
        })
        e.target.textContent = game.played
      }
    }
  }

  useEffect(() => {
    if (!rounds.flat().join('').match(/[0-9]/g) && !game.winner) {
      setGame((prev) => ({ ...prev, progress: "It's a draw" }))
    }
  }, [game.played])

  const resetGame = () => {
    if (container.current) {
      [0, 1, 2, 3, 4, 5, 6, 7, 8].forEach(el => {
        container.current.children[el].textContent = ''
      })
      rounds = [
        [0, 1, 2], [3, 4, 5], [6, 7, 8],
        [0, 3, 6], [1, 4, 7], [2, 5, 8],
        [0, 4, 8], [2, 4, 6]
      ]
      setGame((prev) => ({ ...prev, winner: false, played: 'X', progress: 'Next player: X' }))
    }
  }

  return (
    <>
      <p>{game.progress}</p>
      <div className="container" ref={container}>
        {game.players.map((player, ind) => {
          return <button value={ind} className="square" key={ind} onClick={(e) => handleClick(e)}></button>
        })}
      </div>
      <button id="reset" type="button" onClick={resetGame}>Reset</button>
    </>
  )
}

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36 OPR/131.0.0.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

Hey! The issue is in your draw detection useEffect. It checks the rounds array (which only contains the 8 win combinations), not all 9 squares, so it never reliably detects a full board.

Here are the 4 changes you need:

Step 1 – Change const rounds to let rounds so you can reset it.

let rounds = […]

Step 2 — Add a board state right after your const [game, setGame] :

code removed by moderator

this creates an array of 9 empty cells that represents the board.

code removed by moderator

Step 3 — Update board on each move inside handleClick, right before the rounds.forEach(…) win check:

Every time a player clicks, it saves their symbol (X or O) in the correct position of the array.

Step 4 — Replace your useEffect entirely:

code removed by moderator

this check if the board is full and if there is a winner.

Step 5 — Reset board inside resetGame, add this line:

code removed by moderator

Hope this helps!

Hello @nicoenrico2013,

It is great that you solved the challenge, but instead of posting your working code, it is best to stay focused on answering the original poster’s question(s) and help guide them with hints and suggestions to solve their own issues with the challenge. How to Help Someone with Their Code Using the Socratic Method

We are trying to cut back on the number of spoiler solutions found on the forum and instead focus on helping other campers with their questions and definitely not posting full working solutions.

In answer to your reply, I can resend my answer with the Socratic Method:

The issue is in your draw detection useEffect. It checks the rounds array, but that only contains the 8 win combinations not all 9 squares so it never reliably detects a full board.

The fix starts by adding a new state to track which squares are filled. Think about what data structure would best represent a board with 9 positions, and how you’d update it each time a player makes a move.

Once you have that, take a look at JavaScript’s array methods there’s one that checks if every element in an array satisfies a condition, which is exactly what you need to detect a full board.

Also worth checking: how is rounds declared at the top? Depending on that, your resetGame might not be able to reassign it and don’t forget to reset your new state there too.

Very nicely done! Thank you.