Tic Tac Toe

Render Board

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Chessboard</title>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <style>
      body {
        font-family: 'Helvetica', sans-serif;
      }

      .board-cell {
        border: 1px solid #666;
        box-sizing: border-box;
        display: inline-block;
        font-size: 32px;
        height: 100px;
        line-height: 100px;
        text-align: center;
        width: 100px;
        margin-left: -1px;
        margin-top: -1px;
      }

      .board-cell .content {
        display: inline-block;
        vertical-align: middle;
      }
    </style>

  </head>
  <body>
    <h1>Tic Tac Toe</h1>
    <p>Current player turn: <span class="js-current-player"></span></p>
    <div class="js-board">

    </div>
    <button class="js-reset">Reset</button>
    <script>
     (() => {
      function init() {
        const DOM = {
          $currentPlayer: document.querySelector('.js-current-player'),
          $board: document.querySelector('.js-board'),
          $resetButton: document.querySelector('.js-reset'),
        }
        const SIZE = 3;
        function initialState() {
          return {
            boardModel: Array(SIZE).fill(null).map(_ => Array(SIZE).fill(null)),
            players: ['X', 'O'],
            currentPlayer: 0, // 0 or 1

            gameEnded: false,
            turn: 0,
          }
        }

        let state = initialState();

        function renderBoard() {
          DOM.$currentPlayer.textContent = state.players[state.currentPlayer];
          // Assuming SIZE > 0;

          DOM.$board.innerHTML = ''
          for(let i = 0; i < SIZE; i++) {
            const $row = document.createElement('div');
            $row.classList.add('border-row');
            for(let j = 0; j < SIZE; j++) {
              const $cell = document.createElement('div');
              $cell.classList.add('board-cell');
              $cell.setAttribute('data-i', i);
              $cell.setAttribute('data-j', j);
              const $content = document.createElement('span');
              $content.classList.add('content');
              $content.textContent = state.boardModel[i][j];
              $cell.appendChild($content);
              $row.appendChild($cell)
            }
            DOM.$board.appendChild($row);
          }
        }
        renderBoard();
      }
      document.addEventListener('DOMContentLoaded', init)
     })()
    </script>

  </body>
</html>

Attach Event Listeners

DOM.$board.addEventListener("click", (event) => {
  if (state.gameEnded) {
    return;
  }
  if (!event.target.classList.contains("board-cell")) {
    return;
  }
 
  // get chosen cell
  const $cell = event.target;
  const i = parseInt($cell.getAttribute("data-i"), 10);
  const j = parseInt($cell.getAttribute("data-j"), 10);
 
  if (state.boardModel[i][j] !== null) {
    alert("Cell has already been taken!");
    return;
  }
 
  // get current player
  const player = state.players[state.currentPlayer];
  // mark the move
  state.boardModel[i][j] = player;
  // after mark the move, we start to check if it is a winning move
  // const winningMove = checkWinning(state.boardModel, player);
  const winningMove = false;
  // increase the turn counter
  state.turn++;
  if (!winningMove) {
    state.currentPlayer = state.currentPlayer === 0 ? 1 : 0;
    // re-render with new mark
    renderBoard();
    if (state.turn === SIZE * SIZE) {
      alert("It is a draw!");
    }
  } else {
    renderBoard();
    state.gameEnded = true;
    alert(`Player ${player} wins!`);
  }
});
 
