Files
ttt/public/game.js
T

132 lines
3.2 KiB
JavaScript

'use strict';
const CpuGame = (() => {
const SCORES_KEY = 'ttt-scores';
const CPU_DELAY = 600;
const state = {
board: Array(9).fill(null),
turn: 'X',
gameOver: false,
scores: { you: 0, opponent: 0, draws: 0 },
};
let cpuTimer = 0;
function persistScores() {
try {
localStorage.setItem(SCORES_KEY, JSON.stringify(state.scores));
} catch (e) { /* storage unavailable */ }
}
function loadScores() {
try {
const raw = localStorage.getItem(SCORES_KEY);
if (raw) {
const s = JSON.parse(raw);
state.scores = {
you: Number(s.you) || 0,
opponent: Number(s.cpu || s.opponent) || 0,
draws: Number(s.draws) || 0,
};
}
} catch (e) { /* corrupted storage */ }
}
function handleMove(i) {
if (state.gameOver || state.turn !== 'X' || state.board[i]) return;
state.board[i] = 'X';
App.renderMark(i, 'X');
const result = App.checkWinner(state.board);
if (result) {
endRound(result);
return;
}
state.turn = 'O';
App.renderStatus('CPU is thinking…', 'thinking');
cpuTimer = setTimeout(cpuMove, CPU_DELAY);
}
function cpuMove() {
if (state.gameOver) return;
const empty = [];
state.board.forEach((v, i) => { if (v === null) empty.push(i); });
if (!empty.length) return;
const i = empty[Math.floor(Math.random() * empty.length)];
state.board[i] = 'O';
App.renderMark(i, 'O');
const result = App.checkWinner(state.board);
if (result) {
endRound(result);
return;
}
state.turn = 'X';
App.renderStatus('Your turn', 'normal');
}
function endRound(result) {
state.gameOver = true;
let title;
let key;
if (result.winner === 'X') {
key = 'you';
title = 'You Win!';
App.renderStatus('You win!', 'normal');
} else if (result.winner === 'O') {
key = 'opponent';
title = 'CPU Wins';
App.renderStatus('CPU wins', 'normal');
document.getElementById('board').classList.add('shake');
} else {
key = 'draws';
title = "It's a Draw";
App.renderStatus("It's a draw", 'normal');
document.getElementById('board').classList.add('pulse');
}
state.scores[key]++;
persistScores();
App.renderScores(state.scores);
if (result.line) App.drawWinLine(result.line);
setTimeout(() => {
App.showOverlay(title);
if (result.winner === 'X') App.burstConfetti();
}, result.winner === 'draw' ? 400 : 600);
}
function newRound() {
clearTimeout(cpuTimer);
state.board = Array(9).fill(null);
state.turn = 'X';
state.gameOver = false;
App.clearBoard();
App.renderStatus('Your turn', 'normal');
}
function resetScores() {
state.scores = { you: 0, opponent: 0, draws: 0 };
persistScores();
App.renderScores(state.scores);
App.showToast('Scores reset');
}
function destroy() {
clearTimeout(cpuTimer);
}
function init() {
clearTimeout(cpuTimer);
App.clearBoard();
state.board = Array(9).fill(null);
state.turn = 'X';
state.gameOver = false;
loadScores();
App.renderScores(state.scores);
App.renderStatus('Your turn', 'normal');
}
return { init, handleMove, newRound, resetScores, destroy };
})();