| | <!DOCTYPE html> |
| | <html lang="en"> |
| | <head> |
| | <meta charset="UTF-8"> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | <title>Card Trick Game</title> |
| | <script src="https://cdn.tailwindcss.com"></script> |
| | <style> |
| | .card { |
| | width: 80px; |
| | height: 120px; |
| | perspective: 1000px; |
| | cursor: pointer; |
| | transition: all 0.3s ease; |
| | } |
| | |
| | .card-inner { |
| | position: relative; |
| | width: 100%; |
| | height: 100%; |
| | transform-style: preserve-3d; |
| | transition: transform 0.6s; |
| | } |
| | |
| | .card .card-inner.flipped { |
| | transform: rotateY(180deg); |
| | } |
| | |
| | .card-front, .card-back { |
| | position: absolute; |
| | width: 100%; |
| | height: 100%; |
| | backface-visibility: hidden; |
| | border-radius: 8px; |
| | display: flex; |
| | align-items: center; |
| | justify-content: center; |
| | box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); |
| | } |
| | |
| | .card-front { |
| | background: white; |
| | transform: rotateY(180deg); |
| | font-weight: bold; |
| | font-size: 1.2rem; |
| | } |
| | |
| | .card-back { |
| | background: linear-gradient(135deg, #4f46e5, #8b5cf6); |
| | color: white; |
| | } |
| | |
| | .highlight-card { |
| | box-shadow: 0 0 15px 5px rgba(255, 215, 0, 0.7); |
| | transform: scale(1.05); |
| | } |
| | |
| | .pile-button { |
| | transition: all 0.3s ease; |
| | } |
| | |
| | .pile-button:hover { |
| | transform: translateY(-3px); |
| | } |
| | |
| | .pile-button:active { |
| | transform: translateY(1px); |
| | } |
| | |
| | @media (max-width: 768px) { |
| | .card { |
| | width: 60px; |
| | height: 90px; |
| | } |
| | } |
| | </style> |
| | </head> |
| | <body class="bg-gray-100 min-h-screen flex flex-col items-center py-8"> |
| | <div class="container mx-auto px-4"> |
| | <h1 class="text-4xl font-bold text-center text-indigo-800 mb-2">Magical Card Trick</h1> |
| | <p class="text-center text-gray-600 mb-8">I'll read your mind through your favorite number!</p> |
| | |
| | <div id="game-container" class="flex flex-col items-center"> |
| | |
| | </div> |
| | </div> |
| |
|
| | <script> |
| | |
| | const gameState = { |
| | phase: 'number-input', |
| | favoriteNumber: null, |
| | deck: [], |
| | currentRound: 0, |
| | ternaryDigits: [], |
| | selectedPiles: [], |
| | memorizedCard: null, |
| | memorizedCardPosition: null |
| | }; |
| | |
| | |
| | const suits = ['♥', '♦', '♠', '♣']; |
| | const values = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']; |
| | |
| | |
| | function initGame() { |
| | renderGame(); |
| | } |
| | |
| | |
| | function renderGame() { |
| | const container = document.getElementById('game-container'); |
| | container.innerHTML = ''; |
| | |
| | switch (gameState.phase) { |
| | case 'number-input': |
| | renderNumberInput(container); |
| | break; |
| | case 'pile-selection': |
| | renderPileSelection(container); |
| | break; |
| | case 'result': |
| | renderResult(container); |
| | break; |
| | } |
| | } |
| | |
| | |
| | function renderNumberInput(container) { |
| | const div = document.createElement('div'); |
| | div.className = 'bg-white p-8 rounded-lg shadow-lg max-w-md w-full'; |
| | |
| | div.innerHTML = ` |
| | <h2 class="text-2xl font-semibold text-indigo-700 mb-4">Enter Your Favorite Number</h2> |
| | <p class="text-gray-600 mb-6">Choose a number between 1 and 27. This will help me find your card later!</p> |
| | |
| | <div class="flex flex-col space-y-4"> |
| | <input |
| | type="number" |
| | id="favorite-number" |
| | min="1" |
| | max="27" |
| | placeholder="1-27" |
| | class="border-2 border-indigo-200 rounded-lg px-4 py-2 focus:outline-none focus:border-indigo-500 text-center text-xl" |
| | > |
| | |
| | <button |
| | id="submit-number" |
| | class="bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-3 px-6 rounded-lg transition duration-200" |
| | > |
| | Start the Magic! |
| | </button> |
| | </div> |
| | `; |
| | |
| | container.appendChild(div); |
| | |
| | document.getElementById('submit-number').addEventListener('click', () => { |
| | const numberInput = document.getElementById('favorite-number'); |
| | const number = parseInt(numberInput.value); |
| | |
| | if (isNaN(number) || number < 1 || number > 27) { |
| | alert('Please enter a number between 1 and 27'); |
| | return; |
| | } |
| | |
| | gameState.favoriteNumber = number; |
| | gameState.phase = 'pile-selection'; |
| | gameState.currentRound = 0; |
| | |
| | |
| | const num = number - 1; |
| | gameState.ternaryDigits = num.toString(3).padStart(3, '0').split('').reverse(); |
| | |
| | |
| | gameState.deck = createDeck(); |
| | shuffleDeck(gameState.deck); |
| | |
| | renderGame(); |
| | }); |
| | } |
| | |
| | |
| | function renderPileSelection(container) { |
| | const div = document.createElement('div'); |
| | div.className = 'bg-white p-6 rounded-lg shadow-lg w-full'; |
| | |
| | div.innerHTML = ` |
| | <h2 class="text-2xl font-semibold text-indigo-700 mb-4">Round ${gameState.currentRound + 1} of 3</h2> |
| | <p class="text-gray-600 mb-6">Look at the cards below and memorize one in your mind. Then select the pile where your card is located:</p> |
| | |
| | <div id="pile-grid" class="grid grid-cols-3 gap-4 mb-8"></div> |
| | |
| | <div class="flex justify-center space-x-4"> |
| | <button |
| | id="pile-0" |
| | class="pile-button bg-red-500 hover:bg-red-600 text-white font-bold py-3 px-6 rounded-lg transition duration-200" |
| | > |
| | Pile 1 |
| | </button> |
| | <button |
| | id="pile-1" |
| | class="pile-button bg-blue-500 hover:bg-blue-600 text-white font-bold py-3 px-6 rounded-lg transition duration-200" |
| | > |
| | Pile 2 |
| | </button> |
| | <button |
| | id="pile-2" |
| | class="pile-button bg-green-500 hover:bg-green-600 text-white font-bold py-3 px-6 rounded-lg transition duration-200" |
| | > |
| | Pile 3 |
| | </button> |
| | </div> |
| | `; |
| | |
| | container.appendChild(div); |
| | |
| | |
| | const pileGrid = document.getElementById('pile-grid'); |
| | const piles = splitIntoPiles(gameState.deck); |
| | |
| | for (let row = 0; row < 9; row++) { |
| | for (let col = 0; col < 3; col++) { |
| | const card = piles[col][row]; |
| | const cardElement = createCardElement(card, row * 3 + col, true); |
| | pileGrid.appendChild(cardElement); |
| | } |
| | } |
| | |
| | |
| | document.getElementById('pile-0').addEventListener('click', () => handlePileSelection(0)); |
| | document.getElementById('pile-1').addEventListener('click', () => handlePileSelection(1)); |
| | document.getElementById('pile-2').addEventListener('click', () => handlePileSelection(2)); |
| | } |
| | |
| | |
| | function renderResult(container) { |
| | const position = gameState.memorizedCardPosition + 1; |
| | |
| | const div = document.createElement('div'); |
| | div.className = 'bg-white p-8 rounded-lg shadow-lg max-w-3xl w-full'; |
| | |
| | div.innerHTML = ` |
| | <h2 class="text-3xl font-bold text-indigo-700 mb-4 text-center">✨ The Magic Revealed! ✨</h2> |
| | |
| | <div class="mb-8 text-center"> |
| | <p class="text-xl text-gray-700 mb-2">Your favorite number was <span class="font-bold text-indigo-600">${gameState.favoriteNumber}</span></p> |
| | <p class="text-xl text-gray-700">Your card is at position <span class="font-bold text-indigo-600">${position}</span> in the deck!</p> |
| | </div> |
| | |
| | <div id="result-deck" class="grid grid-cols-9 gap-1 mb-8"> |
| | <!-- Cards will be inserted here face up --> |
| | </div> |
| | |
| | <div class="text-center"> |
| | <p class="text-gray-600 mb-6">See? I knew your card would be at position ${position} all along!</p> |
| | |
| | <button |
| | id="play-again" |
| | class="bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-3 px-8 rounded-lg transition duration-200" |
| | > |
| | Play Again |
| | </button> |
| | </div> |
| | `; |
| | |
| | container.appendChild(div); |
| | |
| | |
| | const resultDeck = document.getElementById('result-deck'); |
| | |
| | gameState.deck.forEach((card, index) => { |
| | const cardElement = document.createElement('div'); |
| | cardElement.className = `flex flex-col items-center p-2 rounded ${index === gameState.memorizedCardPosition ? 'bg-yellow-100 highlight-card' : 'bg-gray-50'}`; |
| | |
| | cardElement.innerHTML = ` |
| | <div class="text-xs font-semibold text-gray-500 mb-1">${index + 1}</div> |
| | <div class="card"> |
| | <div class="card-inner flipped"> |
| | <div class="card-front">${card.value}${card.suit}</div> |
| | <div class="card-back"></div> |
| | </div> |
| | </div> |
| | `; |
| | |
| | resultDeck.appendChild(cardElement); |
| | }); |
| | |
| | document.getElementById('play-again').addEventListener('click', resetGame); |
| | } |
| | |
| | |
| | function createCardElement(card, index, clickable = true) { |
| | const cardElement = document.createElement('div'); |
| | cardElement.className = 'card'; |
| | cardElement.dataset.index = index; |
| | |
| | |
| | const showFace = true; |
| | |
| | cardElement.innerHTML = ` |
| | <div class="card-inner ${showFace ? 'flipped' : ''}"> |
| | <div class="card-front">${card.value}${card.suit}</div> |
| | <div class="card-back"></div> |
| | </div> |
| | `; |
| | |
| | if (clickable) { |
| | cardElement.addEventListener('click', () => { |
| | |
| | document.querySelectorAll('.card').forEach(c => { |
| | c.classList.remove('highlight-card'); |
| | }); |
| | |
| | |
| | cardElement.classList.add('highlight-card'); |
| | |
| | |
| | gameState.memorizedCard = card; |
| | gameState.memorizedCardPosition = index; |
| | }); |
| | } |
| | |
| | return cardElement; |
| | } |
| | |
| | |
| | function handlePileSelection(pileIndex) { |
| | gameState.selectedPiles[gameState.currentRound] = pileIndex; |
| | |
| | |
| | const ternaryDigit = parseInt(gameState.ternaryDigits[gameState.currentRound]); |
| | const piles = splitIntoPiles(gameState.deck); |
| | |
| | let gatheredPiles = []; |
| | |
| | switch (ternaryDigit) { |
| | case 0: |
| | |
| | gatheredPiles = [...piles[pileIndex], ...piles[(pileIndex + 1) % 3], ...piles[(pileIndex + 2) % 3]]; |
| | break; |
| | case 1: |
| | |
| | gatheredPiles = [...piles[(pileIndex + 1) % 3], ...piles[pileIndex], ...piles[(pileIndex + 2) % 3]]; |
| | break; |
| | case 2: |
| | |
| | gatheredPiles = [...piles[(pileIndex + 1) % 3], ...piles[(pileIndex + 2) % 3], ...piles[pileIndex]]; |
| | break; |
| | } |
| | |
| | gameState.deck = gatheredPiles; |
| | gameState.currentRound++; |
| | |
| | if (gameState.currentRound < 3) { |
| | renderGame(); |
| | } else { |
| | |
| | const ternaryStr = (gameState.favoriteNumber - 1).toString(3).padStart(3, '0'); |
| | const digits = ternaryStr.split('').reverse().map(Number); |
| | |
| | |
| | gameState.memorizedCardPosition = |
| | 9 * digits[2] + 3 * digits[1] + digits[0]; |
| | |
| | gameState.phase = 'result'; |
| | renderGame(); |
| | } |
| | } |
| | |
| | |
| | function createDeck() { |
| | const deck = []; |
| | for (const suit of suits) { |
| | for (const value of values) { |
| | deck.push({ suit, value }); |
| | } |
| | } |
| | return deck; |
| | } |
| | |
| | |
| | function shuffleDeck(deck) { |
| | for (let i = deck.length - 1; i > 0; i--) { |
| | const j = Math.floor(Math.random() * (i + 1)); |
| | [deck[i], deck[j]] = [deck[j], deck[i]]; |
| | } |
| | } |
| | |
| | |
| | function splitIntoPiles(deck) { |
| | const piles = [[], [], []]; |
| | for (let i = 0; i < 27; i++) { |
| | const pileIndex = i % 3; |
| | piles[pileIndex].push(deck[i]); |
| | } |
| | return piles; |
| | } |
| | |
| | |
| | function resetGame() { |
| | gameState.phase = 'number-input'; |
| | gameState.favoriteNumber = null; |
| | gameState.deck = []; |
| | gameState.currentRound = 0; |
| | gameState.ternaryDigits = []; |
| | gameState.selectedPiles = []; |
| | gameState.memorizedCard = null; |
| | gameState.memorizedCardPosition = null; |
| | |
| | renderGame(); |
| | } |
| | |
| | |
| | window.onload = 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 <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=manyone/manyone-cards27" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| | </html> |