server.js

const express = require('express');
const http = require('http');
const WebSocket = require('ws');
const { v4: uuidv4 } = require('uuid');

// Define Game Classes
class Card {
    constructor(value, suit) {
        this.value = value;
        this.suit = suit; // '♠', '♥', '♦', '♣'
    }

    toString() {
        return `${this.value}${this.suit}`;
    }

    getSuitClass() {
        switch (this.suit) {
            case '♠':
                return 'spades';
            case '♥':
                return 'hearts';
            case '♦':
                return 'diamonds';
            case '♣':
                return 'clubs';
            default:
                return '';
        }
    }

    getColor() {
        return (this.suit === '♥' || this.suit === '♦') ? 'red' : 'black';
    }
}

class Player {
    constructor(id, username, ws) {
        this.id = id;
        this.username = username;
        this.ws = ws;
        this.hand = [];
        this.bulletSlots = 6;
        this.isEliminated = false;
    }
}

class Game {
    constructor(roomId, players) {
        this.roomId = roomId;
        this.players = players; // Array of Player objects
        this.currentPlayerIndex = 0;
        this.coreCard = null;
        this.lastPlayedCards = [];
        this.deck = [];
        this.isGameOver = false;
        this.centralTableCards = [];
        this.shootingPlayer = null; // Player object
        this.initializeDeck();
        this.shuffleDeck();
        this.dealCards();
    }

    initializeDeck() {
        const values = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'];
        const suits = ['♠', '♥', '♦', '♣'];
        this.deck = [];
        for (let value of values) {
            for (let suit of suits) {
                this.deck.push(new Card(value, suit));
            }
        }
    }

    shuffleDeck() {
        for (let i = this.deck.length - 1; i > 0; i--) {
            const j = Math.floor(Math.random() * (i + 1));
            [this.deck[i], this.deck[j]] = [this.deck[j], this.deck[i]];
        }
    }

    dealCards() {
        // Assign a core card
        if (this.deck.length === 0) {
            throw new Error('Deck is empty. Cannot deal cards.');
        }
        this.coreCard = this.deck.shift();

        // Deal 5 cards to each player
        this.players.forEach(player => {
            for (let i = 0; i < 5; i++) {
                if (this.deck.length > 0) {
                    player.hand.push(this.deck.shift());
                }
            }
        });
    }

    getCurrentPlayer() {
        return this.players[this.currentPlayerIndex];
    }

    nextPlayer() {
        if (this.isGameOver) return;
        let attempts = 0;
        do {
            this.currentPlayerIndex = (this.currentPlayerIndex + 1) % this.players.length;
            attempts++;
            if (attempts > this.players.length) break; // Prevent infinite loop
        } while (this.getCurrentPlayer().isEliminated);
    }

    getGameState(requestingPlayerId) {
        return {
            roomId: this.roomId,
            players: this.players.map(player => ({
                id: player.id,
                username: player.username,
                isEliminated: player.isEliminated,
                bulletSlots: player.bulletSlots,
                // Only send hand to the requesting player
                hand: player.id === requestingPlayerId ? player.hand.map(card => card.toString()) : undefined
            })),
            currentPlayer: this.getCurrentPlayer().username,
            coreCard: this.coreCard ? this.coreCard.toString() : '-',
            lastPlayedCount: this.lastPlayedCards.length, // Only send count, not actual cards
            centralTableCards: this.centralTableCards.map(cardObj => ({
                card: cardObj.card.toString(),
                revealed: cardObj.revealed
            })),
            isGameOver: this.isGameOver
        };
    }

    // Assign a new core card for the next round
    assignNewCoreCard() {
        if (this.deck.length === 0) {
            // Reshuffle central table cards back into the deck if needed
            this.deck = this.centralTableCards.map(cardObj => cardObj.card);
            this.shuffleDeck();
            this.centralTableCards = [];
        }
        if (this.deck.length > 0) {
            this.coreCard = this.deck.shift();
        } else {
            // If deck is still empty, set coreCard to null (shouldn't happen with proper deck management)
            this.coreCard = null;
        }
    }
}

// Initialize Express server
const app = express();
const server = http.createServer(app);
const wss = new WebSocket.Server({ server });

// Serve static files from 'public' directory
app.use(express.static('public'));

// Define the single room ID
const ROOM_ID = 'ABC123';

// Initialize the room
const room = {
    id: ROOM_ID,
    creator: null, // Will be set to the first player
    players: [], // Array of Player objects
    isStarted: false
};

// Initialize games
let currentGame = null;

// Handle WebSocket connections
wss.on('connection', (ws) => {
    ws.id = uuidv4(); // Assign a unique ID to each connection

    ws.on('message', (message) => {
        let data;
        try {
            data = JSON.parse(message);
        } catch (e) {
            ws.send(JSON.stringify({ type: 'ERROR', message: 'Invalid JSON' }));
            return;
        }

        switch (data.type) {
            case 'JOIN_ROOM':
                handleJoinRoom(ws, data);
                break;
            case 'START_GAME':
                handleStartGame(ws, data);
                break;
            case 'PLAY_CARDS':
                handlePlayCards(ws, data);
                break;
            case 'CALL_BLUFF':
                handleCallBluff(ws, data);
                break;
            case 'PULL_TRIGGER':
                handlePullTrigger(ws, data);
                break;
            case 'RESET_ROOM':
                handleResetRoom(ws, data);
                break;
            default:
                ws.send(JSON.stringify({ type: 'ERROR', message: 'Unknown command' }));
        }
    });

    ws.on('close', () => {
        handleDisconnect(ws);
    });
});

