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);
...