Крестики нолики

магический квадрат

HTML

<table id="tic_tac_toe">
    <tr>
        <td data-value="4"></td>
        <td data-value="3"></td>
        <td data-value="8"></td>
    </tr>
    <tr>
        <td data-value="9"></td>
        <td data-value="5"></td>
        <td data-value="1"></td>
    </tr>
    <tr>
        <td data-value="2"></td>
        <td data-value="7"></td>
        <td data-value="6"></td>
    </tr>
</table>

CSS

table {
        border-collapse: collapse;
    }

    td {
        padding: 10px;
        border: 1px solid #333;
        cursor: pointer;
        width: 64px;
        height: 64px;
        font-size: 36px;
        text-align: center;
        vertical-align: middle;
    }

JavaScript

// Author - Fedor Vlasenko, [email protected]
let step, moves;
const clear = _ => {
  [...tic_tac_toe.querySelectorAll('td')].forEach(e => {
    e.innerText = '';
    e.style.pointerEvents = '';
  });
  step = 0;
  moves = {
    X: [],
    0: []
  }
},
check = arr => {
  arr.sort((a, b) => a - b);
  for (let sum, len = arr.length, i = 0, j = 1, k = len - 1; i < len; i++, j = i + 1) {
    while (k > j) {
      sum = arr[i] + arr[j] + arr[k];
      if (sum === 15) return true;
      sum > 15 ? k-- : j++;
    }
  }
};
clear();
tic_tac_toe.onclick = ({target}) => {
  if (target.tagName !== 'TD') return;
  const val = ++step % 2 ? 'X' : '0';
  moves[val].push(+target.dataset.value);
  const result = moves[val].length > 2 ? check(moves[val]) : false;
  target.insertAdjacentText('beforeend', val);
  target.style.pointerEvents = 'none';
  if (result || step === 9) {
    alert(result ? `Won ${val}` : 'Draw in game');
    clear();
  }
};