// Handle joining the room
function handleJoinRoom(ws, data) {
    const username = data.username;
    const dataRoomId = data.roomId;

    if (!username) {
        ws.send(JSON.stringify({ type: 'ERROR', message: 'Username is required to join the room' }));
        return;
    }

    // Validate room ID
    if (dataRoomId !== ROOM_ID) {
        ws.send(JSON.stringify({ type: 'ERROR', message: 'Room not found' }));
        return;
    }

    if (room.isStarted) {
        ws.send(JSON.stringify({ type: 'ERROR', message: 'Game already started in this room. Please wait for the next round.' }));
        return;
    }

    if (room.players.length >= 5) { // Assuming max 5 players
        ws.send(JSON.stringify({ type: 'ERROR', message: 'Room is full' }));
        return;
    }

    const playerId = uuidv4();
    const player = new Player(playerId, username, ws);
    room.players.push(player);

    ws.roomId = room.id;
    ws.playerId = playerId;

    // Assign creator if first player
    let isCreator = false;
    if (room.creator === null) {
        room.creator = playerId;
        isCreator = true;
    }

    // Notify all players in the room about the new player and current creator
    broadcastToRoom(room.id, {
        type: 'PLAYER_JOINED',
        players: room.players.map(p => p.username),
        creator: isCreator ? player.username : getPlayerById(room.creator).username
    });

    // If the new player is the creator, send a separate message
    if (isCreator) {
        ws.send(JSON.stringify({ type: 'CREATOR', isCreator: true }));
    }
}

// Handle starting the game
function handleStartGame(ws, data) {
    const roomId = ws.roomId;
    const playerId = ws.playerId;

    if (roomId !== ROOM_ID) {
        ws.send(JSON.stringify({ type: 'ERROR', message: 'Invalid room' }));
        return;
    }

    if (room.creator !== playerId) {
        ws.send(JSON.stringify({ type: 'ERROR', message: 'Only the room creator can start the game' }));
        return;
    }

    if (room.isStarted) {
        ws.send(JSON.stringify({ type: 'ERROR', message: 'Game already started' }));
        return;
    }

    if (room.players.length < 2) {
        ws.send(JSON.stringify({ type: 'ERROR', message: 'At least two players are required to start the game' }));
        return;
    }

    try {
        // Initialize game
        currentGame = new Game(roomId, room.players);
        room.isStarted = true;
    } catch (error) {
        ws.send(JSON.stringify({ type: 'ERROR', message: error.message }));
        return;
    }

    // Broadcast game start and initial game state to all players
    room.players.forEach(player => {
        player.ws.send(JSON.stringify({
            type: 'GAME_STARTED',
            gameState: currentGame.getGameState(player.id),
            hand: player.hand.map(card => card.toString())
        }));
    });
}

// Handle playing cards
function handlePlayCards(ws, data) {
    if (!currentGame || currentGame.isGameOver) {
        ws.send(JSON.stringify({ type: 'ERROR', message: 'Game not found or already over' }));
        return;
    }

    const playerId = ws.playerId;
    const selectedIndices = data.selectedIndices; // Array of indices

    const player = currentGame.players.find(p => p.id === playerId);
    if (!player) {
        ws.send(JSON.stringify({ type: 'ERROR', message: 'Player not found in the game' }));
        return;
    }

    if (currentGame.getCurrentPlayer().id !== playerId) {
        ws.send(JSON.stringify({ type: 'ERROR', message: 'Not your turn' }));
        return;
    }

    if (!Array.isArray(selectedIndices) || selectedIndices.length === 0) {
        ws.send(JSON.stringify({ type: 'ERROR', message: 'No cards selected' }));
        return;
    }

    // Validate selected indices
    if (selectedIndices.some(index => index < 0 || index >= player.hand.length)) {
        ws.send(JSON.stringify({ type: 'ERROR', message: 'Invalid card indices' }));
        return;
    }

    // Get selected cards
    const selectedCards = selectedIndices.map(index => player.hand[index]);

    // Remove selected cards from player's hand (reverse sort to remove from higher index to lower)
    selectedIndices.sort((a, b) => b - a);
    selectedIndices.forEach(index => {
        player.hand.splice(index, 1);
    });

    // Update game state's last played cards
    currentGame.lastPlayedCards = selectedCards;

    // **Reset the central table to only hold the latest played cards**
    currentGame.centralTableCards = []; // Clear previous cards

    // Add to central table as face-down
    selectedCards.forEach(card => {
        currentGame.centralTableCards.push({
            card: card,
            revealed: false
        });
    });

    // Broadcast game state to all players
    broadcastGameState(room.id);

    // Move to next player
    currentGame.nextPlayer();

    // Broadcast updated game state after moving the turn
    broadcastGameState(room.id);
}

