about summary refs log blame commit diff stats
path: root/html/cards/script.js
blob: 50c42877684a7158d0bf96cb10a674b9e37da8a4 (plain) (tree)













































                                                                                  
                                                  
                                
                                    
                                                                                         













                                                                                 
                                                                                           

  
                                                   

                                 
                                                                               
                            


                                                                         
                                                            








                                                                                                              
                                                                  
                                                                           
                                                                                                          


                            
                                                        





















































                                                                                          

                                                                             






























                                                                                                  
                                                              

















                                                                       
// 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);
};

// Update renderCardBack to use regular rectangles
const renderCardBack = card => {
    ctx.fillStyle = CARD_BACK_COLOR;
    ctx.fillRect(card.x, card.y, CARD_WIDTH, CARD_HEIGHT); // Revert to regular rectangle
    
    // 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); // Revert to regular rectangle
};

// Update renderCardFront to use regular rectangles
const renderCardFront = card => {
    ctx.fillStyle = 'white';
    ctx.fillRect(card.x, card.y, CARD_WIDTH, CARD_HEIGHT); // Regular rectangle
    ctx.fillStyle = 'black';
    
    // Set a larger font size
    ctx.font = '30px Arial'; // Increased font size for better visibility
    ctx.strokeRect(card.x, card.y, CARD_WIDTH, CARD_HEIGHT);
    
    // Draw value in the top left corner
    ctx.textAlign = 'left'; // Ensure left alignment for the value
    ctx.fillText(card.card.value, card.x + 10, card.y + 40); // Adjusted y position for larger font
    
    // Draw suit in the center of the card
    ctx.textAlign = 'center'; // Center the text for the suit
    ctx.fillText(card.card.suit, card.x + CARD_WIDTH / 2, card.y + CARD_HEIGHT / 2 + 10); // Centered position
    
    // Draw value in the bottom right corner with adjusted padding
    ctx.textAlign = 'right'; // Ensure right alignment for the bottom value
    ctx.fillText(card.card.value, card.x + CARD_WIDTH - 20, card.y + CARD_HEIGHT - 10); // Reduced padding
};

const renderCard = card => {
    // Always render the card based on its current state
    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; // Toggle card face
            renderAllCards(gameState.cards); // Re-render all 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
        card.isFaceUp = false; // Ensure cards start face down
    });

    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());
});