about summary refs log tree commit diff stats
path: root/html/cards/script.js
diff options
context:
space:
mode:
Diffstat (limited to 'html/cards/script.js')
-rw-r--r--html/cards/script.js183
1 files changed, 183 insertions, 0 deletions
diff --git a/html/cards/script.js b/html/cards/script.js
new file mode 100644
index 0000000..64c145b
--- /dev/null
+++ b/html/cards/script.js
@@ -0,0 +1,183 @@
+// 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
+});
+
+// Function to check if a point is within a card
+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
+    }
+};
+
+// 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);
+};
+
+// Define handleMouseDown before using it
+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;
+    }
+
+    // Find the top-most clicked card
+    const clickedCard = gameState.cards.slice().reverse().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());
+    gameState.cards = gameState.deck.map(cardData => createCard(0, 0, cardData));
+
+    // 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());
+}); 
\ No newline at end of file