// Handle calling bluff
function handleCallBluff(ws, data) {
    if (!currentGame || currentGame.isGameOver) {
        ws.send(JSON.stringify({ type: 'ERROR', message: 'Game not found or already over' }));
        return;
    }

    const playerId = ws.playerId;
    const caller = currentGame.players.find(p => p.id === playerId);
    if (!caller) {
        ws.send(JSON.stringify({ type: 'ERROR', message: 'Player not found in the game' }));
        return;
    }

    // Cannot call bluff if no one has played yet
    if (currentGame.lastPlayedCards.length === 0) {
        ws.send(JSON.stringify({ type: 'ERROR', message: 'No cards have been played yet' }));
        return;
    }

    // Determine if the last played cards are a bluff
    // Assuming in this game, players must play the coreCard value
    const coreCard = currentGame.coreCard;
    if (!coreCard) {
        ws.send(JSON.stringify({ type: 'ERROR', message: 'Core card is not set' }));
        return;
    }

    const actual = currentGame.lastPlayedCards.every(card => card.value === coreCard.value);

    if (!actual) {
        // Bluff was called, and player who played the last cards was bluffing
        const lastPlayerIndex = (currentGame.currentPlayerIndex - 1 + currentGame.players.length) % currentGame.players.length;
        const lastPlayer = currentGame.players[lastPlayerIndex];

        lastPlayer.bulletSlots -= 1;

        // Check if player is eliminated
        const isEliminated = lastPlayer.bulletSlots <= 0;
        if (isEliminated) {
            lastPlayer.isEliminated = true;
        }

        // Notify all players about the result
        broadcastToRoom(room.id, {
            type: 'BLUFF_RESULT',
            result: 'PLAYER_BLUFFED',
            player: lastPlayer.username,
            bulletSlots: lastPlayer.bulletSlots,
            isEliminated: isEliminated,
            centralTableCards: currentGame.centralTableCards.map(cardObj => ({
                card: cardObj.card.toString(),
                revealed: true // Reveal the cards
            })),
            coreCard: coreCard.toString()
        });

        // **Set shootingPlayer to the last player who bluffed**
        currentGame.shootingPlayer = lastPlayer;

        // Do not clear played cards and central table here
        // currentGame.lastPlayedCards = [];
        // currentGame.centralTableCards = [];

        // Do not assign a new core card here; wait until after the shot is resolved
    } else {
        // Bluff was called, but it was not a bluff
        caller.bulletSlots -= 1;

        // Check if caller is eliminated
        const isEliminated = caller.bulletSlots <= 0;
        if (isEliminated) {
            caller.isEliminated = true;
        }

        // Notify all players about the result
        broadcastToRoom(room.id, {
            type: 'BLUFF_RESULT',
            result: 'PLAYER_NOT_BLUFFED',
            player: caller.username,
            bulletSlots: caller.bulletSlots,
            isEliminated: isEliminated,
            centralTableCards: currentGame.centralTableCards.map(cardObj => ({
                card: cardObj.card.toString(),
                revealed: true // Reveal the cards
            })),
            coreCard: coreCard.toString()
        });

        // **Set shootingPlayer to the caller who called a false bluff**
        currentGame.shootingPlayer = caller;

        // Do not clear played cards and central table here
        // currentGame.lastPlayedCards = [];
        // currentGame.centralTableCards = [];

        // Do not assign a new core card here; wait until after the shot is resolved
    }

    // No longer assign a new core card or clear centralTableCards here
    // The shot overlay will handle the next steps
}

// Handle pulling trigger
function handlePullTrigger(ws, data) {
    if (!currentGame || currentGame.isGameOver) {
        ws.send(JSON.stringify({ type: 'ERROR', message: 'Game not found or already over' }));
        return;
    }

    const playerId = ws.playerId;

    if (!currentGame.shootingPlayer) {
        ws.send(JSON.stringify({ type: 'ERROR', message: 'No shooting action pending' }));
        return;
    }

    const shootingPlayer = currentGame.shootingPlayer;

    // **Ensure that only the shooting player can pull the trigger**
    if (shootingPlayer.id !== playerId) {
        ws.send(JSON.stringify({ type: 'ERROR', message: 'You are not authorized to pull the trigger' }));
        return;
    }

    // Pull the trigger: determine if the player is shot
    const bulletChance = 1 / shootingPlayer.bulletSlots;
    const shot = Math.random() < bulletChance;

    if (shot) {
        shootingPlayer.bulletSlots -= 1;
        shootingPlayer.isEliminated = true;

        broadcastToRoom(room.id, {
            type: 'SHOT_RESULT',
            result: 'SHOT',
            player: shootingPlayer.username,
            bulletSlots: shootingPlayer.bulletSlots,
            isEliminated: shootingPlayer.isEliminated
        });
    } else {
        shootingPlayer.bulletSlots -= 1;

        broadcastToRoom(room.id, {
            type: 'SHOT_RESULT',
            result: 'SURVIVED',
            player: shootingPlayer.username,
            bulletSlots: shootingPlayer.bulletSlots,
            isEliminated: shootingPlayer.isEliminated
        });
    }

    // Reset shooting player
    currentGame.shootingPlayer = null;

    // Check if the game is over
    const remainingPlayers = currentGame.players.filter(p => !p.isEliminated);
    if (remainingPlayers.length === 1) {
        currentGame.isGameOver = true;
        broadcastToRoom(room.id, {
            type: 'GAME_OVER',
            winner: remainingPlayers[0].username
        });
        currentGame = null;
        room.isStarted = false;
        return;
    }

    // Reset for next round
    // Assign a new core card here after the shot is resolved
    currentGame.assignNewCoreCard();

    // Move to next player
    currentGame.nextPlayer();

    // Broadcast updated game state
    broadcastGameState(room.id);

    // Clear central table cards for the next round
    currentGame.centralTableCards = [];
}

