|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Solitaire</title> |
|
<style> |
|
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap'); |
|
|
|
* { |
|
margin: 0; |
|
padding: 0; |
|
box-sizing: border-box; |
|
user-select: none; |
|
} |
|
|
|
body { |
|
font-family: 'Roboto', sans-serif; |
|
background: linear-gradient(135deg, #1e5799, #207cca, #2989d8, #7db9e8); |
|
height: 100vh; |
|
display: flex; |
|
flex-direction: column; |
|
align-items: center; |
|
color: #333; |
|
overflow: hidden; |
|
} |
|
|
|
.game-container { |
|
width: 100%; |
|
max-width: 1000px; |
|
margin: 20px auto; |
|
display: flex; |
|
flex-direction: column; |
|
height: calc(100vh - 100px); |
|
} |
|
|
|
.header { |
|
display: flex; |
|
justify-content: space-between; |
|
align-items: center; |
|
padding: 10px 0; |
|
margin-bottom: 10px; |
|
color: white; |
|
} |
|
|
|
.score { |
|
font-size: 1.2rem; |
|
font-weight: bold; |
|
} |
|
|
|
.restart-btn { |
|
background: rgba(255, 255, 255, 0.2); |
|
border: none; |
|
color: white; |
|
padding: 8px 15px; |
|
border-radius: 5px; |
|
cursor: pointer; |
|
font-size: 1rem; |
|
transition: background 0.3s; |
|
} |
|
|
|
.restart-btn:hover { |
|
background: rgba(255, 255, 255, 0.3); |
|
} |
|
|
|
.game-board { |
|
display: flex; |
|
flex-direction: column; |
|
flex-grow: 1; |
|
} |
|
|
|
.top-section { |
|
display: flex; |
|
justify-content: space-between; |
|
margin-bottom: 20px; |
|
} |
|
|
|
.deck { |
|
width: 70px; |
|
height: 100px; |
|
position: relative; |
|
} |
|
|
|
.stock { |
|
cursor: pointer; |
|
position: relative; |
|
} |
|
|
|
.waste { |
|
position: relative; |
|
margin-left: 10px; |
|
} |
|
|
|
.foundations { |
|
display: flex; |
|
gap: 10px; |
|
} |
|
|
|
.foundation { |
|
width: 70px; |
|
height: 100px; |
|
border: 2px dashed rgba(255, 255, 255, 0.2); |
|
border-radius: 5px; |
|
} |
|
|
|
.tableau-section { |
|
display: flex; |
|
justify-content: space-between; |
|
flex-grow: 1; |
|
} |
|
|
|
.pile { |
|
display: flex; |
|
flex-direction: column; |
|
gap: 3px; |
|
min-height: 100px; |
|
position: relative; |
|
} |
|
|
|
.pile.bottom { |
|
margin-top: 30px; |
|
} |
|
|
|
.card { |
|
width: 70px; |
|
height: 100px; |
|
background: white; |
|
border-radius: 5px; |
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); |
|
position: absolute; |
|
transition: transform 0.2s; |
|
cursor: pointer; |
|
display: flex; |
|
flex-direction: column; |
|
justify-content: space-between; |
|
padding: 5px; |
|
font-weight: bold; |
|
font-size: 1.2rem; |
|
overflow: hidden; |
|
} |
|
|
|
.card.red { |
|
color: red; |
|
} |
|
|
|
.card.black { |
|
color: black; |
|
} |
|
|
|
.card.hidden { |
|
background: linear-gradient(135deg, #3498db, #2980b9); |
|
color: transparent; |
|
box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.2); |
|
border: 2px solid rgba(255, 255, 255, 0.2); |
|
} |
|
|
|
.card.hidden .card-value, |
|
.card.hidden .card-suit { |
|
display: none; |
|
} |
|
|
|
.card-suit { |
|
text-align: center; |
|
font-size: 30px; |
|
} |
|
|
|
.card-bottom-suit { |
|
transform: rotate(180deg); |
|
} |
|
|
|
.card-placeholder { |
|
border: 2px dashed rgba(255, 255, 255, 0.3); |
|
border-radius: 5px; |
|
} |
|
|
|
.card.dragging { |
|
opacity: 0.8; |
|
z-index: 100; |
|
transform: rotate(2deg); |
|
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.3); |
|
} |
|
|
|
.card.selected { |
|
transform: translateY(-10px); |
|
} |
|
|
|
.win-message { |
|
position: fixed; |
|
top: 0; |
|
left: 0; |
|
width: 100%; |
|
height: 100%; |
|
background: rgba(0, 0, 0, 0.7); |
|
display: flex; |
|
flex-direction: column; |
|
justify-content: center; |
|
align-items: center; |
|
z-index: 1000; |
|
color: white; |
|
opacity: 0; |
|
pointer-events: none; |
|
transition: opacity 0.5s; |
|
} |
|
|
|
.win-message.show { |
|
opacity: 1; |
|
pointer-events: all; |
|
} |
|
|
|
.win-text { |
|
font-size: 3rem; |
|
margin-bottom: 20px; |
|
text-align: center; |
|
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.5); |
|
} |
|
|
|
.win-details { |
|
font-size: 1.2rem; |
|
margin-bottom: 30px; |
|
} |
|
|
|
.move-counter { |
|
font-size: 1.1rem; |
|
margin-bottom: 5px; |
|
color: rgba(255, 255, 255, 0.8); |
|
} |
|
|
|
@media (max-width: 768px) { |
|
.game-container { |
|
transform: scale(0.8); |
|
transform-origin: top center; |
|
} |
|
} |
|
|
|
@media (max-width: 480px) { |
|
.game-container { |
|
transform: scale(0.6); |
|
} |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<div class="game-container"> |
|
<div class="header"> |
|
<div class="score">Score: <span id="score">0</span></div> |
|
<div class="move-counter">Moves: <span id="moves">0</span></div> |
|
<button class="restart-btn" id="restart">New Game</button> |
|
</div> |
|
|
|
<div class="game-board"> |
|
<div class="top-section"> |
|
<div class="deck"> |
|
<div class="card-placeholder stock" id="stock"></div> |
|
<div class="card-placeholder waste" id="waste"></div> |
|
</div> |
|
<div class="foundations"> |
|
<div class="card-placeholder foundation" id="foundation-1"></div> |
|
<div class="card-placeholder foundation" id="foundation-2"></div> |
|
<div class="card-placeholder foundation" id="foundation-3"></div> |
|
<div class="card-placeholder foundation" id="foundation-4"></div> |
|
</div> |
|
</div> |
|
|
|
<div class="tableau-section"> |
|
<div class="pile" id="pile-1"></div> |
|
<div class="pile" id="pile-2"></div> |
|
<div class="pile" id="pile-3"></div> |
|
<div class="pile" id="pile-4"></div> |
|
<div class="pile" id="pile-5"></div> |
|
<div class="pile" id="pile-6"></div> |
|
<div class="pile" id="pile-7"></div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div class="win-message" id="win-message"> |
|
<div class="win-text">You Won!</div> |
|
<div class="win-details">Congratulations on completing Solitaire!</div> |
|
<div>Final Score: <span id="final-score">0</span></div> |
|
<div>Total Moves: <span id="final-moves">0</span></div> |
|
<button class="restart-btn" style="margin-top: 20px;">Play Again</button> |
|
</div> |
|
|
|
<script> |
|
document.addEventListener('DOMContentLoaded', () => { |
|
|
|
const game = { |
|
deck: [], |
|
stock: [], |
|
waste: [], |
|
foundations: [[], [], [], []], |
|
piles: [[], [], [], [], [], [], []], |
|
score: 0, |
|
moves: 0, |
|
draggingCards: [], |
|
dragSource: null, |
|
gameStarted: false |
|
}; |
|
|
|
|
|
const elements = { |
|
stock: document.getElementById('stock'), |
|
waste: document.getElementById('waste'), |
|
foundations: [ |
|
document.getElementById('foundation-1'), |
|
document.getElementById('foundation-2'), |
|
document.getElementById('foundation-3'), |
|
document.getElementById('foundation-4') |
|
], |
|
piles: [ |
|
document.getElementById('pile-1'), |
|
document.getElementById('pile-2'), |
|
document.getElementById('pile-3'), |
|
document.getElementById('pile-4'), |
|
document.getElementById('pile-5'), |
|
document.getElementById('pile-6'), |
|
document.getElementById('pile-7') |
|
], |
|
scoreDisplay: document.getElementById('score'), |
|
movesDisplay: document.getElementById('moves'), |
|
restartBtn: document.getElementById('restart'), |
|
winMessage: document.getElementById('win-message'), |
|
finalScore: document.getElementById('final-score'), |
|
finalMoves: document.getElementById('final-moves') |
|
}; |
|
|
|
|
|
const suits = ['♥', '♦', '♠', '♣']; |
|
const values = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']; |
|
|
|
|
|
function initGame() { |
|
resetGame(); |
|
createDeck(); |
|
shuffleDeck(); |
|
dealCards(); |
|
updateUI(); |
|
game.gameStarted = true; |
|
} |
|
|
|
|
|
function resetGame() { |
|
game.deck = []; |
|
game.stock = []; |
|
game.waste = []; |
|
game.foundations = [[], [], [], []]; |
|
game.piles = [[], [], [], [], [], [], []]; |
|
game.score = 0; |
|
game.moves = 0; |
|
game.draggingCards = []; |
|
game.dragSource = null; |
|
game.gameStarted = false; |
|
|
|
|
|
elements.piles.forEach(pile => pile.innerHTML = ''); |
|
elements.foundations.forEach(foundation => foundation.innerHTML = ''); |
|
elements.waste.innerHTML = ''; |
|
} |
|
|
|
|
|
function createDeck() { |
|
game.deck = []; |
|
for (let suitIndex = 0; suitIndex < suits.length; suitIndex++) { |
|
for (let valueIndex = 0; valueIndex < values.length; valueIndex++) { |
|
const card = { |
|
suit: suits[suitIndex], |
|
value: values[valueIndex], |
|
color: (suitIndex < 2) ? 'red' : 'black', |
|
rank: valueIndex + 1, |
|
hidden: true |
|
}; |
|
game.deck.push(card); |
|
} |
|
} |
|
} |
|
|
|
|
|
function shuffleDeck() { |
|
for (let i = game.deck.length - 1; i > 0; i--) { |
|
const j = Math.floor(Math.random() * (i + 1)); |
|
[game.deck[i], game.deck[j]] = [game.deck[j], game.deck[i]]; |
|
} |
|
} |
|
|
|
|
|
function dealCards() { |
|
let cardIndex = 0; |
|
|
|
|
|
for (let pileIndex = 0; pileIndex < game.piles.length; pileIndex++) { |
|
const pile = game.piles[pileIndex]; |
|
|
|
|
|
for (let i = 0; i <= pileIndex; i++) { |
|
const card = game.deck[cardIndex++]; |
|
|
|
|
|
card.hidden = i !== pileIndex; |
|
pile.push(card); |
|
} |
|
} |
|
|
|
|
|
game.stock = game.deck.slice(cardIndex); |
|
} |
|
|
|
|
|
function updateUI() { |
|
updateStock(); |
|
updateWaste(); |
|
updateFoundations(); |
|
updatePiles(); |
|
updateScore(); |
|
checkWin(); |
|
} |
|
|
|
|
|
function updateStock() { |
|
elements.stock.innerHTML = ''; |
|
|
|
if (game.stock.length > 0) { |
|
const cardBack = document.createElement('div'); |
|
cardBack.className = 'card hidden'; |
|
elements.stock.appendChild(cardBack); |
|
} else { |
|
elements.stock.classList.add('empty'); |
|
} |
|
} |
|
|
|
|
|
function updateWaste() { |
|
elements.waste.innerHTML = ''; |
|
|
|
if (game.waste.length > 0) { |
|
const lastCard = game.waste[game.waste.length - 1]; |
|
const cardEl = createCardElement(lastCard); |
|
cardEl.style.left = '0'; |
|
cardEl.style.top = '0'; |
|
elements.waste.appendChild(cardEl); |
|
} |
|
} |
|
|
|
|
|
function updateFoundations() { |
|
game.foundations.forEach((foundation, index) => { |
|
elements.foundations[index].innerHTML = ''; |
|
|
|
if (foundation.length > 0) { |
|
const lastCard = foundation[foundation.length - 1]; |
|
const cardEl = createCardElement(lastCard); |
|
elements.foundations[index].appendChild(cardEl); |
|
} |
|
}); |
|
} |
|
|
|
|
|
function updatePiles() { |
|
game.piles.forEach((pile, pileIndex) => { |
|
elements.piles[pileIndex].innerHTML = ''; |
|
|
|
pile.forEach((card, cardIndex) => { |
|
const cardEl = createCardElement(card); |
|
cardEl.style.top = `${cardIndex * 25}px`; |
|
cardEl.setAttribute('data-pile-index', pileIndex); |
|
cardEl.setAttribute('data-card-index', cardIndex); |
|
elements.piles[pileIndex].appendChild(cardEl); |
|
}); |
|
}); |
|
} |
|
|
|
|
|
function createCardElement(card) { |
|
const cardEl = document.createElement('div'); |
|
cardEl.className = `card ${card.color}`; |
|
cardEl.innerHTML = ` |
|
<div class="card-value">${card.value}</div> |
|
<div class="card-suit">${card.suit}</div> |
|
<div class="card-suit card-bottom-suit">${card.suit}</div> |
|
`; |
|
|
|
if (card.hidden) { |
|
cardEl.classList.add('hidden'); |
|
} |
|
|
|
|
|
cardEl.draggable = true; |
|
cardEl.addEventListener('dragstart', handleDragStart); |
|
cardEl.addEventListener('mouseup', handleCardClick); |
|
|
|
return cardEl; |
|
} |
|
|
|
|
|
function updateScore() { |
|
elements.scoreDisplay.textContent = game.score; |
|
elements.movesDisplay.textContent = game.moves; |
|
} |
|
|
|
|
|
function drawCard() { |
|
if (!game.gameStarted) return; |
|
|
|
if (game.stock.length === 0) { |
|
|
|
game.waste.reverse().forEach(card => { |
|
card.hidden = true; |
|
}); |
|
game.stock = [...game.waste]; |
|
game.waste = []; |
|
game.score = Math.max(0, game.score - 100); |
|
} else { |
|
|
|
const card = game.stock.pop(); |
|
card.hidden = false; |
|
game.waste.push(card); |
|
game.score += 5; |
|
} |
|
|
|
game.moves++; |
|
updateUI(); |
|
} |
|
|
|
|
|
function handleDragStart(e) { |
|
if (!game.gameStarted) return; |
|
|
|
const cardEl = e.target.closest('.card'); |
|
if (!cardEl || cardEl.classList.contains('hidden')) { |
|
e.preventDefault(); |
|
return; |
|
} |
|
|
|
const pileIndex = parseInt(cardEl.getAttribute('data-pile-index')); |
|
const cardIndex = parseInt(cardEl.getAttribute('data-card-index')); |
|
|
|
|
|
game.draggingCards = game.piles[pileIndex].slice(cardIndex); |
|
game.dragSource = { type: 'pile', index: pileIndex }; |
|
|
|
|
|
if (isNaN(pileIndex)) { |
|
if (cardEl.parentElement === elements.waste) { |
|
game.draggingCards = [game.waste[game.waste.length - 1]]; |
|
game.dragSource = { type: 'waste' }; |
|
} else { |
|
const foundationIndex = elements.foundations.findIndex(f => cardEl.parentElement === f); |
|
if (foundationIndex !== -1) { |
|
game.draggingCards = [game.foundations[foundationIndex][game.foundations[foundationIndex].length - 1]]; |
|
game.dragSource = { type: 'foundation', index: foundationIndex }; |
|
} |
|
} |
|
} |
|
|
|
|
|
const dragImage = cardEl.cloneNode(true); |
|
dragImage.style.position = 'fixed'; |
|
dragImage.style.left = '-1000px'; |
|
dragImage.style.top = '0'; |
|
dragImage.style.zIndex = '10000'; |
|
document.body.appendChild(dragImage); |
|
e.dataTransfer.setDragImage(dragImage, 35, 50); |
|
setTimeout(() => document.body.removeChild(dragImage), 0); |
|
|
|
|
|
e.dataTransfer.effectAllowed = 'move'; |
|
|
|
|
|
setTimeout(() => cardEl.classList.add('dragging'), 0); |
|
} |
|
|
|
|
|
function handleDrop(target, e) { |
|
e.preventDefault(); |
|
if (!game.draggingCards.length) return; |
|
|
|
const card = game.draggingCards[0]; |
|
|
|
|
|
let targetType, targetIndex; |
|
if (target === elements.waste) { |
|
return; |
|
} else if (target === elements.stock) { |
|
return; |
|
} else if (elements.foundations.includes(target)) { |
|
targetType = 'foundation'; |
|
targetIndex = elements.foundations.indexOf(target); |
|
} else if (elements.piles.includes(target)) { |
|
targetType = 'pile'; |
|
targetIndex = elements.piles.indexOf(target); |
|
} else { |
|
return; |
|
} |
|
|
|
|
|
let isValid = false; |
|
|
|
if (targetType === 'foundation') { |
|
isValid = canMoveToFoundation(card, targetIndex); |
|
} else if (targetType === 'pile') { |
|
isValid = canMoveToPile(card, targetIndex); |
|
} |
|
|
|
if (isValid) { |
|
|
|
if (game.dragSource.type === 'pile') { |
|
game.piles[game.dragSource.index].splice( |
|
game.piles[game.dragSource.index].length - game.draggingCards.length, |
|
game.draggingCards.length |
|
); |
|
|
|
|
|
const sourcePile = game.piles[game.dragSource.index]; |
|
if (sourcePile.length > 0 && sourcePile[sourcePile.length - 1].hidden) { |
|
sourcePile[sourcePile.length - 1].hidden = false; |
|
game.score += 5; |
|
} |
|
} else if (game.dragSource.type === 'waste') { |
|
game.waste.pop(); |
|
} else if (game.dragSource.type === 'foundation') { |
|
game.foundations[game.dragSource.index].pop(); |
|
} |
|
|
|
|
|
if (targetType === 'foundation') { |
|
game.foundations[targetIndex].push(card); |
|
game.score += 10; |
|
} else if (targetType === 'pile') { |
|
game.piles[targetIndex].push(...game.draggingCards); |
|
|
|
|
|
game.score += (game.draggingCards.length === 1) ? 5 : 0; |
|
} |
|
|
|
game.moves++; |
|
game.draggingCards = []; |
|
game.dragSource = null; |
|
updateUI(); |
|
} |
|
} |
|
|
|
|
|
function canMoveToFoundation(card, foundationIndex) { |
|
const foundation = game.foundations[foundationIndex]; |
|
|
|
if (foundation.length === 0) { |
|
|
|
return card.value === 'A'; |
|
} else { |
|
const topCard = foundation[foundation.length - 1]; |
|
|
|
return topCard.suit === card.suit && card.rank === topCard.rank + 1; |
|
} |
|
} |
|
|
|
|
|
function canMoveToPile(card, pileIndex) { |
|
const pile = game.piles[pileIndex]; |
|
|
|
if (pile.length === 0) { |
|
|
|
return card.value === 'K'; |
|
} else { |
|
const topCard = pile[pile.length - 1]; |
|
|
|
return topCard.color !== card.color && card.rank === topCard.rank - 1; |
|
} |
|
} |
|
|
|
|
|
function handleCardClick(e) { |
|
if (!game.gameStarted) return; |
|
|
|
const cardEl = e.target.closest('.card'); |
|
if (!cardEl || cardEl.classList.contains('hidden')) return; |
|
|
|
|
|
if (cardEl.parentElement === elements.waste && game.waste.length > 0) { |
|
const card = game.waste[game.waste.length - 1]; |
|
|
|
|
|
for (let i = 0; i < game.foundations.length; i++) { |
|
if (canMoveToFoundation(card, i)) { |
|
game.foundations[i].push(game.waste.pop()); |
|
game.score += 10; |
|
game.moves++; |
|
updateUI(); |
|
return; |
|
} |
|
} |
|
} |
|
|
|
|
|
const pileIndex = parseInt(cardEl.getAttribute('data-pile-index')); |
|
const cardIndex = parseInt(cardEl.getAttribute('data-card-index')); |
|
|
|
if (!isNaN(pileIndex) && cardIndex === game.piles[pileIndex].length - 1) { |
|
const card = game.piles[pileIndex][cardIndex]; |
|
|
|
for (let i = 0; i < game.foundations.length; i++) { |
|
if (canMoveToFoundation(card, i)) { |
|
game.foundations[i].push(game.piles[pileIndex].pop()); |
|
game.score += 10; |
|
game.moves++; |
|
|
|
|
|
if (game.piles[pileIndex].length > 0 && game.piles[pileIndex][game.piles[pileIndex].length - 1].hidden) { |
|
game.piles[pileIndex][game.piles[pileIndex].length - 1].hidden = false; |
|
game.score += 5; |
|
} |
|
|
|
updateUI(); |
|
return; |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
function checkWin() { |
|
const allCardsInFoundations = game.foundations.every(foundation => foundation.length === 13); |
|
|
|
if (allCardsInFoundations) { |
|
showWinMessage(); |
|
} |
|
} |
|
|
|
|
|
function showWinMessage() { |
|
elements.finalScore.textContent = game.score; |
|
elements.finalMoves.textContent = game.moves; |
|
elements.winMessage.classList.add('show'); |
|
} |
|
|
|
|
|
function setupEventListeners() { |
|
|
|
elements.stock.addEventListener('click', drawCard); |
|
|
|
|
|
elements.restartBtn.addEventListener('click', initGame); |
|
elements.winMessage.querySelector('.restart-btn').addEventListener('click', () => { |
|
elements.winMessage.classList.remove('show'); |
|
initGame(); |
|
}); |
|
|
|
|
|
document.addEventListener('dragover', e => { |
|
e.preventDefault(); |
|
const target = document.elementFromPoint(e.clientX, e.clientY)?.closest('.pile, .foundation, .waste, .stock'); |
|
if (target) { |
|
const rect = target.getBoundingClientRect(); |
|
const isOver = e.clientX > rect.left && e.clientX < rect.right && |
|
e.clientY > rect.top && e.clientY < rect.bottom; |
|
if (isOver) { |
|
if (target === elements.stock || target === elements.waste) return; |
|
target.classList.add('highlight'); |
|
} |
|
} |
|
}); |
|
|
|
document.addEventListener('dragleave', e => { |
|
const target = document.elementFromPoint(e.clientX, e.clientY)?.closest('.pile, .foundation'); |
|
if (!target || e.target !== target) { |
|
elements.piles.forEach(pile => pile.classList.remove('highlight')); |
|
elements.foundations.forEach(foundation => foundation.classList.remove('highlight')); |
|
} |
|
}); |
|
|
|
document.addEventListener('drop', e => { |
|
e.preventDefault(); |
|
elements.piles.forEach(pile => pile.classList.remove('highlight')); |
|
elements.foundations.forEach(foundation => foundation.classList.remove('highlight')); |
|
|
|
const target = document.elementFromPoint(e.clientX, e.clientY)?.closest('.pile, .foundation, .waste, .stock'); |
|
if (target && game.draggingCards.length) { |
|
handleDrop(target, e); |
|
} |
|
}); |
|
|
|
document.addEventListener('dragend', () => { |
|
document.querySelectorAll('.card').forEach(card => card.classList.remove('dragging')); |
|
elements.piles.forEach(pile => pile.classList.remove('highlight')); |
|
elements.foundations.forEach(foundation => foundation.classList.remove('highlight')); |
|
}); |
|
|
|
|
|
document.addEventListener('keydown', e => { |
|
if (e.code === 'Space' && !e.target.matches('button, input, textarea')) { |
|
e.preventDefault(); |
|
drawCard(); |
|
} else if (e.code === 'KeyR' && !e.target.matches('button, input, textarea')) { |
|
e.preventDefault(); |
|
initGame(); |
|
} |
|
}); |
|
} |
|
|
|
|
|
setupEventListeners(); |
|
initGame(); |
|
}); |
|
</script> |
|
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body> |
|
</html> |