about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorelioat <elioat@tilde.institute>2025-01-11 12:24:36 -0500
committerelioat <elioat@tilde.institute>2025-01-11 12:24:36 -0500
commit49338d20b0e95e05cf3502b5394996b97e44ae03 (patch)
tree07237b172141ac9c95142fd6179069d35857e853
parent95067512ffb2ee129581146cc433ddd9c79c3b49 (diff)
downloadtour-49338d20b0e95e05cf3502b5394996b97e44ae03.tar.gz
*
-rw-r--r--html/cards/script.js133
1 files changed, 93 insertions, 40 deletions
diff --git a/html/cards/script.js b/html/cards/script.js
index 8df9453..049f91f 100644
--- a/html/cards/script.js
+++ b/html/cards/script.js
@@ -1,3 +1,25 @@
+/**
+ * @typedef {Object} Card
+ * @property {number} x
+ * @property {number} y
+ * @property {CardData} card
+ * @property {boolean} isFaceUp
+ */
+
+/**
+ * @typedef {Object} CardData
+ * @property {string} suit
+ * @property {string} value
+ */
+
+/**
+ * @typedef {Object} GameState
+ * @property {Card[]} cards
+ * @property {Card|null} draggingCard
+ * @property {CardData[]} deck
+ * @property {{x: number, y: number}} stackPosition
+ */
+
 // Constants
 const CARD_WIDTH = 100;
 const CARD_HEIGHT = 150;
@@ -18,19 +40,23 @@ 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 shuffle = array => {
+    const result = [...array];
+    for (let i = result.length - 1; i > 0; i--) {
+        const j = Math.floor(Math.random() * (i + 1));
+        [result[i], result[j]] = [result[j], result[i]];
+    }
+    return result;
+};
 
 const createDeck = () => SUITS.flatMap(suit => VALUES.map(value => ({ suit, value })));
 
-const createCard = (x, y, cardData) => ({
+// Create a more functional card factory
+const createCard = (x, y, cardData) => Object.freeze({
     x: x + PADDING,
     y: y + PADDING,
-    card: cardData,
-    isFaceUp: false  // Cards start face down
+    card: Object.freeze({ ...cardData }),
+    isFaceUp: false
 });
 
 // Function to check if a point is within a card
@@ -105,25 +131,27 @@ const initializeGameState = () => ({
 });
 
 const initializeGame = () => {
-    gameState = initializeGameState();
-    gameState.cards = gameState.deck.map(cardData => createCard(INITIAL_CARD_X, INITIAL_CARD_Y, cardData));
-    
-    gameState.cards.forEach((card, index) => {
-        card.y += index * 5; // Stack cards with a slight offset for visibility
-    });
+    try {
+        gameState = initializeGameState();
+        gameState.cards = gameState.deck.map((cardData, index) => 
+            createCard(INITIAL_CARD_X, INITIAL_CARD_Y + index * 5, cardData)
+        );
+
+        clearCanvas();
+        renderAllCards(gameState.cards);
+        
+        setupEventListeners();
+    } catch (error) {
+        console.error('Failed to initialize game:', error);
+        alert('Failed to initialize game. Please refresh the page.');
+    }
+};
 
-    clearCanvas();
-    renderAllCards(gameState.cards);
-    
-    // Add event listeners
+const setupEventListeners = () => {
     canvas.addEventListener('mousedown', handleMouseDown);
     canvas.addEventListener('contextmenu', e => e.preventDefault());
-
-    // Add event listener for the 'r' key
     document.addEventListener('keydown', e => {
-        if (e.key === 'q') {
-            handleResetGame();
-        }
+        if (e.key === 'q') handleResetGame();
     });
 };
 
@@ -132,9 +160,15 @@ const handleMouseMove = e => {
     if (!gameState.draggingCard) return;
 
     const rect = canvas.getBoundingClientRect();
-    // Update the card's position using the offset
-    gameState.draggingCard.x = e.clientX - rect.left - dragOffset.x;
-    gameState.draggingCard.y = e.clientY - rect.top - dragOffset.y;
+    const newX = e.clientX - rect.left - dragOffset.x;
+    const newY = e.clientY - rect.top - dragOffset.y;
+
+    // Update the card's position immutably
+    const updatedCard = moveCard(gameState.draggingCard, newX, newY);
+    gameState.cards = gameState.cards.map(card => 
+        card === gameState.draggingCard ? updatedCard : card
+    );
+    gameState.draggingCard = updatedCard;
 
     renderAllCards(gameState.cards);
 };
@@ -162,33 +196,41 @@ const handleMouseUp = e => {
 
 let dragOffset = { x: 0, y: 0 }; // To store the offset of the click position
 
+const findClickedCard = (x, y, cards) => 
+    cards.slice().reverse().find(card => isPointInCard(x, y, card));
+
+const moveCardToTop = (targetCard, cards) => [
+    ...cards.filter(card => card !== targetCard),
+    targetCard
+];
+
 const handleMouseDown = e => {
     const rect = canvas.getBoundingClientRect();
     const x = e.clientX - rect.left;
     const y = e.clientY - rect.top;
 
-    if (e.button === 2) { // Right click
+    if (e.button === 2) {
         e.preventDefault();
-        const clickedCard = gameState.cards.find(card => isPointInCard(x, y, card));
+        const clickedCard = findClickedCard(x, y, gameState.cards);
         if (clickedCard) {
-            clickedCard.isFaceUp = !clickedCard.isFaceUp; // Toggle card face
-            renderAllCards(gameState.cards); // Re-render all cards
+            const updatedCard = toggleCardFace(clickedCard);
+            gameState.cards = gameState.cards.map(card => 
+                card === clickedCard ? updatedCard : card
+            );
+            renderAllCards(gameState.cards);
         }
         return;
     }
 
-    const clickedCard = gameState.cards.slice().reverse().find(card => isPointInCard(x, y, card));
+    const clickedCard = findClickedCard(x, y, gameState.cards);
     if (clickedCard) {
         gameState.draggingCard = clickedCard;
-
-        // Calculate the offset between the mouse position and the card's position
-        dragOffset.x = x - clickedCard.x;
-        dragOffset.y = y - clickedCard.y;
-
-        // Move the dragged card to the top of the stack
-        gameState.cards = gameState.cards.filter(card => card !== clickedCard);
-        gameState.cards.push(clickedCard);
-
+        dragOffset = {
+            x: x - clickedCard.x,
+            y: y - clickedCard.y
+        };
+        gameState.cards = moveCardToTop(clickedCard, gameState.cards);
+        
         document.addEventListener('mousemove', handleMouseMove);
         document.addEventListener('mouseup', handleMouseUp);
     }
@@ -201,6 +243,17 @@ const handleResetGame = () => {
     }
 };
 
+const moveCard = (card, newX, newY) => ({
+    ...card,
+    x: newX,
+    y: newY
+});
+
+const toggleCardFace = card => ({
+    ...card,
+    isFaceUp: !card.isFaceUp
+});
+
 // Start the game
 initializeGame();