// Handle resetting the room
function handleResetRoom(ws, data) {
    const playerId = ws.playerId;
    if (room.creator !== playerId) {
        ws.send(JSON.stringify({ type: 'ERROR', message: 'Only the room creator can reset the room' }));
        return;
    }

    // Reset game state
    if (currentGame && !currentGame.isGameOver) {
        currentGame.isGameOver = true;
        broadcastToRoom(room.id, {
            type: 'GAME_OVER',
            winner: null,
            message: 'Game has been reset by the creator.'
        });
        currentGame = null;
    }

    // Reset room state
    room.isStarted = false;

    // Notify all players to reset
    broadcastToRoom(room.id, {
        type: 'RESET',
        message: 'Room has been reset. You can join again.'
    });

    // Reset all players' states
    room.players.forEach(player => {
        player.hand = [];
        player.bulletSlots = 6;
        player.isEliminated = false;
    });
}

// Handle disconnects
function handleDisconnect(ws) {
    const roomId = ws.roomId;
    const playerId = ws.playerId;

    if (roomId !== ROOM_ID) return;

    const playerIndex = room.players.findIndex(p => p.id === playerId);
    if (playerIndex === -1) return;

    const player = room.players.splice(playerIndex, 1)[0];

    // Notify remaining players
    broadcastToRoom(room.id, {
        type: 'PLAYER_LEFT',
        players: room.players.map(p => p.username)
    });

    // If game is ongoing, mark player as eliminated
    if (currentGame) {
        const gamePlayer = currentGame.players.find(p => p.id === playerId);
        if (gamePlayer) {
            gamePlayer.isEliminated = true;

            // Check for game over
            const remainingPlayers = currentGame.players.filter(p => !p.isEliminated);
            if (remainingPlayers.length === 1) {
                currentGame.isGameOver = true;
                broadcastToRoom(room.id, {
                    type: 'GAME_OVER',
                    winner: remainingPlayers[0].username
                });
                currentGame = null;
                room.isStarted = false;
                return;
            }

            // Broadcast updated game state
            broadcastGameState(room.id);
        }
    }

    // If room is empty, reset creator and game state
    if (room.players.length === 0) {
        room.creator = null;
        room.isStarted = false;
        currentGame = null;
    } else {
        // If creator disconnected, assign a new creator
        if (room.creator === playerId) {
            room.creator = room.players[0].id;
            room.players[0].ws.send(JSON.stringify({
                type: 'NEW_CREATOR',
                newCreator: room.players[0].username
            }));
        }
    }
}

// Broadcast game state to all players in the room
function broadcastGameState(roomId) {
    if (!currentGame) return;

    room.players.forEach(player => {
        player.ws.send(JSON.stringify({
            type: 'GAME_UPDATE',
            gameState: currentGame.getGameState(player.id)
        }));
    });
}

// Broadcast a message to all players in a room
function broadcastToRoom(roomId, message) {
    const targetRoom = roomId === ROOM_ID ? room : null;
    if (!targetRoom) return;

    targetRoom.players.forEach(player => {
        if (player.ws.readyState === WebSocket.OPEN) {
            player.ws.send(JSON.stringify(message));
        }
    });
}

// Helper function to get a player by ID
function getPlayerById(playerId) {
    return room.players.find(p => p.id === playerId);
}

const PORT = process.env.PORT || 3000;

