-
-
Save shricodev/31969dd257e12f0791ab196af7676508 to your computer and use it in GitHub Desktop.
Blog - Chess (Claude Opus 4)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<title>Modern Chess Game</title> | |
<style> | |
* { | |
margin: 0; | |
padding: 0; | |
box-sizing: border-box; | |
} | |
body { | |
font-family: | |
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; | |
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%); | |
min-height: 100vh; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
color: #333; | |
} | |
.game-container { | |
display: flex; | |
gap: 30px; | |
padding: 20px; | |
background: rgba(255, 255, 255, 0.95); | |
border-radius: 20px; | |
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); | |
} | |
.board-section { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
} | |
.timer { | |
width: 100%; | |
padding: 15px; | |
margin-bottom: 15px; | |
background: #f0f0f0; | |
border-radius: 10px; | |
text-align: center; | |
font-size: 24px; | |
font-weight: bold; | |
transition: all 0.3s ease; | |
} | |
.timer.active { | |
background: #4caf50; | |
color: white; | |
transform: scale(1.05); | |
} | |
.timer.warning { | |
background: #ff6b6b; | |
color: white; | |
} | |
.board { | |
display: grid; | |
grid-template-columns: repeat(8, 75px); | |
grid-template-rows: repeat(8, 75px); | |
border: 3px solid #333; | |
border-radius: 8px; | |
overflow: hidden; | |
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); | |
} | |
.square { | |
width: 75px; | |
height: 75px; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
cursor: pointer; | |
position: relative; | |
transition: all 0.2s ease; | |
} | |
.square.light { | |
background: #f0d9b5; | |
} | |
.square.dark { | |
background: #b58863; | |
} | |
.square.selected { | |
background: #7fc97f !important; | |
box-shadow: inset 0 0 0 3px #4caf50; | |
} | |
.square.highlight { | |
background: #fff3b2 !important; | |
} | |
.square.possible-move::before { | |
content: ""; | |
position: absolute; | |
width: 30%; | |
height: 30%; | |
background: rgba(0, 0, 0, 0.2); | |
border-radius: 50%; | |
} | |
.square.possible-capture::before { | |
content: ""; | |
position: absolute; | |
width: 100%; | |
height: 100%; | |
border: 3px solid rgba(255, 0, 0, 0.5); | |
border-radius: 50%; | |
} | |
.piece { | |
font-size: 50px; | |
cursor: grab; | |
user-select: none; | |
transition: transform 0.1s ease; | |
} | |
.piece:hover { | |
transform: scale(1.1); | |
} | |
.piece:active { | |
cursor: grabbing; | |
transform: scale(1.2); | |
} | |
.info-section { | |
display: flex; | |
flex-direction: column; | |
min-width: 280px; | |
} | |
.status { | |
padding: 20px; | |
background: #f8f8f8; | |
border-radius: 10px; | |
margin-bottom: 20px; | |
text-align: center; | |
} | |
.status h2 { | |
margin-bottom: 10px; | |
color: #333; | |
} | |
.turn-indicator { | |
font-size: 18px; | |
font-weight: bold; | |
padding: 10px 20px; | |
background: #4caf50; | |
color: white; | |
border-radius: 25px; | |
display: inline-block; | |
} | |
.moves-container { | |
flex: 1; | |
background: #f8f8f8; | |
border-radius: 10px; | |
padding: 20px; | |
overflow-y: auto; | |
max-height: 400px; | |
} | |
.moves-container h3 { | |
margin-bottom: 15px; | |
color: #333; | |
} | |
.move-list { | |
font-family: "Courier New", monospace; | |
font-size: 14px; | |
line-height: 1.6; | |
} | |
.move-pair { | |
display: grid; | |
grid-template-columns: 30px 1fr 1fr; | |
gap: 10px; | |
margin-bottom: 5px; | |
padding: 5px; | |
border-radius: 5px; | |
transition: background 0.2s; | |
} | |
.move-pair:hover { | |
background: #e0e0e0; | |
} | |
.move-number { | |
font-weight: bold; | |
color: #666; | |
} | |
.button { | |
padding: 12px 30px; | |
background: #4caf50; | |
color: white; | |
border: none; | |
border-radius: 25px; | |
font-size: 16px; | |
font-weight: bold; | |
cursor: pointer; | |
transition: all 0.3s ease; | |
margin-top: 20px; | |
} | |
.button:hover { | |
background: #45a049; | |
transform: translateY(-2px); | |
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); | |
} | |
.game-over-modal { | |
position: fixed; | |
top: 0; | |
left: 0; | |
right: 0; | |
bottom: 0; | |
background: rgba(0, 0, 0, 0.8); | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
z-index: 1000; | |
opacity: 0; | |
visibility: hidden; | |
transition: all 0.3s ease; | |
} | |
.game-over-modal.show { | |
opacity: 1; | |
visibility: visible; | |
} | |
.modal-content { | |
background: white; | |
padding: 40px; | |
border-radius: 20px; | |
text-align: center; | |
transform: scale(0.8); | |
transition: transform 0.3s ease; | |
} | |
.game-over-modal.show .modal-content { | |
transform: scale(1); | |
} | |
.modal-content h2 { | |
font-size: 36px; | |
margin-bottom: 20px; | |
color: #333; | |
} | |
.modal-content p { | |
font-size: 20px; | |
margin-bottom: 30px; | |
color: #666; | |
} | |
@media (max-width: 768px) { | |
.game-container { | |
flex-direction: column; | |
} | |
.board { | |
grid-template-columns: repeat(8, 45px); | |
grid-template-rows: repeat(8, 45px); | |
} | |
.square { | |
width: 45px; | |
height: 45px; | |
} | |
.piece { | |
font-size: 30px; | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<div class="game-container"> | |
<div class="board-section"> | |
<div class="timer" id="blackTimer">⏱ Black: 5:00</div> | |
<div class="board" id="board"></div> | |
<div class="timer" id="whiteTimer">⏱ White: 5:00</div> | |
</div> | |
<div class="info-section"> | |
<div class="status"> | |
<h2>Chess Game</h2> | |
<div class="turn-indicator" id="turnIndicator">White's Turn</div> | |
<div | |
id="checkStatus" | |
style="margin-top: 10px; color: #ff6b6b; font-weight: bold" | |
></div> | |
</div> | |
<div class="moves-container"> | |
<h3>Move History</h3> | |
<div class="move-list" id="moveList"></div> | |
</div> | |
<button class="button" onclick="resetGame()">New Game</button> | |
</div> | |
</div> | |
<div class="game-over-modal" id="gameOverModal"> | |
<div class="modal-content"> | |
<h2 id="gameOverTitle">Game Over</h2> | |
<p id="gameOverMessage"></p> | |
<button class="button" onclick="resetGame()">Play Again</button> | |
</div> | |
</div> | |
<script> | |
// Chess piece Unicode symbols | |
const PIECES = { | |
K: "♔", | |
Q: "♕", | |
R: "♖", | |
B: "♗", | |
N: "♘", | |
P: "♙", | |
k: "♚", | |
q: "♛", | |
r: "♜", | |
b: "♝", | |
n: "♞", | |
p: "♟", | |
}; | |
// Initial board setup | |
const INITIAL_BOARD = [ | |
["r", "n", "b", "q", "k", "b", "n", "r"], | |
["p", "p", "p", "p", "p", "p", "p", "p"], | |
[null, null, null, null, null, null, null, null], | |
[null, null, null, null, null, null, null, null], | |
[null, null, null, null, null, null, null, null], | |
[null, null, null, null, null, null, null, null], | |
["P", "P", "P", "P", "P", "P", "P", "P"], | |
["R", "N", "B", "Q", "K", "B", "N", "R"], | |
]; | |
// Game state | |
let board = []; | |
let currentPlayer = "white"; | |
let selectedSquare = null; | |
let moveHistory = []; | |
let lastMove = null; | |
let whiteTime = 300; // 5 minutes in seconds | |
let blackTime = 300; | |
let timerInterval = null; | |
let gameOver = false; | |
// Initialize the game | |
function initializeGame() { | |
board = INITIAL_BOARD.map((row) => [...row]); | |
renderBoard(); | |
updateStatus(); | |
startTimer(); | |
} | |
// Render the chess board | |
function renderBoard() { | |
const boardElement = document.getElementById("board"); | |
boardElement.innerHTML = ""; | |
for (let row = 0; row < 8; row++) { | |
for (let col = 0; col < 8; col++) { | |
const square = document.createElement("div"); | |
square.className = | |
"square " + ((row + col) % 2 === 0 ? "light" : "dark"); | |
square.dataset.row = row; | |
square.dataset.col = col; | |
square.onclick = () => handleSquareClick(row, col); | |
// Highlight last move | |
if ( | |
lastMove && | |
((lastMove.from.row === row && lastMove.from.col === col) || | |
(lastMove.to.row === row && lastMove.to.col === col)) | |
) { | |
square.classList.add("highlight"); | |
} | |
const piece = board[row][col]; | |
if (piece) { | |
const pieceElement = document.createElement("div"); | |
pieceElement.className = "piece"; | |
pieceElement.textContent = PIECES[piece]; | |
square.appendChild(pieceElement); | |
} | |
boardElement.appendChild(square); | |
} | |
} | |
} | |
// Handle square clicks | |
function handleSquareClick(row, col) { | |
if (gameOver) return; | |
if (selectedSquare) { | |
// Try to make a move | |
if (isValidMove(selectedSquare.row, selectedSquare.col, row, col)) { | |
makeMove(selectedSquare.row, selectedSquare.col, row, col); | |
} | |
clearSelection(); | |
} else { | |
// Select a piece | |
const piece = board[row][col]; | |
if (piece && isPlayerPiece(piece)) { | |
selectSquare(row, col); | |
} | |
} | |
} | |
// Select a square | |
function selectSquare(row, col) { | |
selectedSquare = { row, col }; | |
highlightSelectedSquare(); | |
showPossibleMoves(row, col); | |
} | |
// Clear selection | |
function clearSelection() { | |
selectedSquare = null; | |
document.querySelectorAll(".square").forEach((square) => { | |
square.classList.remove( | |
"selected", | |
"possible-move", | |
"possible-capture", | |
); | |
}); | |
} | |
// Highlight selected square | |
function highlightSelectedSquare() { | |
const squares = document.querySelectorAll(".square"); | |
squares.forEach((square) => { | |
if ( | |
parseInt(square.dataset.row) === selectedSquare.row && | |
parseInt(square.dataset.col) === selectedSquare.col | |
) { | |
square.classList.add("selected"); | |
} | |
}); | |
} | |
// Show possible moves | |
function showPossibleMoves(row, col) { | |
for (let r = 0; r < 8; r++) { | |
for (let c = 0; c < 8; c++) { | |
if (isValidMove(row, col, r, c)) { | |
const square = document.querySelector( | |
`[data-row="${r}"][data-col="${c}"]`, | |
); | |
if (board[r][c]) { | |
square.classList.add("possible-capture"); | |
} else { | |
square.classList.add("possible-move"); | |
} | |
} | |
} | |
} | |
} | |
// Check if a piece belongs to the current player | |
function isPlayerPiece(piece) { | |
return ( | |
(currentPlayer === "white" && piece === piece.toUpperCase()) || | |
(currentPlayer === "black" && piece === piece.toLowerCase()) | |
); | |
} | |
// Check if a move is valid | |
function isValidMove(fromRow, fromCol, toRow, toCol) { | |
const piece = board[fromRow][fromCol]; | |
if (!piece || !isPlayerPiece(piece)) return false; | |
if (fromRow === toRow && fromCol === toCol) return false; | |
const targetPiece = board[toRow][toCol]; | |
if (targetPiece && isPlayerPiece(targetPiece)) return false; | |
// Check piece-specific moves | |
switch (piece.toLowerCase()) { | |
case "p": | |
return isValidPawnMove(fromRow, fromCol, toRow, toCol, piece); | |
case "n": | |
return isValidKnightMove(fromRow, fromCol, toRow, toCol); | |
case "b": | |
return isValidBishopMove(fromRow, fromCol, toRow, toCol); | |
case "r": | |
return isValidRookMove(fromRow, fromCol, toRow, toCol); | |
case "q": | |
return isValidQueenMove(fromRow, fromCol, toRow, toCol); | |
case "k": | |
return isValidKingMove(fromRow, fromCol, toRow, toCol); | |
} | |
return false; | |
} | |
// Pawn move validation | |
function isValidPawnMove(fromRow, fromCol, toRow, toCol, piece) { | |
const direction = piece === piece.toUpperCase() ? -1 : 1; | |
const startRow = piece === piece.toUpperCase() ? 6 : 1; | |
// Forward move | |
if (fromCol === toCol && !board[toRow][toCol]) { | |
if (toRow === fromRow + direction) return true; | |
if ( | |
fromRow === startRow && | |
toRow === fromRow + 2 * direction && | |
!board[fromRow + direction][fromCol] | |
) | |
return true; | |
} | |
// Capture | |
if ( | |
Math.abs(fromCol - toCol) === 1 && | |
toRow === fromRow + direction && | |
board[toRow][toCol] | |
) { | |
return true; | |
} | |
return false; | |
} | |
// Knight move validation | |
function isValidKnightMove(fromRow, fromCol, toRow, toCol) { | |
const rowDiff = Math.abs(toRow - fromRow); | |
const colDiff = Math.abs(toCol - fromCol); | |
return ( | |
(rowDiff === 2 && colDiff === 1) || (rowDiff === 1 && colDiff === 2) | |
); | |
} | |
// Bishop move validation | |
function isValidBishopMove(fromRow, fromCol, toRow, toCol) { | |
if (Math.abs(toRow - fromRow) !== Math.abs(toCol - fromCol)) | |
return false; | |
return isDiagonalClear(fromRow, fromCol, toRow, toCol); | |
} | |
// Rook move validation | |
function isValidRookMove(fromRow, fromCol, toRow, toCol) { | |
if (fromRow !== toRow && fromCol !== toCol) return false; | |
return isStraightClear(fromRow, fromCol, toRow, toCol); | |
} | |
// Queen move validation | |
function isValidQueenMove(fromRow, fromCol, toRow, toCol) { | |
return ( | |
isValidBishopMove(fromRow, fromCol, toRow, toCol) || | |
isValidRookMove(fromRow, fromCol, toRow, toCol) | |
); | |
} | |
// King move validation | |
function isValidKingMove(fromRow, fromCol, toRow, toCol) { | |
return Math.abs(toRow - fromRow) <= 1 && Math.abs(toCol - fromCol) <= 1; | |
} | |
// Check if diagonal path is clear | |
function isDiagonalClear(fromRow, fromCol, toRow, toCol) { | |
const rowStep = toRow > fromRow ? 1 : -1; | |
const colStep = toCol > fromCol ? 1 : -1; | |
let row = fromRow + rowStep; | |
let col = fromCol + colStep; | |
while (row !== toRow) { | |
if (board[row][col]) return false; | |
row += rowStep; | |
col += colStep; | |
} | |
return true; | |
} | |
// Check if straight path is clear | |
function isStraightClear(fromRow, fromCol, toRow, toCol) { | |
const rowStep = toRow > fromRow ? 1 : toRow < fromRow ? -1 : 0; | |
const colStep = toCol > fromCol ? 1 : toCol < fromCol ? -1 : 0; | |
let row = fromRow + rowStep; | |
let col = fromCol + colStep; | |
while (row !== toRow || col !== toCol) { | |
if (board[row][col]) return false; | |
row += rowStep; | |
col += colStep; | |
} | |
return true; | |
} | |
// Make a move | |
function makeMove(fromRow, fromCol, toRow, toCol) { | |
// Save the move for undo (if needed) and check validation | |
const tempBoard = board.map((row) => [...row]); | |
const piece = board[fromRow][fromCol]; | |
const capturedPiece = board[toRow][toCol]; | |
// Make the move | |
board[toRow][toCol] = piece; | |
board[fromRow][fromCol] = null; | |
// Check if the move leaves the king in check | |
if (isKingInCheck(currentPlayer)) { | |
// Undo the move | |
board = tempBoard; | |
return; | |
} | |
// Record the move | |
lastMove = { | |
from: { row: fromRow, col: fromCol }, | |
to: { row: toRow, col: toCol }, | |
}; | |
const moveNotation = generateMoveNotation( | |
piece, | |
fromRow, | |
fromCol, | |
toRow, | |
toCol, | |
capturedPiece, | |
); | |
moveHistory.push(moveNotation); | |
updateMoveList(); | |
// Switch players | |
currentPlayer = currentPlayer === "white" ? "black" : "white"; | |
// Check for checkmate or check | |
if (isCheckmate(currentPlayer)) { | |
endGame( | |
`${currentPlayer === "white" ? "Black" : "White"} wins by checkmate!`, | |
); | |
} else if (isKingInCheck(currentPlayer)) { | |
document.getElementById("checkStatus").textContent = "Check!"; | |
} else { | |
document.getElementById("checkStatus").textContent = ""; | |
} | |
renderBoard(); | |
updateStatus(); | |
} | |
// Generate move notation | |
function generateMoveNotation( | |
piece, | |
fromRow, | |
fromCol, | |
toRow, | |
toCol, | |
capturedPiece, | |
) { | |
const files = "abcdefgh"; | |
const ranks = "87654321"; | |
let notation = ""; | |
if (piece.toLowerCase() !== "p") { | |
notation += piece.toUpperCase(); | |
} | |
if (capturedPiece) { | |
if (piece.toLowerCase() === "p") { | |
notation += files[fromCol]; | |
} | |
notation += "x"; | |
} | |
notation += files[toCol] + ranks[toRow]; | |
// Check for check or checkmate | |
currentPlayer = currentPlayer === "white" ? "black" : "white"; | |
if (isCheckmate(currentPlayer)) { | |
notation += "#"; | |
} else if (isKingInCheck(currentPlayer)) { | |
notation += "+"; | |
} | |
currentPlayer = currentPlayer === "white" ? "black" : "white"; | |
return notation; | |
} | |
// Find king position | |
function findKing(player) { | |
const kingPiece = player === "white" ? "K" : "k"; | |
for (let row = 0; row < 8; row++) { | |
for (let col = 0; col < 8; col++) { | |
if (board[row][col] === kingPiece) { | |
return { row, col }; | |
} | |
} | |
} | |
return null; | |
} | |
// Check if king is in check | |
function isKingInCheck(player) { | |
const king = findKing(player); | |
if (!king) return false; | |
const opponent = player === "white" ? "black" : "white"; | |
for (let row = 0; row < 8; row++) { | |
for (let col = 0; col < 8; col++) { | |
const piece = board[row][col]; | |
if ( | |
piece && | |
((opponent === "white" && piece === piece.toUpperCase()) || | |
(opponent === "black" && piece === piece.toLowerCase())) | |
) { | |
// Temporarily switch players to check if opponent can attack king | |
const temp = currentPlayer; | |
currentPlayer = opponent; | |
const canAttack = isValidMove(row, col, king.row, king.col); | |
currentPlayer = temp; | |
if (canAttack) return true; | |
} | |
} | |
} | |
return false; | |
} | |
// Check for checkmate | |
function isCheckmate(player) { | |
if (!isKingInCheck(player)) return false; | |
// Try all possible moves | |
for (let fromRow = 0; fromRow < 8; fromRow++) { | |
for (let fromCol = 0; fromCol < 8; fromCol++) { | |
const piece = board[fromRow][fromCol]; | |
if (!piece) continue; | |
if ( | |
(player === "white" && piece === piece.toUpperCase()) || | |
(player === "black" && piece === piece.toLowerCase()) | |
) { | |
for (let toRow = 0; toRow < 8; toRow++) { | |
for (let toCol = 0; toCol < 8; toCol++) { | |
if (isValidMove(fromRow, fromCol, toRow, toCol)) { | |
// Try the move | |
const tempBoard = board.map((row) => [...row]); | |
board[toRow][toCol] = board[fromRow][fromCol]; | |
board[fromRow][fromCol] = null; | |
const stillInCheck = isKingInCheck(player); | |
// Undo the move | |
board = tempBoard; | |
if (!stillInCheck) return false; | |
} | |
} | |
} | |
} | |
} | |
} | |
return true; | |
} | |
// Timer functions | |
function startTimer() { | |
if (timerInterval) clearInterval(timerInterval); | |
timerInterval = setInterval(() => { | |
if (gameOver) { | |
clearInterval(timerInterval); | |
return; | |
} | |
if (currentPlayer === "white") { | |
whiteTime--; | |
if (whiteTime <= 0) { | |
endGame("Black wins on time!"); | |
} | |
} else { | |
blackTime--; | |
if (blackTime <= 0) { | |
endGame("White wins on time!"); | |
} | |
} | |
updateTimers(); | |
}, 1000); | |
} | |
// Update timer displays | |
function updateTimers() { | |
const whiteMinutes = Math.floor(whiteTime / 60); | |
const whiteSeconds = whiteTime % 60; | |
const blackMinutes = Math.floor(blackTime / 60); | |
const blackSeconds = blackTime % 60; | |
const whiteTimerEl = document.getElementById("whiteTimer"); | |
const blackTimerEl = document.getElementById("blackTimer"); | |
whiteTimerEl.textContent = `⏱ White: ${whiteMinutes}:${whiteSeconds.toString().padStart(2, "0")}`; | |
blackTimerEl.textContent = `⏱ Black: ${blackMinutes}:${blackSeconds.toString().padStart(2, "0")}`; | |
// Update active timer | |
whiteTimerEl.classList.toggle("active", currentPlayer === "white"); | |
blackTimerEl.classList.toggle("active", currentPlayer === "black"); | |
// Warning when time is low | |
if (whiteTime < 30) whiteTimerEl.classList.add("warning"); | |
if (blackTime < 30) blackTimerEl.classList.add("warning"); | |
} | |
// Update game status | |
function updateStatus() { | |
const turnIndicator = document.getElementById("turnIndicator"); | |
turnIndicator.textContent = `${currentPlayer.charAt(0).toUpperCase() + currentPlayer.slice(1)}'s Turn`; | |
turnIndicator.style.background = | |
currentPlayer === "white" ? "#4CAF50" : "#333"; | |
} | |
// Update move list | |
function updateMoveList() { | |
const moveListEl = document.getElementById("moveList"); | |
moveListEl.innerHTML = ""; | |
for (let i = 0; i < moveHistory.length; i += 2) { | |
const movePair = document.createElement("div"); | |
movePair.className = "move-pair"; | |
const moveNumber = document.createElement("span"); | |
moveNumber.className = "move-number"; | |
moveNumber.textContent = `${Math.floor(i / 2) + 1}.`; | |
const whiteMove = document.createElement("span"); | |
whiteMove.textContent = moveHistory[i]; | |
const blackMove = document.createElement("span"); | |
blackMove.textContent = moveHistory[i + 1] || ""; | |
movePair.appendChild(moveNumber); | |
movePair.appendChild(whiteMove); | |
movePair.appendChild(blackMove); | |
moveListEl.appendChild(movePair); | |
} | |
// Scroll to bottom | |
moveListEl.scrollTop = moveListEl.scrollHeight; | |
} | |
// End the game | |
function endGame(message) { | |
gameOver = true; | |
clearInterval(timerInterval); | |
const modal = document.getElementById("gameOverModal"); | |
const title = document.getElementById("gameOverTitle"); | |
const messageEl = document.getElementById("gameOverMessage"); | |
if (message.includes("checkmate")) { | |
title.textContent = "Checkmate!"; | |
} else { | |
title.textContent = "Game Over"; | |
} | |
messageEl.textContent = message; | |
modal.classList.add("show"); | |
} | |
// Reset the game | |
function resetGame() { | |
gameOver = false; | |
currentPlayer = "white"; | |
selectedSquare = null; | |
moveHistory = []; | |
lastMove = null; | |
whiteTime = 300; | |
blackTime = 300; | |
document.getElementById("checkStatus").textContent = ""; | |
document.getElementById("gameOverModal").classList.remove("show"); | |
initializeGame(); | |
} | |
// Initialize the game on load | |
initializeGame(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment