๐ Christmas Tic Tac Toe ๐
by SonicBear35629 lines23.0 KB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>๐ Christmas Tic Tac Toe ๐
</title>
<style>
* {
box-sizing: border-box;
user-select: none;
-webkit-tap-highlight-color: transparent;
}
body {
margin: 0;
min-height: 100vh;
font-family: 'Segoe UI', 'Arial', system-ui, sans-serif;
touch-action: manipulation;
overflow: hidden;
background: #0a1a0a;
}
.video-bg {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 0;
object-fit: cover;
background: #0a1a0a;
}
.snow-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 1;
background: radial-gradient(ellipse at center, transparent 60%, rgba(0,0,0,0.3) 100%);
}
.game-wrapper {
position: relative;
z-index: 2;
background: rgba(10, 26, 10, 0.85);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
padding: 20px 16px 26px;
border-radius: 60px 60px 40px 40px;
box-shadow: 0 20px 40px rgba(0,0,0,0.9), inset 0 0 60px rgba(255, 200, 100, 0.05);
max-width: 500px;
width: 100%;
margin: 12px auto;
border: 2px solid rgba(42, 90, 42, 0.6);
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 4px 14px 4px;
color: #f0e8d5;
font-weight: bold;
flex-wrap: wrap;
gap: 8px;
border-bottom: 2px solid rgba(42, 90, 42, 0.5);
padding-bottom: 12px;
}
.title {
font-size: 1.4rem;
background: linear-gradient(90deg, #ff4444, #ff6b6b, #ffd700, #ff6b6b, #ff4444);
background-size: 200% auto;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: shimmer 3s linear infinite;
font-weight: 900;
letter-spacing: 1px;
}
@keyframes shimmer {
0% { background-position: 0% center; }
100% { background-position: 200% center; }
}
.turn-indicator {
background: rgba(13, 31, 13, 0.8);
padding: 4px 14px;
border-radius: 40px;
box-shadow: inset 0 2px 8px rgba(26, 58, 26, 0.5);
font-size: 1.1rem;
display: flex;
align-items: center;
gap: 8px;
border: 1px solid rgba(42, 90, 42, 0.5);
color: #f0e8d5;
}
.turn-indicator span {
color: #ffd700;
}
.score-display {
display: flex;
gap: 10px;
background: rgba(13, 31, 13, 0.8);
padding: 4px 12px;
border-radius: 40px;
border: 1px solid rgba(42, 90, 42, 0.5);
box-shadow: inset 0 2px 8px rgba(26, 58, 26, 0.5);
}
.score-item {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.95rem;
color: #c8d8c8;
}
.score-item .score-num {
color: #ffd700;
font-weight: bold;
}
.mode-selector {
display: flex;
justify-content: center;
gap: 10px;
margin: 12px 0 14px 0;
flex-wrap: wrap;
}
.mode-btn {
background: rgba(13, 31, 13, 0.7);
border: 2px solid rgba(42, 90, 42, 0.5);
color: #8aaa8a;
padding: 6px 16px;
border-radius: 30px;
font-size: 0.85rem;
font-weight: bold;
cursor: pointer;
transition: all 0.25s ease;
touch-action: manipulation;
box-shadow: 0 2px 0 rgba(10, 26, 10, 0.5);
}
.mode-btn.active {
background: rgba(42, 74, 42, 0.8);
border-color: #ffd700;
color: #ffd700;
box-shadow: 0 0 25px rgba(255, 215, 0, 0.15);
}
.mode-btn:active {
transform: scale(0.95);
}
.board {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
aspect-ratio: 1 / 1;
width: 100%;
background: rgba(13, 31, 13, 0.6);
border-radius: 32px;
padding: 12px;
box-shadow: inset 0 0 0 3px rgba(42, 90, 42, 0.5), 0 8px 0 rgba(10, 26, 10, 0.5);
}
.cell {
background: rgba(26, 48, 26, 0.7);
border-radius: 18px;
display: flex;
align-items: center;
justify-content: center;
font-size: 3.5rem;
font-weight: bold;
cursor: pointer;
transition: all 0.15s ease;
touch-action: manipulation;
box-shadow: inset 0 -4px 0 rgba(10, 26, 10, 0.5);
border: 1px solid rgba(42, 90, 42, 0.4);
aspect-ratio: 1 / 1;
color: #f0e8d5;
}
.cell:active {
transform: scale(0.92);
}
.cell.x {
color: #5fc3e4;
text-shadow: 0 0 30px rgba(95, 195, 228, 0.4);
}
.cell.o {
color: #ffd700;
text-shadow: 0 0 30px rgba(255, 215, 0, 0.4);
}
.cell.win-cell {
background: rgba(42, 90, 42, 0.7);
box-shadow: 0 0 40px rgba(255, 215, 0, 0.2), inset 0 0 30px rgba(255, 215, 0, 0.05);
border-color: #ffd700;
}
.cell.draw-cell {
background: rgba(58, 42, 42, 0.6);
border-color: rgba(138, 106, 106, 0.5);
}
.cell.disabled {
cursor: default;
}
.cell:not(.disabled):hover {
background: rgba(42, 74, 42, 0.6);
border-color: rgba(58, 122, 58, 0.6);
}
.controls {
display: flex;
justify-content: center;
gap: 14px;
margin-top: 16px;
padding: 4px 0;
flex-wrap: wrap;
}
.ctrl-btn {
background: linear-gradient(145deg, rgba(42, 74, 42, 0.8), rgba(26, 58, 26, 0.8));
border: none;
border-bottom: 6px solid rgba(10, 26, 10, 0.6);
color: #f0e8d5;
font-size: 1rem;
padding: 10px 22px;
border-radius: 40px;
box-shadow: 0 4px 0 rgba(10, 26, 10, 0.5);
transition: all 0.06s ease;
touch-action: manipulation;
cursor: pointer;
font-weight: bold;
min-width: 80px;
text-align: center;
border: 1px solid rgba(58, 106, 58, 0.4);
}
.ctrl-btn:active {
transform: translateY(4px);
border-bottom-width: 2px;
}
.ctrl-btn.reset-btn {
background: linear-gradient(145deg, rgba(74, 42, 42, 0.8), rgba(58, 26, 26, 0.8));
border-color: rgba(106, 58, 58, 0.5);
color: #ffdbb5;
}
.message {
text-align: center;
font-size: 1.1rem;
font-weight: bold;
color: #ffd700;
margin-top: 10px;
min-height: 2.2rem;
padding: 0 6px;
text-shadow: 0 0 20px rgba(255, 215, 0, 0.15);
}
.cup-display {
display: flex;
justify-content: center;
gap: 12px;
margin-top: 8px;
padding: 6px;
background: rgba(13, 31, 13, 0.5);
border-radius: 20px;
border: 1px solid rgba(42, 90, 42, 0.3);
}
.cup-item {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.9rem;
color: #c8d8c8;
}
.cup-item .cup-emoji {
font-size: 1.4rem;
}
.cup-item .cup-score {
color: #ffd700;
font-weight: bold;
}
.cup-item.winner {
background: rgba(255, 215, 0, 0.15);
padding: 2px 12px;
border-radius: 20px;
border: 1px solid #ffd700;
}
@media (max-width: 480px) {
.cell { font-size: 2.8rem; }
.header { font-size: 0.85rem; }
.turn-indicator { font-size: 0.95rem; padding: 4px 10px; }
.ctrl-btn { padding: 8px 14px; font-size: 0.85rem; min-width: 65px; }
.title { font-size: 1.1rem; }
.mode-btn { padding: 5px 12px; font-size: 0.75rem; }
.game-wrapper { padding: 14px 12px 20px; }
.cup-item { font-size: 0.8rem; }
.cup-item .cup-emoji { font-size: 1.1rem; }
}
@media (max-width: 380px) {
.cell { font-size: 2.2rem; }
.board { gap: 6px; padding: 8px; }
}
</style>
</head>
<body>
<!-- Video Background -->
<video class="video-bg" id="bgVideo" autoplay loop muted playsinline>
<source src="https://assets.mixkit.co/videos/preview/mixkit-snowy-christmas-tree-and-lights-3287-large.mp4" type="video/mp4">
</video>
<div class="snow-overlay"></div>
<div class="game-wrapper">
<div class="header">
<div class="title">๐ X & O ยท Christmas ๐
</div>
<div class="turn-indicator">
๐ฏ <span id="turnDisplay">X's turn</span>
</div>
<div class="score-display">
<div class="score-item">โ <span class="score-num" id="scoreX">0</span></div>
<div class="score-item">โญ <span class="score-num" id="scoreO">0</span></div>
<div class="score-item">๐ <span class="score-num" id="scoreDraw">0</span></div>
</div>
</div>
<div class="mode-selector">
<button class="mode-btn active" id="modePlayer" data-mode="player">๐ฅ 2 Players</button>
<button class="mode-btn" id="modeAI" data-mode="ai">๐ vs AI</button>
</div>
<div class="board" id="board">
<div class="cell" data-index="0"></div>
<div class="cell" data-index="1"></div>
<div class="cell" data-index="2"></div>
<div class="cell" data-index="3"></div>
<div class="cell" data-index="4"></div>
<div class="cell" data-index="5"></div>
<div class="cell" data-index="6"></div>
<div class="cell" data-index="7"></div>
<div class="cell" data-index="8"></div>
</div>
<div id="messageBox" class="message">๐ X goes first! Merry Christmas! ๐</div>
<div class="cup-display" id="cupDisplay">
<div class="cup-item" id="cupX">๐ <span class="cup-emoji">โ</span> <span class="cup-score" id="cupScoreX">0</span></div>
<div class="cup-item" id="cupO">๐ <span class="cup-emoji">โญ</span> <span class="cup-score" id="cupScoreO">0</span></div>
</div>
<div class="controls">
<button class="ctrl-btn reset-btn" id="resetBtn">๐ New Game ๐
</button>
<button class="ctrl-btn reset-btn" id="resetCupBtn">๐ Reset Cup</button>
</div>
</div>
<script>
(function(){
// ---------- DOM Elements ----------
const cells = document.querySelectorAll('.cell');
const turnDisplay = document.getElementById('turnDisplay');
const messageBox = document.getElementById('messageBox');
const scoreX = document.getElementById('scoreX');
const scoreO = document.getElementById('scoreO');
const scoreDraw = document.getElementById('scoreDraw');
const resetBtn = document.getElementById('resetBtn');
const resetCupBtn = document.getElementById('resetCupBtn');
const modePlayer = document.getElementById('modePlayer');
const modeAI = document.getElementById('modeAI');
const cupScoreX = document.getElementById('cupScoreX');
const cupScoreO = document.getElementById('cupScoreO');
const cupX = document.getElementById('cupX');
const cupO = document.getElementById('cupO');
// ---------- Game State ----------
let board = ['', '', '', '', '', '', '', '', ''];
let currentPlayer = 'X';
let gameActive = true;
let winner = null;
let scores = { X: 0, O: 0, draw: 0 };
let cupScores = { X: 0, O: 0 };
let gameMode = 'player'; // 'player' or 'ai'
let aiThinking = false;
const winPatterns = [
[0, 1, 2], [3, 4, 5], [6, 7, 8],
[0, 3, 6], [1, 4, 7], [2, 5, 8],
[0, 4, 8], [2, 4, 6]
];
const christmasMessages = [
'๐ Merry Christmas! ๐', '๐
Ho Ho Ho! ๐
',
'โ Let it snow! โ', '๐ Star of wonder! ๐',
'๐ Gift of victory! ๐', '๐ฆ Dasher says hi! ๐ฆ',
'โจ Magical move! โจ', 'โ๏ธ Winter wonderland! โ๏ธ',
'๐ Jingle bells! ๐', 'โ๏ธ Frosty is happy! โ๏ธ',
'๐๏ธ Peace on Earth! ๐๏ธ'
];
function getRandomChristmasMessage() {
return christmasMessages[Math.floor(Math.random() * christmasMessages.length)];
}
// ---------- Cup System ----------
function updateCupDisplay() {
cupScoreX.textContent = cupScores.X;
cupScoreO.textContent = cupScores.O;
cupX.classList.toggle('winner', cupScores.X >= 2);
cupO.classList.toggle('winner', cupScores.O >= 2);
if (cupScores.X >= 2) {
messageBox.textContent = `๐ X WINS THE CUP! ๐ ${getRandomChristmasMessage()}`;
} else if (cupScores.O >= 2) {
messageBox.textContent = `๐ O WINS THE CUP! ๐ ${getRandomChristmasMessage()}`;
}
}
function resetCup() {
cupScores = { X: 0, O: 0 };
updateCupDisplay();
if (!cupScores.X >= 2 && !cupScores.O >= 2) {
messageBox.textContent = `๐ Cup reset! First to 2 wins! ๐`;
}
}
function awardCupPoint(player) {
cupScores[player]++;
updateCupDisplay();
if (cupScores[player] >= 2) {
messageBox.textContent = `๐ ${player} WINS THE CUP! ๐ ${getRandomChristmasMessage()}`;
}
}
// ---------- Render ----------
function renderBoard() {
cells.forEach((cell, index) => {
cell.textContent = board[index];
cell.className = 'cell';
if (board[index] === 'X') cell.classList.add('x');
if (board[index] === 'O') cell.classList.add('o');
if (!gameActive || board[index] !== '') {
cell.classList.add('disabled');
} else {
cell.classList.remove('disabled');
}
});
if (winner && winner !== 'draw') {
const winPattern = winPatterns.find(pattern => {
const [a, b, c] = pattern;
return board[a] === winner && board[b] === winner && board[c] === winner;
});
if (winPattern) {
winPattern.forEach(idx => cells[idx].classList.add('win-cell'));
}
} else if (winner === 'draw') {
cells.forEach(cell => cell.classList.add('draw-cell'));
}
// Update turn display
if (!gameActive) {
if (winner === 'draw') {
turnDisplay.textContent = "๐ค Draw! ๐";
} else if (winner) {
turnDisplay.textContent = `๐ ${winner} wins! โญ`;
} else {
turnDisplay.textContent = `${currentPlayer}'s turn`;
}
} else {
turnDisplay.textContent = `${currentPlayer}'s turn`;
}
// Update scores
scoreX.textContent = scores.X;
scoreO.textContent = scores.O;
scoreDraw.textContent = scores.draw;
updateCupDisplay();
// Update message if not already showing cup win
if (!gameActive && winner && winner !== 'draw' && !cupScores.X >= 2 && !cupScores.O >= 2) {
messageBox.textContent = `๐ ${winner} wins! ${getRandomChristmasMessage()}`;
} else if (!gameActive && winner === 'draw') {
messageBox.textContent = "๐ค It's a draw! Click New Game ๐
";
} else if (gameActive) {
messageBox.textContent = `${currentPlayer}'s turn ยท ${getRandomChristmasMessage()}`;
}
}
// ---------- Game Logic ----------
function checkWinner() {
for (let pattern of winPatterns) {
const [a, b, c] = pattern;
if (board[a] && board[a] === board[b] && board[a] === board[c]) {
return board[a];
}
}
if (board.every(cell => cell !== '')) return 'draw';
return null;
}
function makeMove(index) {
if (!gameActive || board[index] !== '' || aiThinking) return false;
board[index] = currentPlayer;
const result = checkWinner();
if (result) {
gameActive = false;
winner = result;
if (result === 'draw') {
scores.draw++;
} else {
scores[result]++;
awardCupPoint(result);
}
renderBoard();
return true;
}
currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
renderBoard();
// Trigger AI if it's O's turn and AI mode
if (gameActive && gameMode === 'ai' && currentPlayer === 'O') {
scheduleAI();
}
return true;
}
// ---------- AI ----------
function scheduleAI() {
if (aiThinking || !gameActive || currentPlayer !== 'O' || gameMode !== 'ai') return;
aiThinking = true;
setTimeout(() => {
if (!gameActive || currentPlayer !== 'O') {
aiThinking = false;
return;
}
const empty = board.map((v, i) => v === '' ? i : null).filter(v => v !== null);
if (empty.length === 0) { aiThinking = false; return; }
// Easy AI: random move
const move = empty[Math.floor(Math.random() * empty.length)];
if (move !== null && board[move] === '') {
board[move] = 'O';
const result = checkWinner();
if (result) {
gameActive = false;
winner = result;
if (result === 'draw') {
scores.draw++;
} else {
scores[result]++;
awardCupPoint(result);
}
renderBoard();
aiThinking = false;
return;
}
currentPlayer = 'X';
renderBoard();
}
aiThinking = false;
}, 250);
}
// ---------- Reset ----------
function resetGame() {
board = ['', '', '', '', '', '', '', '', ''];
currentPlayer = 'X';
gameActive = true;
winner = null;
aiThinking = false;
renderBoard();
if (!cupScores.X >= 2 && !cupScores.O >= 2) {
messageBox.textContent = `๐ New game! X goes first! ${getRandomChristmasMessage()}`;
}
}
// ---------- Mode switching ----------
function setMode(mode) {
gameMode = mode;
modePlayer.classList.toggle('active', mode === 'player');
modeAI.classList.toggle('active', mode === 'ai');
resetGame();
if (mode === 'player') {
messageBox.textContent = "๐ฅ 2 Players mode ยท Merry Christmas! ๐";
} else {
messageBox.textContent = "๐ vs AI mode ยท You are X, Santa's helper! ๐
";
}
}
// ---------- Events ----------
cells.forEach((cell, index) => {
const handler = (e) => {
e.preventDefault();
if (aiThinking) return;
if (gameMode === 'ai' && currentPlayer === 'O') return; // AI's turn
makeMove(index);
};
cell.addEventListener('click', handler);
cell.addEventListener('touchstart', handler, { passive: false });
});
resetBtn.addEventListener('click', resetGame);
resetCupBtn.addEventListener('click', resetCup);
modePlayer.addEventListener('click', () => setMode('player'));
modeAI.addEventListener('click', () => setMode('ai'));
// Keyboard support
document.addEventListener('keydown', (e) => {
if (e.key === 'r' || e.key === 'R') resetGame();
if (e.key === 'c' || e.key === 'C') resetCup();
if (e.key >= '1' && e.key <= '9') {
const idx = parseInt(e.key) - 1;
if (idx >= 0 && idx < 9 && gameActive) {
if (aiThinking) return;
if (gameMode === 'ai' && currentPlayer === 'O') return;
makeMove(idx);
}
}
});
// ---------- Start ----------
renderBoard();
messageBox.textContent = "๐ X goes first! Merry Christmas! ๐";
})();
</script>
</body>
</html>Game Source: ๐ Christmas Tic Tac Toe ๐
Creator: SonicBear35
Libraries: none
Complexity: complex (629 lines, 23.0 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: christmas-tic-tac-toe-sonicbear35-mqmndyo2" to link back to the original. Then publish at arcadelab.ai/publish.