server.listen(PORT, () => {
    console.log(`Server is listening on port ${PORT}`);
});
index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Card Bluff & Shoot - Multiplayer</title>
    <link rel="stylesheet" href="style.css">
    <style>
        /* Basic styling for visibility */
        .selected {
            border: 2px solid blue;
        }
        .player {
            margin: 5px 0;
        }
        .active {
            background-color: #e0ffe0;
        }
        .eliminated {
            text-decoration: line-through;
            color: gray;
        }
        .card {
            display: inline-block;
            width: 50px;
            height: 70px;
            border: 1px solid #000;
            border-radius: 5px;
            margin: 5px;
            text-align: center;
            line-height: 70px;
            font-size: 1.2em;
            cursor: pointer;
            position: relative;
            perspective: 1000px; /* For flip animation */
        }
        .card-inner {
            position: absolute;
            width: 100%;
            height: 100%;
            transition: transform 0.6s;
            transform-style: preserve-3d;
        }
        .card.flipped .card-inner {
            transform: rotateY(180deg);
        }
        .card-front, .card-back {
            position: absolute;
            width: 100%;
            height: 100%;
            backface-visibility: hidden;
            border: 1px solid #000;
            border-radius: 5px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 1.2em;
        }
        .card-front {
            background-color: #fff;
            color: inherit; /* Inherit color for suit */
        }
        .card-back {
            background-color: #333;
            color: #333;
            transform: rotateY(180deg);
        }
        .hearts, .diamonds {
            color: red;
        }
        .spades, .clubs {
            color: black;
        }
        .match {
            border: 2px solid green; /* Highlight matching cards */
        }
        .mismatch {
            border: 2px solid red; /* Highlight non-matching cards */
        }
        .overlay-content {
            background-color: rgba(0, 0, 0, 0.8);
            color: white;
            padding: 20px;
            border-radius: 10px;
            text-align: center;
        }
        .overlay-button {
            padding: 10px 20px;
            margin-top: 15px;
            font-size: 1em;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div class="container">
        <!-- Waiting Room Section -->
        <div id="waitingRoom" class="section">
            <h3>Room: <span id="roomDisplay">ABC123</span></h3>
            <div id="playersList">
                <h4>Players in room:</h4>
                <!-- List of players will be appended here -->
            </div>
            <button id="startGameBtn" style="display: none;">Start Game</button>
            <button id="resetRoomBtn" style="display: none; margin-top: 10px;">Reset Room</button>
        </div>

        <!-- Game Section -->
        <div id="gameSection" class="game-container" style="display: none;">
            <div class="top-section" id="players"></div>

            <div class="middle-section">
                <div class="game-info">
                    <strong>Core Card:</strong> <span id="coreCard">-</span>
                </div>
                <div class="game-info">
                    <strong>Last Played:</strong> <span id="lastPlayed">None</span>
                </div>
                <!-- Central Card Table -->
                <div class="central-table" id="centralTable"></div>
            </div>

            <div class="bottom-section">
                <div class="player-hand" id="currentHand"></div>
                <div class="actions">
                    <button id="playButton" onclick="playSelectedCards()">Play Selected Cards</button>
                    <button id="callBluffButton" onclick="callBluff()" style="background: #f44336">Call Bluff</button>
                </div>
            </div>

            <!-- Overlay for Bluff Result -->
            <div class="overlay" id="resultOverlay" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; 
                 background: rgba(0,0,0,0.5); align-items: center; justify-content: center;">
                <div class="overlay-content">
                    <h2 id="overlayTitle">Result</h2>
                    <p id="overlayMessage"></p>
                    <button id="overlayButton" class="overlay-button" onclick="handleOverlayAction()">Shoot</button>
                </div>
            </div>
        </div>

        <!-- Game Over Overlay -->
        <div class="shooting-overlay" id="gameOverOverlay" style="display: none;">
            <div class="overlay-content">
                <h2>Game Over!</h2>
                <p id="gameOverText"></p>
                <button onclick="resetToLobby()" class="overlay-button">Return to Lobby</button>
            </div>
        </div>
    </div>

    <script>
        let ws;
        let username;
        const roomId = 'ABC123'; // Predefined room ID
        let isCreator = false;
        let userHand = [];
        let gameState = null;
        let isWaitingForShot = false; // Flag to prevent multiple shots

        // Utility function to generate random username
        function generateRandomUsername() {
            const adjectives = ['Swift', 'Silent', 'Mighty', 'Brave', 'Clever', 'Nimble', 'Fierce', 'Sly', 'Bold', 'Wise'];
            const nouns = ['Lion', 'Tiger', 'Eagle', 'Shark', 'Wolf', 'Panther', 'Dragon', 'Falcon', 'Leopard', 'Cobra'];
            const adj = adjectives[Math.floor(Math.random() * adjectives.length)];
            const noun = nouns[Math.floor(Math.random() * nouns.length)];
            const number = Math.floor(Math.random() * 1000);
            return `${adj}${noun}${number}`;
        }

        // Connect to WebSocket server
        function connectWebSocket() {
            ws = new WebSocket('ws://localhost:3000'); // Ensure this URL matches your server's address and port

            ws.onopen = () => {
                console.log('Connected to server');
                // Assign random username
                username = generateRandomUsername();
                // Send JOIN_ROOM message
                ws.send(JSON.stringify({
                    type: 'JOIN_ROOM',
                    username: username,
                    roomId: roomId
                }));
            };

            ws.onmessage = (event) => {
                const data = JSON.parse(event.data);
                handleWebSocketMessage(data);
            };

            ws.onclose = () => {
                console.log('Disconnected from server');
                alert('Disconnected from server');
                resetToLobby();
            };
        }

        // Handle WebSocket messages
        function handleWebSocketMessage(data) {
            switch(data.type) {
                case 'PLAYER_JOINED':
                    updatePlayersList(data.players, data.creator);
                    break;
                case 'PLAYER_LEFT':
                    updatePlayersList(data.players, null); // Creator remains unchanged
                    break;
                case 'NEW_CREATOR':
                    isCreator = false;
                    alert(`New room creator is: ${data.newCreator}`);
                    updateStartButton(); // Update button visibility
                    break;
                case 'CREATOR':
                    isCreator = data.isCreator;
                    updateStartButton();
                    break;
                case 'GAME_STARTED':
                    gameState = data.gameState;
                    userHand = data.hand || [];
                    startGameUI(data.gameState);
                    break;
                case 'GAME_UPDATE':
                    gameState = data.gameState;
                    if (data.gameState.hand !== undefined) {
                        userHand = data.gameState.hand || [];
                    }
                    updateGameUI();
                    break;
                case 'BLUFF_RESULT':
                    handleBluffResult(data);
                    break;
                case 'SHOT_RESULT':
                    handleShotResult(data);
                    break;
                case 'GAME_OVER':
                    showGameOver(data);
                    break;
                case 'RESET':
                    alert(data.message);
                    resetToLobby();
                    break;
                case 'ERROR':
                    alert(data.message);
                    break;
                default:
                    console.log('Unknown message type:', data.type);
            }
        }

        // Update players list in waiting room
        function updatePlayersList(players, creatorUsername) {
            const list = document.getElementById('playersList');
            list.innerHTML = '<h4>Players in room:</h4>';
            players.forEach(player => {
                const div = document.createElement('div');
                div.className = 'player';
                div.textContent = player;
                list.appendChild(div);
            });

            // Determine if the current user is the creator
            if (creatorUsername && creatorUsername === username) {
                isCreator = true;
                updateStartButton();
            }

            // Show start button only to creator and if at least two players
            if (isCreator && players.length >=2 ) {
                document.getElementById('startGameBtn').style.display = 'block';
            } else {
                document.getElementById('startGameBtn').style.display = 'none';
            }

            // Show reset button only to creator
            if (isCreator) {
                document.getElementById('resetRoomBtn').style.display = 'block';
            } else {
                document.getElementById('resetRoomBtn').style.display = 'none';
            }
        }

        // Update start button based on creator status and player count
        function updateStartButton() {
            const players = document.querySelectorAll('#playersList .player');
            if (isCreator && players.length >= 2) {
                document.getElementById('startGameBtn').style.display = 'block';
            } else {
                document.getElementById('startGameBtn').style.display = 'none';
            }

            // Show reset button only to creator
            if (isCreator) {
                document.getElementById('resetRoomBtn').style.display = 'block';
            } else {
                document.getElementById('resetRoomBtn').style.display = 'none';
            }
        }

        // Start Game UI
        function startGameUI(gameStateData) {
            document.getElementById('waitingRoom').style.display = 'none';
            document.getElementById('gameSection').style.display = 'grid';
            renderGame(gameStateData);
        }

        // Update Game UI
        function updateGameUI() {
            if (!gameState) return;
            renderGame(gameState);
        }

        // Render Game UI
        function renderGame(gameState) {
            // Render players
            const playersContainer = document.getElementById('players');
            playersContainer.innerHTML = '';
            gameState.players.forEach(player => {
                const playerEl = document.createElement('div');
                playerEl.className = `player ${player.username === gameState.currentPlayer ? 'active' : ''} ${player.isEliminated ? 'eliminated' : ''}`;
                playerEl.innerHTML = `
                    <strong>${player.username}${player.username === username ? ' (You)' : ''}</strong>
                    <div class="bullets">Bullets: ${player.bulletSlots}/6</div>
                `;
                playersContainer.appendChild(playerEl);
            });

            // Render core card
            const coreCardSpan = document.getElementById('coreCard');
            if (gameState.coreCard && gameState.coreCard !== '-') {
                coreCardSpan.innerHTML = formatCard(gameState.coreCard);
            } else {
                coreCardSpan.textContent = '-';
            }

            // Render last played
            const lastPlayedSpan = document.getElementById('lastPlayed');
            if (gameState.lastPlayedCount > 0) {
                lastPlayedSpan.textContent = `${gameState.lastPlayedCount} card(s) played`;
            } else {
                lastPlayedSpan.textContent = 'None';
            }

            // Render central table
            const centralTable = document.getElementById('centralTable');
            centralTable.innerHTML = '';
            gameState.centralTableCards.forEach(cardObj => {
                const cardEl = document.createElement('div');
                if (!cardObj.revealed) {
                    // Keep cards hidden
                    cardEl.className = `card`;
                    cardEl.innerHTML = `
                        <div class="card-inner">
                            <div class="card-front">🂠</div>
                            <div class="card-back">🂠</div>
                        </div>
                    `;
                } else {
                    // Reveal cards using flip animation
                    cardEl.className = `card flipped ${getSuitClass(cardObj.card)}`;
                    cardEl.innerHTML = `
                        <div class="card-inner">
                            <div class="card-front">${cardObj.card}</div>
                            <div class="card-back">🂠</div>
                        </div>
                    `;

                    // Set color based on suit
                    cardEl.style.color = getCardColor(cardObj.card);

                    // Highlight card based on core card matching
                    const coreValue = gameState.coreCard.slice(0, -1); // Extract value without suit
                    const cardValue = cardObj.card.slice(0, -1); // Extract value without suit

                    if (cardValue === coreValue) {
                        cardEl.classList.add('match');
                    } else {
                        cardEl.classList.add('mismatch');
                    }
                }
                centralTable.appendChild(cardEl);
            });

            // Always render current player's hand
            const handContainer = document.getElementById('currentHand');
            handContainer.innerHTML = '';
            userHand.forEach((card, index) => {
                const cardEl = document.createElement('div');
                cardEl.className = `card ${getSuitClass(card)}`;
                cardEl.style.color = getCardColor(card);
                cardEl.dataset.index = index;
                cardEl.textContent = card; // Display the card face
                cardEl.onclick = () => {
                    // Only allow selection if it's the player's turn
                    if (gameState.currentPlayer === username && !gameState.isGameOver) {
                        cardEl.classList.toggle('selected');
                    }
                };
                handContainer.appendChild(cardEl);
            });

            // Enable/Disable action buttons based on turn
            if (gameState.currentPlayer === username && !gameState.isGameOver) {
                document.getElementById('playButton').disabled = (userHand.length === 0);
                document.getElementById('callBluffButton').disabled = (gameState.lastPlayedCount === 0);
            } else {
                document.getElementById('playButton').disabled = true;
                document.getElementById('callBluffButton').disabled = true;
            }
        }

        // Format card HTML
        function formatCard(cardStr) {
            const color = getCardColor(cardStr);
            return `<span style="color: ${color};">${cardStr}</span>`;
        }

        // Get card color based on suit
        function getCardColor(cardStr) {
            const suit = cardStr.slice(-1);
            return (suit === '♥' || suit === '♦') ? 'red' : 'black';
        }

        // Get suit class for styling
        function getSuitClass(cardStr) {
            const suit = cardStr.slice(-1);
            return suit === '♠' ? 'spades' :
                   suit === '♣' ? 'clubs' :
                   suit === '♥' ? 'hearts' :
                   'diamonds';
        }

        // Play selected cards
        function playSelectedCards() {
            // Get selected cards
            const selectedCards = Array.from(document.querySelectorAll('.card.selected'));
            if (selectedCards.length === 0) {
                alert('Please select at least one card to play.');
                return;
            }

            const selectedIndices = selectedCards.map(card => parseInt(card.dataset.index));

            // Send PLAY_CARDS message to server
            ws.send(JSON.stringify({
                type: 'PLAY_CARDS',
                selectedIndices: selectedIndices
            }));

            // Remove selected cards from userHand
            selectedIndices.sort((a, b) => b - a); // Remove from higher index to lower
            selectedIndices.forEach(index => {
                userHand.splice(index, 1);
            });

            // Clear selected classes
            selectedCards.forEach(card => {
                card.classList.remove('selected');
            });
        }

        // Call bluff
        function callBluff() {
            // Send CALL_BLUFF message to server
            ws.send(JSON.stringify({
                type: 'CALL_BLUFF'
            }));
        }

        // Handle Bluff Result
        function handleBluffResult(data) {
            // Reveal the central table cards based on 'revealed' flag
            const centralTable = document.getElementById('centralTable');
            centralTable.innerHTML = '';
            const coreCard = data.coreCard;

            data.centralTableCards.forEach(cardObj => {
                const cardEl = document.createElement('div');
                if (!cardObj.revealed) {
                    // Keep cards hidden
                    cardEl.className = `card`;
                    cardEl.innerHTML = `
                        <div class="card-inner">
                            <div class="card-front">🂠</div>
                            <div class="card-back">🂠</div>
                        </div>
                    `;
                } else {
                    // Reveal cards using flip animation
                    cardEl.className = `card flipped ${getSuitClass(cardObj.card)}`;
                    cardEl.innerHTML = `
                        <div class="card-inner">
                            <div class="card-front">${cardObj.card}</div>
                            <div class="card-back">🂠</div>
                        </div>
                    `;

                    // Set color based on suit
                    cardEl.style.color = getCardColor(cardObj.card);

                    // Highlight card based on core card matching
                    const coreValue = coreCard.slice(0, -1); // Extract value without suit
                    const cardValue = cardObj.card.slice(0, -1); // Extract value without suit

                    if (cardValue === coreValue) {
                        cardEl.classList.add('match');
                    } else {
                        cardEl.classList.add('mismatch');
                    }
                }
                centralTable.appendChild(cardEl);
            });

            // After a 2-second delay, show the shooting overlay
            setTimeout(() => {
                const overlay = document.getElementById('resultOverlay');
                const overlayTitle = document.getElementById('overlayTitle');
                const overlayMessage = document.getElementById('overlayMessage');
                const overlayButton = document.getElementById('overlayButton');

                // Determine if the current user is the one who lost the bluff
                const lostPlayer = data.player;
                const isUser = lostPlayer === username;

                if (isUser) {
                    overlayTitle.textContent = '⛳ You must Trigger the Gun!';
                    overlayMessage.textContent = 'Click the button below to Pull the Trigger.';
                    overlayButton.style.display = 'inline-block';
                    overlayButton.textContent = 'Shoot';
                } else {
                    overlayTitle.textContent = `${lostPlayer} must Trigger the Gun!`;
                    overlayMessage.textContent = 'Wait for the outcome.';
                    overlayButton.style.display = 'none';
                }

                overlay.style.display = 'flex';
            }, 2000); // 2-second delay
        }

        // Handle Shot Result
        function handleShotResult(data) {
            const overlay = document.getElementById('resultOverlay');
            const overlayTitle = document.getElementById('overlayTitle');
            const overlayMessage = document.getElementById('overlayMessage');
            const overlayButton = document.getElementById('overlayButton');

            if (data.player === username) {
                if (data.result === 'SHOT') {
                    overlayTitle.textContent = '💀 You have been Shot!';
                    overlayMessage.textContent = 'You have been eliminated.';
                } else if (data.result === 'SURVIVED') {
                    overlayTitle.textContent = '✨ You Survived!';
                    overlayMessage.textContent = 'You can continue playing.';
                }
            } else {
                // For observers or other players
                overlayTitle.textContent = `${data.player} has ${data.result === 'SHOT' ? 'been Shot!' : 'Survived!'}`;
                overlayMessage.textContent = '';
            }

            // Hide the 'Shoot' button if present
            overlayButton.style.display = 'none';

            // After 2 seconds, hide the overlay and reset the game state
            setTimeout(() => {
                overlay.style.display = 'none';
                // Clear the central table (if desired, based on game rules)
                // centralTable.innerHTML = '';
                // Update game UI via GAME_UPDATE
                // If game is over, GAME_OVER will handle the UI
            }, 2000);
        }

        // Show game over overlay
        function showGameOver(data) {
            document.getElementById('gameSection').style.display = 'none';
            document.getElementById('gameOverOverlay').style.display = 'flex';
            if (data.winner) {
                document.getElementById('gameOverText').textContent = `Winner: ${data.winner}`;
            } else if (data.message) {
                document.getElementById('gameOverText').textContent = data.message;
            } else {
                document.getElementById('gameOverText').textContent = 'The game has ended.';
            }
        }

        // Reset to lobby
        function resetToLobby() {
            document.getElementById('gameSection').style.display = 'none';
            document.getElementById('gameOverOverlay').style.display = 'none';
            document.getElementById('resultOverlay').style.display = 'none';
            document.getElementById('waitingRoom').style.display = 'block';
            // Reset variables
            // roomId is constant 'ABC123'
            isCreator = false;
            gameState = null;
            userHand = [];
            isWaitingForShot = false;
            // Reload the page to rejoin
            window.location.reload();
        }

        // Handle overlay button (Shoot)
        function handleOverlayAction() {
            if (isWaitingForShot) return; // Prevent multiple shots
            isWaitingForShot = true;
            // Send PULL_TRIGGER message to server
            ws.send(JSON.stringify({
                type: 'PULL_TRIGGER'
            }));
        }

        // Handle start game button
        document.getElementById('startGameBtn').addEventListener('click', () => {
            ws.send(JSON.stringify({
                type: 'START_GAME'
            }));
        });

        // Handle reset room button
        document.getElementById('resetRoomBtn').addEventListener('click', () => {
            if (confirm('Are you sure you want to reset the room? This will end the current game.')) {
                ws.send(JSON.stringify({
                    type: 'RESET_ROOM'
                }));
            }
        });

        // Initialize connection on page load
        window.onload = () => {
            username = generateRandomUsername();
            connectWebSocket();
        };
    </script>
</body>
</html>
style.css

/* General Styles */
body {
    margin: 0;
    padding: 20px;
    background: #1a1a1a;
    color: white;
    font-family: Arial, sans-serif;
}

.container {
    max-width: 1200px;
    margin: 0 auto;
}

.section {
    background: #333;
    padding: 20px;
    border-radius: 8px;
    margin-bottom: 20px;
}

h2, h3, h4 {
    margin-top: 0;
}

input, button {
    padding: 10px;
    margin: 5px 0;
    width: 100%;
    box-sizing: border-box;
    border: none;
    border-radius: 5px;
}

button {
    background: #4CAF50;
    color: white;
    cursor: pointer;
    transition: background 0.3s;
}

button:hover {
    background: #45a049;
}

button:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

#playersList .player {
    padding: 10px;
    background: #444;
    margin: 5px 0;
    border-radius: 4px;
}