DOM.$resetButton.addEventListener("click", () => {
  if (confirm("Start a new game?")) {
    state = initialState();
    renderBoard();
  }
});
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Chessboard</title>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <style>
      body {
        font-family: 'Helvetica', sans-serif;
      }

      .board-cell {
        border: 1px solid #666;
        box-sizing: border-box;
        display: inline-block;
        font-size: 32px;
        height: 100px;
        line-height: 100px;
        text-align: center;
        width: 100px;
        margin-left: -1px;
        margin-top: -1px;
      }

      .board-cell .content {
        display: inline-block;
        vertical-align: middle;
      }
    </style>

  </head>
  <body>
    <h1>Tic Tac Toe</h1>
    <p>Current player turn: <span class="js-current-player"></span></p>
    <div class="js-board">

    </div>
    <button class="js-reset">Reset</button>
    <script>
     (() => {
      function init() {
        const DOM = {
          $currentPlayer: document.querySelector('.js-current-player'),
          $board: document.querySelector('.js-board'),
          $resetButton: document.querySelector('.js-reset'),
        }
        const SIZE = 3;
        function initialState() {
          return {
            boardModel: Array(SIZE).fill(null).map(_ => Array(SIZE).fill(null)),
            players: ['X', 'O'],
            currentPlayer: 0, // 0 or 1

            gameEnded: false,
            turn: 0,
          }
        }

        let state = initialState();

        function renderBoard() {
          DOM.$currentPlayer.textContent = state.players[state.currentPlayer];
          // Assuming SIZE > 0;

          DOM.$board.innerHTML = ''
          for(let i = 0; i < SIZE; i++) {
            const $row = document.createElement('div');
            $row.classList.add('border-row');
            for(let j = 0; j < SIZE; j++) {
              const $cell = document.createElement('div');
              $cell.classList.add('board-cell');
              $cell.setAttribute('data-i', i);
              $cell.setAttribute('data-j', j);
              const $content = document.createElement('span');
              $content.classList.add('content');
              $content.textContent = state.boardModel[i][j];
              $cell.appendChild($content);
              $row.appendChild($cell)
            }
            DOM.$board.appendChild($row);
          }
        }
        function attachEventListeners() {
          DOM.$board.addEventListener('click', (event) => {
            if (state.gameEnded) {
              return;
            }
            if (!event.target.classList.contains('board-cell')) {
              return;
            }
            const $cell = event.target;
            const i = parseInt($cell.getAttribute('data-i'), 10);
            const j = parseInt($cell.getAttribute('data-j'), 10);
            if (state.boardModel[i][j] !== null) {
              alert('Cell has already been taken!');
              return;
            }
            const player = state.players[state.currentPlayer];
            state.boardModel[i][j] = player;
            const winningMove = false;

            state.turn++;
            if (!winningMove) {
              state.currentPlayer = (state.currentPlayer + 1) % 2;
              renderBoard();
              if (state.turn === SIZE * SIZE) {
                alert('It is a draw!');
              }
            } else {
              renderBoard();
              state.gameEnded = true;
              alert(`Player \${player} wins!`);
            }
          });

          DOM.$resetButton.addEventListener('click', () => {
            if (confirm('Start a new game?')) {
              state = initialState();
              renderBoard();
            }
          });
        }

        renderBoard();
        attachEventListeners();
      }
      document.addEventListener('DOMContentLoaded', init)
     })()
    </script>

  </body>
</html>

Check Winning

