| <!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> |