class BiscaGame { constructor(id, type, player1, player2) { this.id = id; this.type = type; this.deck = []; this.trumpCard = null; this.trumpSuit = null; this.table = { playerCard: null, opponentCard: null, firstPlayerId: null, }; this.currentTurn = null; this.status = "pending"; this.startTime = null; this.players = { [player1.id]: { id: player1.id, name: player1.name, hand: [], points: 0, tricks: 0, }, [player2.id]: { id: player2.id, name: player2.name, hand: [], points: 0, tricks: 0, }, }; } init() { this.deck = this.createDeck(); const lastCard = this.deck.pop(); this.trumpCard = lastCard; this.trumpSuit = lastCard.suit; this.dealInitialCards(); const playerIds = Object.keys(this.players); this.currentTurn = playerIds.find((id) => id != 1) || playerIds[0]; this.startTime = Date.now(); this.status = "playing"; } createDeck() { const suits = ["c", "e", "o", "p"]; const ranks = [1, 2, 3, 4, 5, 6, 7, 11, 12, 13]; let deck = []; for (const suit of suits) { for (const rank of ranks) { deck.push({ id: `${suit}-${rank}`, suit, rank, strength: this.getStrength(rank), points: this.getPoints(rank), }); } } return deck.sort(() => Math.random() - 0.5); } dealInitialCards() { const cardsToDeal = this.type == 9 ? 9 : 3; const pIds = Object.keys(this.players); for (let i = 0; i < cardsToDeal; i++) { pIds.forEach((id) => this.drawCard(id)); } } drawCard(userId) { if (this.deck.length > 0) { this.players[userId].hand.push(this.deck.pop()); } else if (this.trumpCard) { this.players[userId].hand.push(this.trumpCard); this.trumpCard = null; } } playCard(userId, cardId) { if (this.currentTurn != userId) return { error: "Not your turn!" }; const player = this.players[userId]; const cardIndex = player.hand.findIndex((c) => c.id === cardId); if (cardIndex === -1) return { error: "You don't own that card!" }; const card = player.hand[cardIndex]; const deckEmpty = this.deck.length === 0 && this.trumpCard === null; if (this.table.playerCard && deckEmpty) { const suitToFollow = this.table.playerCard.suit; const hasSuit = player.hand.some((c) => c.suit === suitToFollow); if (hasSuit && card.suit !== suitToFollow) { return { error: "You must follow the suit!" }; } } player.hand.splice(cardIndex, 1); if (!this.table.playerCard) { this.table.playerCard = card; this.table.firstPlayerId = userId; this.currentTurn = this.getOpponentId(userId); return { action: "next_turn" }; } else { this.table.opponentCard = card; return this.resolveTrick(); } } resolveTrick() { const p1Id = this.table.firstPlayerId; const p2Id = this.getOpponentId(p1Id); const card1 = this.table.playerCard; const card2 = this.table.opponentCard; let winnerId = p2Id; let p1Wins = false; if (card2.suit === this.trumpSuit && card1.suit !== this.trumpSuit) p1Wins = false; else if (card1.suit === this.trumpSuit && card2.suit !== this.trumpSuit) p1Wins = true; else if (card1.suit === card2.suit) p1Wins = card1.strength > card2.strength; else p1Wins = true; winnerId = p1Wins ? p1Id : p2Id; const points = card1.points + card2.points; this.players[winnerId].points += points; this.players[winnerId].tricks += 1; this.currentTurn = winnerId; if (this.type == 3) { this.drawCard(winnerId); this.drawCard(this.getOpponentId(winnerId)); } const trickResult = { winnerId, points, cards: [card1, card2] }; this.table = { playerCard: null, opponentCard: null, firstPlayerId: null }; if (this.players[winnerId].hand.length === 0) { this.status = "finished"; const pIds = Object.keys(this.players); const p1Obj = this.players[pIds[0]]; const p2Obj = this.players[pIds[1]]; const totalTime = (Date.now() - this.startTime) / 1000; let gameWinnerId = null; let isDraw = false; if (p1Obj.points > p2Obj.points) gameWinnerId = p1Obj.id; else if (p2Obj.points > p1Obj.points) gameWinnerId = p2Obj.id; else isDraw = true; const finalWinner = isDraw ? null : gameWinnerId; const finalLoser = isDraw ? null : gameWinnerId == p1Obj.id ? p2Obj.id : p1Obj.id; return { action: "game_ended", trickResult, winnerId: finalWinner, loserId: finalLoser, isDraw: isDraw, player1_id: pIds[0], player1_points: p1Obj.points, player2_id: pIds[1], player2_points: p2Obj.points, totalTime: totalTime, }; } return { action: "trick_resolved", trickResult }; } getStateForPlayer(userId) { const oppId = this.getOpponentId(userId); return { id: this.id, trumpCard: this.trumpCard, trumpSuit: this.trumpSuit, deckCount: this.deck.length + (this.trumpCard ? 1 : 0), currentTurn: this.currentTurn, table: this.table, hand: this.players[userId].hand, points: this.players[userId].points, opponent: { points: this.players[oppId].points, cardCount: this.players[oppId].hand.length, }, }; } getOpponentId(id) { return Object.keys(this.players).find((pid) => pid != id); } getPoints(r) { const p = { 1: 11, 7: 10, 13: 4, 11: 3, 12: 2 }; return p[r] || 0; } getStrength(r) { const s = { 1: 10, 7: 9, 13: 8, 11: 7, 12: 6, 6: 5, 5: 4, 4: 3, 3: 2, 2: 1, }; return s[r] || 0; } } module.exports = BiscaGame;