You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

211 lines
4.0 KiB

'use strict'
/* eslint-env browser */
/* eslint-disable no-labels */
const game = document.body.querySelector('.ttt-game')
const icons = {
x: game.querySelector('.ttt-icon-x'),
o: game.querySelector('.ttt-icon-o'),
}
const scores = {
x: {
el: game.querySelector('.ttt-score-x'),
score: 0,
},
o: {
el: game.querySelector('.ttt-score-o'),
score: 0,
},
}
const ahead = game.querySelector('.ttt-player-icon--ahead')
const behind = game.querySelector('.ttt-player-icon--behind')
let paused = true
const cells = {}
const axes = ['y', 'x']
const players = ['x', 'o']
function fill(cell, player) {
cell.value = player
cell.el.firstChild && cell.el.removeChild(cell.el.firstChild)
cell.el.appendChild(icons[player].cloneNode(true))
}
function reset() {
scores.x.el.textContent = scores.x.score.toLocaleString()
scores.o.el.textContent = scores.o.score.toLocaleString()
ahead.classList[scores.x.score < scores.o.score ? 'add' : 'remove'](
'ttt-player-icon--hidden'
)
behind.classList[scores.x.score < scores.o.score ? 'remove' : 'add'](
'ttt-player-icon--hidden'
)
for (let y = 1; y <= 3; y++) {
for (let x = 1; x <= 3; x++) {
const cell = cells[y][x]
cell.el.classList.remove('ttt-blink')
cell.el.firstChild && cell.el.removeChild(cell.el.firstChild)
cell.value = ''
}
}
const { empty } = check()
play(empty)
}
function checkLine(line, complete) {
for (const player of players) {
if (line[player].length === 3) {
complete.player = player
complete.cells.push(...line[player])
}
}
}
function check(dryrun) {
const empty = []
const complete = {
player: null,
cells: [],
}
for (const axis of axes) {
const diagonal = { o: [], x: [] }
for (let a = 1; a <= 3; a++) {
const y = a
const x = axis === 'y' ? y : 4 - y
const cell = cells[y][x]
cell.value && diagonal[cell.value].push(cell)
const straight = { o: [], x: [] }
for (let b = 1; b <= 3; b++) {
const y = axis === 'y' ? a : b
const x = axis === 'y' ? b : a
const cell = cells[y][x]
cell.value ? straight[cell.value].push(cell) : empty.push(cell)
}
checkLine(straight, complete)
}
checkLine(diagonal, complete)
}
if (!dryrun) {
paused = true
if (complete.player) {
scores[complete.player].score++
complete.cells.forEach(({ el }) => el.classList.add('ttt-blink'))
if (complete.player === 'o') {
ahead.classList.add('ttt-player-icon--hidden')
behind.classList.remove('ttt-player-icon--hidden')
}
setTimeout(() => {
reset()
}, 1200)
} else if (!empty.length) {
setTimeout(() => {
reset()
}, 1200)
}
}
return { winner: complete.player, empty: [...new Set(empty)] }
}
function play(cells) {
setTimeout(() => {
let found = false
search: for (const player of players) {
for (const cell of cells) {
cell.value = player
const { winner, empty } = check(true)
if (winner || !empty) {
found = true
fill(cell, 'x')
break search
} else {
cell.value = ''
}
}
}
if (!found) {
const cell = cells[Math.round(Math.random() * (cells.length - 1))]
fill(cell, 'x')
}
const { winner, empty } = check()
if (!winner && empty) {
paused = false
}
}, 400)
}
for (let y = 1; y <= 3; y++) {
for (let x = 1; x <= 3; x++) {
const el = game.querySelector(
`.ttt-row:nth-child(${y}) .ttt-cell:nth-child(${x})`
)
el.addEventListener('click', () => {
if (paused) {
return
}
const cell = cells[y][x]
if (!cell.value) {
paused = true
fill(cell, 'o')
const { winner, empty } = check()
!winner && play(empty)
}
})
cells[y] = cells[y] || {}
cells[y][x] = {
x,
y,
el,
value: '',
}
}
}
reset()