/* Game UI Styles */
.game-container {
    display: none; /* Hidden by default */
    width: 100%;
    min-height: 100vh;
    display: grid;
    grid-template-rows: auto 2fr 1fr;
    gap: 20px;
}

.top-section, .bottom-section {
    display: flex;
    justify-content: space-around;
    align-items: center;
    flex-wrap: wrap;
}

.player {
    background: #333;
    padding: 10px 20px;
    border-radius: 8px;
    text-align: center;
    margin: 5px;
    min-width: 120px;
    position: relative;
}

.player.active {
    border: 2px solid #4CAF50;
}

.player.eliminated {
    background: #555;
    opacity: 0.6;
    text-decoration: line-through;
}

.player .bullets {
    margin-top: 5px;
    font-size: 14px;
}

/* Middle Section */
.middle-section {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 15px;
}

.game-info {
    text-align: center;
    font-size: 1.2em;
    color: #ddd;
}

/* Player Hand */
.player-hand {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    justify-content: center;
}

/* Card Styles */
.card {
    width: 60px;
    height: 90px;
    background: white;
    border-radius: 5px;
    display: flex;
    justify-content: center;
    align-items: center;
    color: black;
    cursor: pointer;
    user-select: none;
    border: 2px solid transparent;
    transition: border 0.3s;
    position: relative;
    font-size: 1em;
}

