MarkTheArtist's picture
Give the ability to change the look so have multiple themes and have the ability to change between them
d10df16 verified
document.addEventListener('DOMContentLoaded', () => {
// Game state
let board = ['', '', '', '', '', '', '', '', ''];
let currentPlayer = 'X';
let gameActive = true;
let gameMode = 'pvp'; // pvp, pvc, cvc
let scores = { x: 0, o: 0, d: 0 };
let theme = 'default';
// DOM elements
const cells = document.querySelectorAll('#game-board .cell');
const gameOverlay = document.getElementById('game-overlay');
const gameResult = document.getElementById('game-result');
const playAgainBtn = document.getElementById('play-again-btn');
const xWinsEl = document.getElementById('x-wins');
const oWinsEl = document.getElementById('o-wins');
const drawsEl = document.getElementById('draws');
const gameBoard = document.getElementById('game-board');
// Initialize the game board
function initializeBoard() {
gameBoard.innerHTML = '';
for (let i = 0; i < 9; i++) {
const cell = document.createElement('div');
cell.classList.add('cell', 'flex', 'items-center', 'justify-center', 'text-6xl', 'font-bold', 'bg-white', 'dark:bg-gray-800', 'rounded-lg', 'shadow-md');
cell.dataset.index = i;
cell.addEventListener('click', () => handleCellClick(i));
gameBoard.appendChild(cell);
}
}
// Handle cell click
function handleCellClick(index) {
if (!gameActive || board[index] !== '') return;
makeMove(index, currentPlayer);
if (gameMode === 'pvc' && currentPlayer === 'O' && gameActive) {
setTimeout(() => {
const bestMove = findBestMove();
makeMove(bestMove, 'O');
}, 500);
} else if (gameMode === 'cvc' && gameActive) {
setTimeout(() => {
const bestMove = findBestMove();
makeMove(bestMove, currentPlayer);
}, 500);
}
}
// Make a move
function makeMove(index, player) {
board[index] = player;
const cell = gameBoard.children[index];
cell.textContent = player;
cell.classList.add(player.toLowerCase());
if (checkWin(player)) {
endGame(`${player} Wins!`);
scores[player.toLowerCase()]++;
updateScoreboard();
highlightWinningCells();
return;
}
if (checkDraw()) {
endGame("It's a Draw!");
scores.d++;
updateScoreboard();
return;
}
currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
// If it's computer's turn in pvc mode and player is O
if (gameMode === 'pvc' && currentPlayer === 'O' && gameActive) {
setTimeout(() => {
const bestMove = findBestMove();
makeMove(bestMove, 'O');
}, 500);
}
}
// Check for win
function checkWin(player) {
const winPatterns = [
[0, 1, 2], [3, 4, 5], [6, 7, 8], // rows
[0, 3, 6], [1, 4, 7], [2, 5, 8], // columns
[0, 4, 8], [2, 4, 6] // diagonals
];
return winPatterns.some(pattern => {
return pattern.every(index => board[index] === player);
});
}
// Check for draw
function checkDraw() {
return board.every(cell => cell !== '');
}
// End the game
function endGame(message) {
gameActive = false;
gameResult.textContent = message;
gameOverlay.classList.remove('hidden');
}
// Reset the game
function resetGame() {
board = ['', '', '', '', '', '', '', '', ''];
currentPlayer = 'X';
gameActive = true;
gameOverlay.classList.add('hidden');
Array.from(gameBoard.children).forEach(cell => {
cell.textContent = '';
cell.classList.remove('x', 'o', 'winning-cell');
});
if (gameMode === 'cvc') {
setTimeout(() => {
const bestMove = findBestMove();
makeMove(bestMove, 'X');
}, 500);
} else if (gameMode === 'pvc' && currentPlayer === 'O') {
setTimeout(() => {
const bestMove = findBestMove();
makeMove(bestMove, 'O');
}, 500);
}
}
// Update scoreboard
function updateScoreboard() {
xWinsEl.textContent = scores.x;
oWinsEl.textContent = scores.o;
drawsEl.textContent = scores.d;
}
// Highlight winning cells
function highlightWinningCells() {
const winPatterns = [
[0, 1, 2], [3, 4, 5], [6, 7, 8], // rows
[0, 3, 6], [1, 4, 7], [2, 5, 8], // columns
[0, 4, 8], [2, 4, 6] // diagonals
];
for (const pattern of winPatterns) {
const [a, b, c] = pattern;
if (board[a] !== '' && board[a] === board[b] && board[a] === board[c]) {
gameBoard.children[a].classList.add('winning-cell');
gameBoard.children[b].classList.add('winning-cell');
gameBoard.children[c].classList.add('winning-cell');
break;
}
}
}
// Computer AI - Minimax algorithm
function findBestMove() {
if (gameMode === 'cvc') {
// For computer vs computer, just pick a random empty spot for demo
const emptyCells = board.map((cell, index) => cell === '' ? index : null).filter(val => val !== null);
return emptyCells[Math.floor(Math.random() * emptyCells.length)];
}
// Minimax algorithm for optimal play
let bestScore = -Infinity;
let bestMove = null;
for (let i = 0; i < 9; i++) {
if (board[i] === '') {
board[i] = 'O';
const score = minimax(board, 0, false);
board[i] = '';
if (score > bestScore) {
bestScore = score;
bestMove = i;
}
}
}
return bestMove;
}
function minimax(board, depth, isMaximizing) {
const scores = {
'X': -10,
'O': 10,
'draw': 0
};
if (checkWin('O')) return scores['O'] - depth;
if (checkWin('X')) return scores['X'] + depth;
if (checkDraw()) return scores['draw'];
if (isMaximizing) {
let bestScore = -Infinity;
for (let i = 0; i < 9; i++) {
if (board[i] === '') {
board[i] = 'O';
const score = minimax(board, depth + 1, false);
board[i] = '';
bestScore = Math.max(score, bestScore);
}
}
return bestScore;
} else {
let bestScore = Infinity;
for (let i = 0; i < 9; i++) {
if (board[i] === '') {
board[i] = 'X';
const score = minimax(board, depth + 1, true);
board[i] = '';
bestScore = Math.min(score, bestScore);
}
}
return bestScore;
}
}
// Event listeners
playAgainBtn.addEventListener('click', resetGame);
// Theme change handler
document.addEventListener('theme-change', (e) => {
theme = e.detail.theme;
applyTheme();
});
// Game mode change handler
document.addEventListener('game-mode-change', (e) => {
gameMode = e.detail.mode;
resetGame();
});
// Apply theme
function applyTheme() {
// Remove all theme classes first
const themeClasses = Array.from(document.body.classList).filter(cls => cls.startsWith('theme-'));
themeClasses.forEach(cls => document.body.classList.remove(cls));
// Add the current theme class
document.body.classList.add(`theme-${theme}`);
// Update CSS variables
document.documentElement.style.setProperty('--primary-color', getComputedStyle(document.body).getPropertyValue('--primary-color'));
document.documentElement.style.setProperty('--secondary-color', getComputedStyle(document.body).getPropertyValue('--secondary-color'));
// Update cell colors
document.querySelectorAll('.cell.x').forEach(cell => {
cell.style.color = `var(--primary-color)`;
});
document.querySelectorAll('.cell.o').forEach(cell => {
cell.style.color = `var(--secondary-color)`;
});
}
// Initialize
initializeBoard();
applyTheme();
});