tic-tac-toe. Lev Mishin

by kontrach

HTML

<div class="game">
  <ul id="gameField" tabindex="1" class="game-field"></ul>
  <div class="game-controls">
    <button id="newGame">Start new game</button>
    <div class="top-buffer-m">
      <pre id="gameLog">log</pre>
    </div>
  </div>
</div>

<template id="cellTemplate">
  <li class="game-field-cell">
    <div class="sign-O">
      <span></span>
    </div>
    <div class="sign-X">
      <span></span>
    </div>
  </li>
</template>

CSS

* {
  box-sizing: border-box; }

button {
  display: inline-block;
  font-weight: 400;
  line-height: 1.25;
  text-align: center;
  white-space: nowrap;
  vertical-align: middle;
  border: 1px solid transparent;
  padding: .5rem 1rem;
  font-size: 1rem;
  border-radius: .25rem;
  color: #292b2c;
  background-color: #fff;
  border-color: #ccc;
  cursor: pointer; }
  button:active, button:hover {
    color: #292b2c;
    background-color: #e6e6e6;
    background-image: none;
    border-color: #adadad; }

pre {
  display: block;
  padding: 9.5px;
  margin: 0 0 10px;
  font-size: 13px;
  line-height: 1.4;
  color: #333;
  word-break: break-all;
  word-wrap: break-word;
  background-color: #f5f5f5;
  border: 1px solid #ccc;
  border-radius: 4px; }

.top-buffer-m {
  margin-top: 10px; }

.sign-X, .sign-O {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  padding: 5px;
  display: none; }
  .sign-X span, .sign-O span {
    width: 100%;
    height: 100%;
    display: block; }

.sign-O span {
  top: 0;
  left: 0;
  border-width: 5px;
  border-style: solid;
  border-radius: 50%;
  border-color: cornflowerblue; }

.sign-X span {
  position: relative; }
  .sign-X span:before, .sign-X span:after {
    content: "";
    border: 2px solid;
    border-radius: 2px;
    margin-left: -2px;
    margin-top: -2px;
    position: absolute;
    width: 0;
    height: 100%;
    left: 50%;
    border-color: indianred; }
  .sign-X span:before {
    transform: rotateZ(45deg); }
  .sign-X span:after {
    transform: rotateZ(-45deg); }

.game {
  border-radius: 5px;
  display: flex;
  padding: 0; }
  .game-field {
    width: 210px;
    height: 210px;
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-wrap: wrap; }
    .game-field.game-over .game-field-cell {
      pointer-events: none; }
    .game-field-cell {
      width: 70px;
      height: 70px;
      border: 1px solid black;
      background-color: lightgray;
      pointer-events: all;
 ...

JavaScript

(function() {
	// from here and next - change 'var' to 'const' if variable shouldn't be changed and to 'let' if should
  var cells;
  var activeCell = 0;
  const activeCellClass = "cell-active";
  const gameFieldElement = document.getElementById("gameField");
  const gameLogElement = document.getElementById("gameLog");
  
  // If you are using ES2015 you can use 'class' syntax
  function TicTacToe() {
    var self = this;
    var currentPlayer = 0;
    const cellsCountX = 3;
    const playerSign = ["X", "O"];
		
    // would it be easier to manage two dimensional array? 
    /*
     [
       [0,0,0],
       [0,0,0],
       [0,0,0]
     ]
    */
    // And with this approach you can create patterns and check for wins. But it is not necessary
    var state = Array(Math.pow(cellsCountX, 2)).fill(0);

    this.game = {
      currentPlayer,
      playerSign,
      cellsCount: Math.pow(cellsCountX, 2),
      cellsCountX,
      state
    };

    this.isGameOver = () => {
      var game = self.game;
      const getWeight = (x) => x === game.playerSign[0] ? -1 : !x ? 0 : +1;
      // "d" for diagonal? It's better to have self-explanatory variable names
      var d1 = 0;
      var d2 = 0;
      for (var i = 0; i < game.cellsCountX; i++) {
        var v = 0;
        var h = 0;
        for (var j = 0; j < game.cellsCountX; j++) {
          h += getWeight(game.state[i * game.cellsCountX + j]);
          v += getWeight(game.state[i + game.cellsCountX * j]);
        }
        v = Math.abs(v);
        h = Math.abs(h);
        if (v === 3 || h === 3) {
          return {
            start: v === 3 ? i : i * game.cellsCountX,
            direction: v === 3 ? TicTacToe.winLineDirection.VERTICAL : TicTacToe.winLineDirection.HORIZONTAL,
            result: TicTacToe.gameResult.WIN
          };
        }
        d1 += getWeight(game.state[i * game.cellsCountX + i]);
        d2 += getWeight(game.state[game.cellsCountX + i * game.cellsCountX - i - 1]);
      }
      d1 = Math.abs(d1);
 ...