Files
DADProject/websockets/classes/BiscaGame.js

281 lines
7.0 KiB
JavaScript

class BiscaGame {
constructor(id, type, player1, player2) {
this.id = id;
this.type = type;
this.deck = [];
this.trumpCard = null;
this.trumpSuit = null;
this.timer = null;
this.turnDeadline = null;
this.timeoutCallback = 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";
}
startTurnTimer(callback) {
if (this.timer) clearTimeout(this.timer);
this.timeoutCallback = callback;
const DURATION_MS = 20000;
this.turnDeadline = Date.now() + DURATION_MS;
console.log(`[Game ${this.id}] Timer started for User ${this.currentTurn}`);
this.timer = setTimeout(() => {
this.handleTimeout();
}, DURATION_MS);
}
stopTimer() {
if (this.timer) clearTimeout(this.timer);
this.timer = null;
this.turnDeadline = null;
}
handleTimeout() {
console.log(`[Game ${this.id}] Timeout! Auto-playing for ${this.currentTurn}`);
const lowestCard = this.getLowestCard(this.currentTurn);
if (lowestCard && this.timeoutCallback) {
this.timeoutCallback(this.currentTurn, lowestCard.id);
}
}
getLowestCard(userId) {
const hand = this.players[userId].hand;
if (!hand || hand.length === 0) return null;
const sortedHand = [...hand].sort((a, b) => a.strength - b.strength);
return sortedHand[0];
}
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!" };
}
}
this.stopTimer();
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";
this.stopTimer();
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,
},
turnDeadline: this.turnDeadline,
};
}
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;