function checkWinning(board, player) {
  // check horizontal
  for (let i = 0; i < SIZE; i++) {
    if (board[i].every((cell) => cell === player)) {
      return true;
    }
  }
 
  // check vertical
  for (let j = 0; j < SIZE; i++) {
    let verticalAllPlayer = true;
    for (let i = 0; i < SIZE; i++) {
      if (board[i][j] != player) {
        verticalAllPlayer = false;
        break;
      }
    }
    if (verticalAllPlayer) {
      return verticalAllPlayer;
    }
  }
 
  // check diagonal south-east
  let diagonalAllPlayer = true;
  for (let i = 0; i < SIZE; i++) {
    if (board[i][i] !== player) {
      diagonalAllPlayer = false;
      break;
    }
  }
  if (diagonalAllPlayer) {
    return diagonalAllPlayer;
  }
 
  // check diagonal north-east
  let diagonalAllPlayer = true;
  // i = 2, j = 0 ; i = 1, j = 1; i = 2; j = 0;
  for (let i = SIZE - 1, j = 0; i >= 0; i--, j++) {
    if (board[i][i] !== player) {
      diagonalAllPlayer = false;
      break;
    }
  }
  if (diagonalAllPlayer) {
    return diagonalAllPlayer;
  }
 
  return false;
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Chessboard</title>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <style>
      body {
        font-family: 'Helvetica', sans-serif;
      }

      .board-cell {
        border: 1px solid #666;
        box-sizing: border-box;
        display: inline-block;
        font-size: 32px;
        height: 100px;
        line-height: 100px;
        text-align: center;
        width: 100px;
        margin-left: -1px;
        margin-top: -1px;
      }

      .board-cell .content {
        display: inline-block;
        vertical-align: middle;
      }
    </style>

  </head>
  <body>
    <h1>Tic Tac Toe</h1>
    <p>Current player turn: <span class="js-current-player"></span></p>
    <div class="js-board">

    </div>
    <button class="js-reset">Reset</button>
    <script>
     (() => {
      function init() {
        const DOM = {
          $currentPlayer: document.querySelector('.js-current-player'),
          $board: document.querySelector('.js-board'),
          $resetButton: document.querySelector('.js-reset'),
        }
        const SIZE = 3;
        function initialState() {
          return {
            boardModel: Array(SIZE).fill(null).map(_ => Array(SIZE).fill(null)),
            players: ['X', 'O'],
            currentPlayer: 0, // 0 or 1

            gameEnded: false,
            turn: 0,
          }
        }

        let state = initialState();

        function renderBoard() {
          DOM.$currentPlayer.textContent = state.players[state.currentPlayer];
          // Assuming SIZE > 0;

          DOM.$board.innerHTML = ''
          for(let i = 0; i < SIZE; i++) {
            const $row = document.createElement('div');
            $row.classList.add('border-row');
            for(let j = 0; j < SIZE; j++) {
              const $cell = document.createElement('div');
              $cell.classList.add('board-cell');
              $cell.setAttribute('data-i', i);
              $cell.setAttribute('data-j', j);
              const $content = document.createElement('span');
              $content.classList.add('content');
              $content.textContent = state.boardModel[i][j];
              $cell.appendChild($content);
              $row.appendChild($cell)
            }
            DOM.$board.appendChild($row);
          }
        }
        function checkWinning(board, player) {
          // Check horizontal.

          for (let i = 0; i < SIZE; i++) {
            if (board[i].every(cell => cell === player)) {
              return true;
            }
          }

          // Check vertical.

          for (let j = 0; j < SIZE; j++) {
            let verticalAllPlayer = true;
            for (let i = 0; i < SIZE; i++) {
              if (board[i][j] !== player) {
                verticalAllPlayer = false;
                break;
              }
            }
            if (verticalAllPlayer) {
              return verticalAllPlayer;
            }
          }

          // Check diagonal South-East.

          let diagonalAllPlayer = true;
          for (let i = 0; i < SIZE; i++) {
            if (board[i][i] !== player) {
              diagonalAllPlayer = false;
              break;
            }
          }
          if (diagonalAllPlayer) {
            return diagonalAllPlayer;
          }

          // Check diagonal North-East.

          diagonalAllPlayer = true;
          for (let i = SIZE - 1, j = 0; i >= 0; i--, j++) {
            if (board[i][j] !== player) {
              diagonalAllPlayer = false;
              break;
            }
          }
          if (diagonalAllPlayer) {
            return diagonalAllPlayer;
          }

          return false;
        }
        function attachEventListeners() {
          DOM.$board.addEventListener('click', (event) => {
            if (state.gameEnded) {
              return;
            }
            if (!event.target.classList.contains('board-cell')) {
              return;
            }
            const $cell = event.target;
            const i = parseInt($cell.getAttribute('data-i'), 10);
            const j = parseInt($cell.getAttribute('data-j'), 10);
            if (state.boardModel[i][j] !== null) {
              alert('Cell has already been taken!');
              return;
            }
            const player = state.players[state.currentPlayer];
            state.boardModel[i][j] = player;
            const winningMove = checkWinning(state.boardModel, player);

            state.turn++;
            if (!winningMove) {
              state.currentPlayer = (state.currentPlayer + 1) % 2;
              renderBoard();
              if (state.turn === SIZE * SIZE) {
                alert('It is a draw!');
              }
            } else {
              renderBoard();
              state.gameEnded = true;
              alert(`Player \${player} wins!`);
            }
          });

          DOM.$resetButton.addEventListener('click', () => {
            if (confirm('Start a new game?')) {
              state = initialState();
              renderBoard();
            }
          });
        }

        renderBoard();
        attachEventListeners();
      }
      document.addEventListener('DOMContentLoaded', init)
     })()
    </script>

  </body>
</html>
Last Update: 12:13 - 22 April 2024

On this page