.card.selected {
    border: 2px solid yellow;
}

.card.back {
    background: #333;
    color: #333;
    cursor: default;
}

.actions {
    display: flex;
    gap: 10px;
    margin-top: 10px;
}

/* Overlay Styles */
.overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0,0,0,0.9);
    display: none;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    gap: 20px;
    z-index: 1000;
}

.overlay.active {
    display: flex;
}

.overlay-content {
    background-color: rgba(0, 0, 0, 0.8);
    color: white;
    padding: 20px;
    border-radius: 10px;
    text-align: center;
}

.overlay-button {
    padding: 10px 20px;
    margin-top: 15px;
    font-size: 1em;
    cursor: pointer;
}

/* Central Table Styles */
.central-table {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    justify-content: center;
    margin-top: 20px;
}

.central-table .card {
    cursor: default;
}

/* Suit Color Styling */
.card.spades, .card.clubs {
    color: black;
}

.card.hearts, .card.diamonds {
    color: red;
}

/* Flip Animation for Cards */
.card-inner {
    position: absolute;
    width: 100%;
    height: 100%;
    transition: transform 0.6s;
    transform-style: preserve-3d;
}

.card.flipped .card-inner {
    transform: rotateY(180deg);
}

.card-front, .card-back {
    position: absolute;
    width: 100%;
    height: 100%;
    backface-visibility: hidden;
    border: 1px solid #000;
    border-radius: 5px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.2em;
}

.card-front {
    background-color: #fff;
    color: inherit; /* Inherit color for suit */
}

.card-back {
    background-color: #333;
    color: #333;
    transform: rotateY(180deg);
}

/* Highlighting Styles */
.match {
    border: 2px solid green; /* Highlight matching cards */
}

.mismatch {
    border: 2px solid red; /* Highlight non-matching cards */
}

/* Game Over Overlay */
.shooting-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0,0,0,0.8);
    display: none;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    gap: 20px;
    z-index: 2000;
}

.shooting-overlay.active {
    display: flex;
}

/* Responsive Design */
@media (max-width: 600px) {
    .player {
        min-width: 100px;
        padding: 8px 15px;
    }

    .card {
        width: 50px;
        height: 75px;
        font-size: 0.9em;
    }
}


I need you acknowledge this game and await for my instructions. Create a developed and detailed mental model of the overall engine.