about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--html/cards/index.html248
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