This commit is contained in:
2025-12-29 11:15:49 +00:00
committed by FernandoJVideira
parent a34027889f
commit 8f7102b335
3 changed files with 370 additions and 191 deletions
+41 -178
View File
@@ -1,186 +1,49 @@
const games = new Map();
let currentGameID = 0;
// websockets/state/game.js
// Bisca deck configuration
const suits = ["hearts", "diamonds", "clubs", "spades"];
const cards = [
{ face: "A", value: 11, points: 11 },
{ face: "7", value: 10, points: 10 },
{ face: "K", value: 4, points: 4 },
{ face: "J", value: 3, points: 3 },
{ face: "Q", value: 2, points: 2 },
{ face: "6", value: 0, points: 0 },
{ face: "5", value: 0, points: 0 },
{ face: "4", value: 0, points: 0 },
{ face: "3", value: 0, points: 0 },
{ face: "2", value: 0, points: 0 },
];
// Importamos a classe que criaste no Passo 1
const BiscaGame = require('../classes/BiscaGame');
const createDeck = () => {
const deck = [];
suits.forEach((suit) => {
cards.forEach((card) => {
deck.push({ ...card, suit });
});
});
return deck;
// Aqui guardamos todos os jogos a decorrer na memória RAM do servidor
// Chave: ID do jogo (matchId) -> Valor: Instância do Jogo (O objeto com o baralho, mãos, etc)
const activeGames = new Map();
/**
* Cria um novo jogo, guarda-o na memória e retorna-o.
*/
const createGame = (id, type, player1, player2) => {
// 1. Instanciar a classe (Criar o jogo com as regras)
const game = new BiscaGame(id, type, player1, player2);
// 2. Preparar o jogo (Baralhar, dar cartas iniciais)
game.init();
// 3. Guardar no "Armazém" para não se perder
activeGames.set(id, game);
console.log(`[Game State] Jogo criado: ${id} (Tipo: ${type})`);
return game;
};
const shuffleDeck = (deck) => {
const shuffled = [...deck];
for (let i = shuffled.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
}
return shuffled;
/**
* Busca um jogo existente pelo ID.
* Usado sempre que um jogador envia uma carta, para sabermos a qual jogo pertence.
*/
const getGame = (id) => {
return activeGames.get(id);
};
const dealCards = (deck, cardsPerPlayer = 3) => {
const player1Hand = deck.slice(0, cardsPerPlayer);
const player2Hand = deck.slice(cardsPerPlayer, cardsPerPlayer * 2);
const trumpCard = deck.pop();
return {
player1Hand,
player2Hand,
trumpCard,
remainingDeck: deck,
};
/**
* Remove o jogo da memória.
* Usado quando o jogo acaba para libertar RAM.
*/
const removeGame = (id) => {
activeGames.delete(id);
console.log(`[Game State] Jogo removido: ${id}`);
};
export const createGame = (type, host) => {
const gameID = currentGameID++;
const game = {
match_id: gameID,
type: type,
player1_user_id: host.id,
player2_user_id: null,
is_draw: false,
winner_user_id: null,
loser_user_id: null,
status: "pending",
began_at: null,
ended_at: null,
player1_points: 0,
player2_points: 0,
// Game-specific state
player1_hand: [],
player2_hand: [],
trump_card: null,
trump_suit: null,
current_trick: [],
current_player: null,
round_number: 0,
};
games.set(gameID, game);
return game;
};
export const joinGame = (gameID, player2) => {
const game = games.get(gameID);
game.player2_user_id = player2.id;
game.status = "playing";
game.began_at = new Date();
const cardCount = parseInt(game.type, 10);
if (![3, 9].includes(cardCount)) {
throw new Error("Unsupported game type");
}
// Initialize game state
const deck = shuffleDeck(createDeck());
const { player1Hand, player2Hand, trumpCard, remainingDeck } = dealCards(
deck,
cardCount
);
game.player1_hand = player1Hand;
game.player2_hand = player2Hand;
game.trump_card = trumpCard;
game.trump_suit = trumpCard.suit;
game.remaining_deck = remainingDeck;
game.current_player = game.player1_user_id; // Player 1 starts
return game;
};
export const playCard = (gameID, playerID, cardIndex) => {
const game = games.get(gameID);
if (game.current_player !== playerID) {
throw new Error("Not this player's turn");
}
const isPlayer1 = playerID === game.player1_user_id;
const playerHand = isPlayer1 ? game.player1_hand : game.player2_hand;
const playedCard = playerHand.splice(cardIndex, 1)[0];
game.current_trick.push({ playerID, card: playedCard });
if (game.current_trick.length === 2) {
resolveTrick(game);
} else {
game.current_player = isPlayer1
? game.player2_user_id
: game.player1_user_id;
}
return game;
};
const resolveTrick = (game) => {
const [first, second] = game.current_trick;
let winner;
if (first.card.suit === second.card.suit) {
winner = first.card.value > second.card.value ? first : second;
} else if (second.card.suit === game.trump_suit) {
winner = second;
} else {
winner = first; // First card wins if no trump and different suits
}
const points = first.card.points + second.card.points;
winner.playerID === game.player1_user_id
? (game.player1_points += points)
: (game.player2_points += points);
if (deck.length > 0) {
const winnerHand =
winner.playerID === game.player1_user_id
? game.player1_hand
: game.player2_hand;
loserHand =
winner.playerID === game.player1_user_id
? game.player2_hand
: game.player1_hand;
winnerHand.push(game.deck.shift());
if (game.deck.length > 0) {
loserHand.push(game.deck.shift());
}
}
game.current_trick = [];
game.current_player = winner.playerID;
game.round_number += 1;
if (game.player1_hand.length === 0 && game.player2_hand.length === 0) {
endGame(game);
}
};
const endGame = (game) => {
game.status = "ended";
game.ended_at = new Date();
if (game.player1_points > game.player2_points) {
game.winner_user_id = game.player1_user_id;
game.loser_user_id = game.player2_user_id;
} else if (game.player2_points > game.player1_points) {
game.winner_user_id = game.player2_user_id;
game.loser_user_id = game.player1_user_id;
} else {
game.is_draw = true;
}
};
export const getGame = (gameID) => games.get(gameID);
export const getAllGames = () => Array.from(games.values());
// Exportamos estas funções para serem usadas no ficheiro de eventos
module.exports = {
createGame,
getGame,
removeGame
};