Royal Tic Tac Toe
by MysticPanther23407 lines11.5 KB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Royal Tic Tac Toe</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700;900&family=Cinzel+Decorative:wght@700;900&display=swap');
- { margin: 0; padding: 0; box-sizing: border-box; }
body {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-family: ‘Cinzel’, ‘Palatino Linotype’, ‘Georgia’, serif;
background: linear-gradient(160deg, #1a1a2e 0%, #16213e 40%, #0f3460 100%);
color: #e8d5b7;
padding: 24px;
position: relative;
overflow: hidden;
user-select: none;
}
.bg-texture {
position: absolute; inset: 0; opacity: 0.04; pointer-events: none;
background-image: url(“data:image/svg+xml,%3Csvg width=‘60’ height=‘60’ viewBox=‘0 0 60 60’ xmlns=‘http://www.w3.org/2000/svg’%3E%3Cg fill=‘none’ fill-rule=‘evenodd’%3E%3Cg fill=’%23e8d5b7’%3E%3Cpath d=‘M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z’/%3E%3C/g%3E%3C/g%3E%3C/svg%3E”);
}
h1 {
font-family: ‘Cinzel Decorative’, ‘Cinzel’, serif;
font-size: clamp(1.6rem, 5vw, 2.6rem);
font-weight: 900;
letter-spacing: 0.08em;
text-align: center;
margin: 0 0 4px;
background: linear-gradient(180deg, #f4e4c1 0%, #c9a84c 50%, #8b6914 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.5));
}
.subtitle {
font-size: clamp(0.65rem, 2vw, 0.8rem);
letter-spacing: 0.25em;
text-transform: uppercase;
opacity: 0.5;
margin: 0 0 28px;
}
.scoreboard {
display: flex;
gap: 24px;
margin-bottom: 24px;
font-size: 0.85rem;
letter-spacing: 0.05em;
}
.score-item { text-align: center; }
.score-label { opacity: 0.6; font-size: 0.7rem; margin-bottom: 4px; }
.score-val { font-size: 1.4rem; font-weight: 700; }
.score-val.x { color: #e74c3c; }
.score-val.o { color: #3498db; }
.score-val.d { color: #e8d5b7; }
.status {
font-size: clamp(0.85rem, 2.5vw, 1.05rem);
margin-bottom: 20px;
font-weight: 700;
min-height: 1.5em;
}
.status.winner { animation: shimmer 1.5s ease infinite; }
.status.x-turn { color: #e8d5b7; }
.status.o-turn { color: #e8d5b7; }
.status.x-win { color: #e74c3c; }
.status.o-win { color: #3498db; }
.status.draw { color: #e8d5b7; }
.board {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 6px;
width: min(340px, 80vw);
aspect-ratio: 1;
background: linear-gradient(135deg, #c9a84c, #8b6914);
padding: 6px;
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0,0,0,0.4), inset 0 1px 0 rgba(255,255,255,0.1);
}
.board.win-pulse { animation: winGlow 0.8s ease; }
.cell {
background: linear-gradient(135deg, #1e2a3a, #152238);
border: none;
border-radius: 8px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
box-shadow: inset 0 2px 4px rgba(0,0,0,0.3);
aspect-ratio: 1;
}
.cell:hover:not(.filled):not(.game-over) {
background: linear-gradient(135deg, #243447, #1a2d42);
}
.cell.filled, .cell.game-over { cursor: default; }
.cell.win-cell {
background: linear-gradient(135deg, #2c3e50, #1a2634);
box-shadow: inset 0 0 20px rgba(218,165,32,0.25), 0 0 12px rgba(218,165,32,0.15);
}
.cell svg { width: 60%; height: 60%; }
.new-game {
margin-top: 28px;
padding: 12px 36px;
font-family: ‘Cinzel’, serif;
font-size: 0.85rem;
font-weight: 700;
letter-spacing: 0.15em;
text-transform: uppercase;
color: #e8d5b7;
background: transparent;
border: 2px solid rgba(201,168,76,0.4);
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
}
.new-game:hover {
border-color: rgba(201,168,76,0.8);
background: rgba(201,168,76,0.1);
}
.taunt {
font-size: clamp(0.7rem, 2vw, 0.85rem);
font-style: italic;
opacity: 0;
margin-top: 0;
height: 0;
transition: all 0.4s ease;
color: #c9a84c;
text-align: center;
}
.taunt.show {
opacity: 0.8;
margin-top: 8px;
height: 1.5em;
}
.confetti-piece {
position: fixed;
width: 8px;
height: 8px;
pointer-events: none;
z-index: 999;
border-radius: 2px;
}
@keyframes confettiFall {
0% { transform: translateY(0) rotate(0deg) scale(1); opacity: 1; }
100% { transform: translateY(105vh) rotate(720deg) scale(0.3); opacity: 0; }
}
@keyframes drawLine {
to { stroke-dashoffset: 0; }
}
@keyframes fadeIn {
from { opacity: 0; transform: scale(0.9); }
to { opacity: 1; transform: scale(1); }
}
@keyframes shimmer {
0%, 100% { opacity: 0.6; }
50% { opacity: 1; }
}
@keyframes winGlow {
0%, 100% { box-shadow: 0 0 15px rgba(218,165,32,0.3); }
50% { box-shadow: 0 0 35px rgba(218,165,32,0.7); }
}
</style>
</head>
<body>
<div class="bg-texture"></div>
<h1>✦ Tic Tac Toe ✦</h1>
<p class="subtitle">A Noble Contest of Wits</p>
<div class="scoreboard">
<div class="score-item">
<div class="score-label">⚔️ Swords</div>
<div class="score-val x" id="scoreX">0</div>
</div>
<div class="score-item">
<div class="score-label">Stalemates</div>
<div class="score-val d" id="scoreD">0</div>
</div>
<div class="score-item">
<div class="score-label">🛡️ Shields</div>
<div class="score-val o" id="scoreO">0</div>
</div>
</div>
<div class="status x-turn" id="status">⚔️ Swords to move</div>
<div class="taunt" id="taunt"></div>
<div class="board" id="board"></div>
<button class="new-game" id="newGame">New Bout</button>
<script>
const WINNING_COMBOS = [
[0,1,2],[3,4,5],[6,7,8],
[0,3,6],[1,4,7],[2,5,8],
[0,4,8],[2,4,6]
];
let board = Array(9).fill(null);
let xIsNext = true;
let gameOver = false;
let scores = { X: 0, O: 0, draws: 0 };
const boardEl = document.getElementById('board');
const statusEl = document.getElementById('status');
const scoreXEl = document.getElementById('scoreX');
const scoreOEl = document.getElementById('scoreO');
const scoreDEl = document.getElementById('scoreD');
const newGameBtn = document.getElementById('newGame');
const tauntEl = document.getElementById('taunt');
const WIN_TAUNTS = [
"Huzzah! A triumph most glorious!",
"The court jester weeps for the vanquished.",
"Let the bards sing of this conquest!",
"By the crown, what a devastating blow!",
"The throne room erupts in applause!",
"Kneel before the victor, ye peasants!",
"A masterful campaign, Your Majesty!",
"The defeated shall polish the winner's armor.",
];
const DRAW_TAUNTS = [
"Neither lord nor lady yields... curious.",
"The knights have fought to a standstill!",
"The kingdom is divided — as is this game.",
"Even the royal cat seems unimpressed.",
"A truce! ...for now.",
"The court scribe records: 'twas a bore.",
];
function pickRandom(arr) { return arr[Math.floor(Math.random() * arr.length)]; }
function spawnConfetti() {
const colors = ['#c9a84c','#f4e4c1','#e74c3c','#3498db','#8b6914','#f1c40f'];
for (let i = 0; i < 40; i++) {
const piece = document.createElement('div');
piece.className = 'confetti-piece';
piece.style.left = Math.random() * 100 + 'vw';
piece.style.top = -10 + 'px';
piece.style.background = colors[Math.floor(Math.random() * colors.length)];
piece.style.width = (4 + Math.random() * 8) + 'px';
piece.style.height = (4 + Math.random() * 8) + 'px';
piece.style.animation = `confettiFall ${1.5 + Math.random() * 2}s ease-out ${Math.random() * 0.5}s forwards`;
document.body.appendChild(piece);
setTimeout(() => piece.remove(), 4000);
}
}
function showTaunt(isDraw) {
tauntEl.textContent = isDraw ? pickRandom(DRAW_TAUNTS) : pickRandom(WIN_TAUNTS);
tauntEl.classList.add('show');
}
function hideTaunt() {
tauntEl.classList.remove('show');
}
function createCross(animate) {
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('viewBox', '0 0 100 100');
const l1 = document.createElementNS('http://www.w3.org/2000/svg', 'line');
l1.setAttribute('x1','18'); l1.setAttribute('y1','18');
l1.setAttribute('x2','82'); l1.setAttribute('y2','82');
l1.setAttribute('stroke','#c0392b'); l1.setAttribute('stroke-width','12');
l1.setAttribute('stroke-linecap','round');
const l2 = document.createElementNS('http://www.w3.org/2000/svg', 'line');
l2.setAttribute('x1','82'); l2.setAttribute('y1','18');
l2.setAttribute('x2','18'); l2.setAttribute('y2','82');
l2.setAttribute('stroke','#c0392b'); l2.setAttribute('stroke-width','12');
l2.setAttribute('stroke-linecap','round');
if (animate) {
l1.style.strokeDasharray = '91';
l1.style.strokeDashoffset = '91';
l1.style.animation = 'drawLine 0.35s ease forwards';
l2.style.strokeDasharray = '91';
l2.style.strokeDashoffset = '91';
l2.style.animation = 'drawLine 0.35s 0.15s ease forwards';
}
svg.appendChild(l1); svg.appendChild(l2);
return svg;
}
function createCircle(animate) {
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('viewBox', '0 0 100 100');
const c = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
c.setAttribute('cx','50'); c.setAttribute('cy','50'); c.setAttribute('r','34');
c.setAttribute('fill','none'); c.setAttribute('stroke','#2471a3');
c.setAttribute('stroke-width','12'); c.setAttribute('stroke-linecap','round');
if (animate) {
c.style.strokeDasharray = '214';
c.style.strokeDashoffset = '214';
c.style.animation = 'drawLine 0.45s ease forwards';
}
svg.appendChild(c);
return svg;
}
function checkWinner() {
for (const [a, b, c] of WINNING_COMBOS) {
if (board[a] && board[a] === board[b] && board[a] === board[c]) {
return { winner: board[a], line: [a, b, c] };
}
}
return board.every(Boolean) ? { winner: 'draw' } : null;
}
function updateStatus() {
scoreXEl.textContent = scores.X;
scoreOEl.textContent = scores.O;
scoreDEl.textContent = scores.draws;
}
function handleClick(i) {
if (board[i] || gameOver) return;
board[i] = xIsNext ? 'X' : 'O';
xIsNext = !xIsNext;
const cell = boardEl.children[i];
cell.classList.add('filled');
cell.style.animation = 'fadeIn 0.2s ease';
cell.appendChild(board[i] === 'X' ? createCross(true) : createCircle(true));
const result = checkWinner();
if (result) {
gameOver = true;
document.querySelectorAll('.cell').forEach(c => c.classList.add('game-over'));
if (result.winner === 'draw') {
scores.draws++;
statusEl.textContent = "A draw! The realm rests uneasy.";
statusEl.className = 'status draw';
showTaunt(true);
} else {
scores[result.winner]++;
const name = result.winner === 'X' ? '⚔️ Swords' : '🛡️ Shields';
statusEl.textContent = `${name} claim victory!`;
statusEl.className = `status winner ${result.winner.toLowerCase()}-win`;
boardEl.classList.add('win-pulse');
result.line.forEach(idx => boardEl.children[idx].classList.add('win-cell'));
spawnConfetti();
showTaunt(false);
}
updateStatus();
} else {
const turn = xIsNext ? '⚔️ Swords' : '🛡️ Shields';
statusEl.textContent = `${turn} to move`;
statusEl.className = `status ${xIsNext ? 'x' : 'o'}-turn`;
}
}
function buildBoard() {
boardEl.innerHTML = '';
boardEl.classList.remove('win-pulse');
for (let i = 0; i < 9; i++) {
const cell = document.createElement('button');
cell.className = 'cell';
cell.addEventListener('click', () => handleClick(i));
boardEl.appendChild(cell);
}
}
function reset() {
board = Array(9).fill(null);
xIsNext = true;
gameOver = false;
statusEl.textContent = '⚔️ Swords to move';
statusEl.className = 'status x-turn';
hideTaunt();
buildBoard();
}
newGameBtn.addEventListener('click', reset);
buildBoard();
</script>
</body>
</html>Game Source: Royal Tic Tac Toe
Creator: MysticPanther23
Libraries: none
Complexity: complex (407 lines, 11.5 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 KIDHUBB header at the top with "remix_of: royal-tic-tac-toe-mysticpanther23" to link back to the original. Then publish at kidhubb.com/publish.