diff options
author | elioat <elioat@tilde.institute> | 2025-01-10 20:20:54 -0500 |
---|---|---|
committer | elioat <elioat@tilde.institute> | 2025-01-10 20:20:54 -0500 |
commit | bf01d51d4b849ce716eae0303a669841d4b914f3 (patch) | |
tree | 52729ebba91b5ce3e487474d0ba1fc567a8343a4 /html | |
parent | 786901f108f50eedde9b8dad9c6d327b108b1276 (diff) | |
download | tour-bf01d51d4b849ce716eae0303a669841d4b914f3.tar.gz |
*
Diffstat (limited to 'html')
-rw-r--r-- | html/cards/index.html | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/html/cards/index.html b/html/cards/index.html new file mode 100644 index 0000000..537920b --- /dev/null +++ b/html/cards/index.html @@ -0,0 +1,248 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Cards</title> + <style> + body { + margin: 0; + padding: 0; + } + + canvas { + display: block; + } + </style> +</head> +<body> + <canvas id="cards"></canvas> + <script> + // Constants + const CARD_WIDTH = 100; + const CARD_HEIGHT = 150; + const PADDING = 10; + const SUITS = ['❤️', '♦️', '♣️', '♠️']; + const VALUES = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']; + const CARD_BACK_COLOR = '#0066cc'; + const PATTERN_SIZE = 10; + + // Canvas setup + const canvas = document.getElementById('cards'); + const ctx = canvas.getContext('2d'); + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + + // Pure functions + const shuffle = array => [...array] + .reduce((acc, _, i) => { + const j = Math.floor(Math.random() * (i + 1)); + [acc[i], acc[j]] = [acc[j], acc[i]]; + return acc; + }, [...array]); + + const createDeck = () => + SUITS.flatMap(suit => VALUES.map(value => ({ suit, value }))); + + const createCard = (x, y, cardData) => ({ + x: x + 20, + y: y + 20, + card: cardData, + isFaceUp: false // Cards start face down + }); + + const isPointInCard = (x, y, card) => + x >= card.x && + x <= card.x + CARD_WIDTH && + y >= card.y && + y <= card.y + CARD_HEIGHT; + + // Rendering functions + const clearCanvas = () => { + ctx.fillStyle = 'beige'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + }; + + const renderCardBack = card => { + // Draw blue background + ctx.fillStyle = CARD_BACK_COLOR; + ctx.fillRect(card.x, card.y, CARD_WIDTH, CARD_HEIGHT); + + // Draw checkered pattern + ctx.strokeStyle = '#003366'; + for (let i = 0; i < CARD_WIDTH; i += PATTERN_SIZE) { + for (let j = 0; j < CARD_HEIGHT; j += PATTERN_SIZE) { + if ((i + j) % (PATTERN_SIZE * 2) === 0) { + ctx.fillStyle = '#0055aa'; + ctx.fillRect(card.x + i, card.y + j, PATTERN_SIZE, PATTERN_SIZE); + } + } + } + + // Draw border + ctx.strokeStyle = 'black'; + ctx.strokeRect(card.x, card.y, CARD_WIDTH, CARD_HEIGHT); + }; + + const renderCardFront = card => { + ctx.fillStyle = 'white'; + ctx.fillRect(card.x, card.y, CARD_WIDTH, CARD_HEIGHT); + ctx.fillStyle = 'black'; + ctx.font = '20px Arial'; + ctx.strokeRect(card.x, card.y, CARD_WIDTH, CARD_HEIGHT); + ctx.fillText(card.card.value, card.x + 10, card.y + 30); + ctx.fillText(card.card.suit, card.x + 10, card.y + 60); + }; + + const renderCard = card => { + if (card.isFaceUp) { + renderCardFront(card); + } else { + renderCardBack(card); + } + }; + + const renderAllCards = cards => { + clearCanvas(); + cards.forEach(renderCard); + }; + + // State management + const gameState = { + cards: [], + draggingCard: null, + deck: shuffle(createDeck()), + stackPosition: { + x: 0, + y: 0 + } + }; + + // Add function to get top card + const getTopStackCard = () => { + return gameState.cards.find(card => + card.x === gameState.stackPosition.x && + card.y === gameState.stackPosition.y + ); + }; + + // Event handlers + const handleMouseMove = e => { + if (!gameState.draggingCard) return; + + const rect = canvas.getBoundingClientRect(); + gameState.draggingCard.x = e.clientX - rect.left; + gameState.draggingCard.y = e.clientY - rect.top; + + renderAllCards(gameState.cards); + }; + + const handleMouseUp = () => { + gameState.draggingCard = null; + document.removeEventListener('mousemove', handleMouseMove); + document.removeEventListener('mouseup', handleMouseUp); + }; + + // Add this function to distribute cards evenly with overlap + const distributeCards = () => { + const totalCards = gameState.cards.length; + const cols = Math.floor((canvas.width - 20) / (CARD_WIDTH / 2)); // Allow overlap by halving the width and accounting for border + const rows = Math.ceil(totalCards / cols); // Calculate number of rows + + const cardWidthWithPadding = CARD_WIDTH / 2; // Allow overlap + const cardHeightWithPadding = CARD_HEIGHT / 2; // Allow overlap + + // Calculate starting positions + const startX = 20 + (canvas.width - (cols * cardWidthWithPadding)) / 2; // Start from 20 pixels from the left + const startY = 20 + (canvas.height - (rows * cardHeightWithPadding)) / 2; // Start from 20 pixels from the top + + // Distribute cards + gameState.cards.forEach((card, index) => { + const row = Math.floor(index / cols); + const col = index % cols; + card.x = startX + col * cardWidthWithPadding + (Math.random() * 20 - 10); // Random offset for overlap + card.y = startY + row * cardHeightWithPadding + (Math.random() * 20 - 10); // Random offset for overlap + card.isFaceUp = true; // Flip cards face up when distributed + }); + + renderAllCards(gameState.cards); + }; + + // Update handleMouseDown to include double-click detection + let lastClickTime = 0; + const handleMouseDown = e => { + const rect = canvas.getBoundingClientRect(); + const x = e.clientX - rect.left; + const y = e.clientY - rect.top; + + // Handle right click for card flipping + if (e.button === 2) { // Right click + e.preventDefault(); + const clickedCard = gameState.cards.find(card => + isPointInCard(x, y, card) && + (card.x !== gameState.stackPosition.x || card.y !== gameState.stackPosition.y) + ); + if (clickedCard) { + clickedCard.isFaceUp = !clickedCard.isFaceUp; + renderAllCards(gameState.cards); + } + return; + } + + // Check for double-click + const currentTime = new Date().getTime(); + if (currentTime - lastClickTime < 300) { // 300 ms for double-click + distributeCards(); // Show all cards when double-clicked + lastClickTime = 0; // Reset last click time + return; + } + lastClickTime = currentTime; + + // Find any clicked card + const clickedCard = gameState.cards.find(card => isPointInCard(x, y, card)); + + if (clickedCard) { + // If it's a card in the stack, handle drawing it + if (clickedCard.x === gameState.stackPosition.x && + clickedCard.y === gameState.stackPosition.y) { + clickedCard.isFaceUp = true; + } + + // Allow dragging regardless of where the card is + gameState.draggingCard = clickedCard; + document.addEventListener('mousemove', handleMouseMove); + document.addEventListener('mouseup', handleMouseUp); + } + }; + + // Initialize game + const initializeGame = () => { + // Create a full deck of cards + gameState.deck = shuffle(createDeck()); // Ensure we have a full deck + gameState.cards = gameState.deck.map(cardData => createCard(0, 0, cardData)); // Create cards with initial position (0, 0) + + // Draw cards in a pile with their reverse side showing + gameState.cards.forEach((card, index) => { + card.x = 20; // Pile at 20 pixels from the left + card.y = 20 + index * 5; // Stack cards with a slight offset for visibility + }); + + clearCanvas(); + renderAllCards(gameState.cards); + + // Add event listeners + canvas.addEventListener('mousedown', handleMouseDown); + canvas.addEventListener('contextmenu', e => e.preventDefault()); + }; + + // Start the game + initializeGame(); + + // Clean up on window unload + window.addEventListener('unload', () => { + canvas.removeEventListener('mousedown', handleMouseDown); + canvas.removeEventListener('contextmenu', e => e.preventDefault()); + }); + </script> +</body> +</html> \ No newline at end of file |