refactors to nested array
This commit is contained in:
parent
96dfc35b1e
commit
d0f6a3fdf2
5 changed files with 197 additions and 211 deletions
121
src/game/card.ts
121
src/game/card.ts
|
|
@ -1,77 +1,24 @@
|
||||||
type CardValue = -2 | -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
|
export type Card =
|
||||||
|
| -2
|
||||||
|
| -1
|
||||||
|
| 0
|
||||||
|
| 1
|
||||||
|
| 2
|
||||||
|
| 3
|
||||||
|
| 4
|
||||||
|
| 5
|
||||||
|
| 6
|
||||||
|
| 7
|
||||||
|
| 8
|
||||||
|
| 9
|
||||||
|
| 10
|
||||||
|
| 11
|
||||||
|
| 12;
|
||||||
|
|
||||||
export type ObfuscatedCardValue = CardValue | "X";
|
export type ConcealableCard = Card | null;
|
||||||
|
|
||||||
type CardColor =
|
export type ConcealableCardStack = {
|
||||||
| "darkblue"
|
cards: ConcealableCard[];
|
||||||
| "lightblue"
|
|
||||||
| "green"
|
|
||||||
| "yellow"
|
|
||||||
| "red"
|
|
||||||
| "black";
|
|
||||||
|
|
||||||
// TODO: use smarter types like Omit<>, Pick<>, etc.
|
|
||||||
export type ObfuscatedCard = {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
value: ObfuscatedCardValue;
|
|
||||||
color: CardColor;
|
|
||||||
};
|
|
||||||
|
|
||||||
export class Card {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
value: CardValue;
|
|
||||||
color: CardColor;
|
|
||||||
constructor(id: number, value: CardValue) {
|
|
||||||
this.id = id;
|
|
||||||
this.value = value;
|
|
||||||
this.name = `${value} Card`;
|
|
||||||
this.color = this.matchColorToCardValue(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
matchColorToCardValue(value: CardValue | ObfuscatedCardValue): CardColor {
|
|
||||||
switch (value) {
|
|
||||||
case -2:
|
|
||||||
return "darkblue";
|
|
||||||
case -1:
|
|
||||||
return "darkblue";
|
|
||||||
case 0:
|
|
||||||
return "lightblue";
|
|
||||||
case 1:
|
|
||||||
return "green";
|
|
||||||
case 2:
|
|
||||||
return "green";
|
|
||||||
case 3:
|
|
||||||
return "green";
|
|
||||||
case 4:
|
|
||||||
return "green";
|
|
||||||
case 5:
|
|
||||||
return "yellow";
|
|
||||||
case 6:
|
|
||||||
return "yellow";
|
|
||||||
case 7:
|
|
||||||
return "yellow";
|
|
||||||
case 8:
|
|
||||||
return "yellow";
|
|
||||||
case 9:
|
|
||||||
return "red";
|
|
||||||
case 10:
|
|
||||||
return "red";
|
|
||||||
case 11:
|
|
||||||
return "red";
|
|
||||||
case 12:
|
|
||||||
return "red";
|
|
||||||
case "X":
|
|
||||||
return "black";
|
|
||||||
default:
|
|
||||||
return "red";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ObfuscatedCardStack = {
|
|
||||||
cards: ObfuscatedCard[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export class CardStack {
|
export class CardStack {
|
||||||
|
|
@ -84,77 +31,77 @@ export class CardStack {
|
||||||
generateCards() {
|
generateCards() {
|
||||||
for (let cardNumber = 1; cardNumber <= 150; cardNumber++) {
|
for (let cardNumber = 1; cardNumber <= 150; cardNumber++) {
|
||||||
if (cardNumber <= 5) {
|
if (cardNumber <= 5) {
|
||||||
this.cards.push(new Card(cardNumber, -2));
|
this.cards.push(-2);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cardNumber > 5 && cardNumber <= 15) {
|
if (cardNumber > 5 && cardNumber <= 15) {
|
||||||
this.cards.push(new Card(cardNumber, -1));
|
this.cards.push(-1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cardNumber > 15 && cardNumber <= 30) {
|
if (cardNumber > 15 && cardNumber <= 30) {
|
||||||
this.cards.push(new Card(cardNumber, 0));
|
this.cards.push(0);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cardNumber > 30 && cardNumber <= 40) {
|
if (cardNumber > 30 && cardNumber <= 40) {
|
||||||
this.cards.push(new Card(cardNumber, 1));
|
this.cards.push(1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cardNumber > 40 && cardNumber <= 50) {
|
if (cardNumber > 40 && cardNumber <= 50) {
|
||||||
this.cards.push(new Card(cardNumber, 2));
|
this.cards.push(2);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cardNumber > 50 && cardNumber <= 60) {
|
if (cardNumber > 50 && cardNumber <= 60) {
|
||||||
this.cards.push(new Card(cardNumber, 3));
|
this.cards.push(3);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cardNumber > 60 && cardNumber <= 70) {
|
if (cardNumber > 60 && cardNumber <= 70) {
|
||||||
this.cards.push(new Card(cardNumber, 4));
|
this.cards.push(4);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cardNumber > 70 && cardNumber <= 80) {
|
if (cardNumber > 70 && cardNumber <= 80) {
|
||||||
this.cards.push(new Card(cardNumber, 5));
|
this.cards.push(5);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cardNumber > 80 && cardNumber <= 90) {
|
if (cardNumber > 80 && cardNumber <= 90) {
|
||||||
this.cards.push(new Card(cardNumber, 6));
|
this.cards.push(6);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cardNumber > 90 && cardNumber <= 100) {
|
if (cardNumber > 90 && cardNumber <= 100) {
|
||||||
this.cards.push(new Card(cardNumber, 7));
|
this.cards.push(7);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cardNumber > 100 && cardNumber <= 110) {
|
if (cardNumber > 100 && cardNumber <= 110) {
|
||||||
this.cards.push(new Card(cardNumber, 8));
|
this.cards.push(8);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cardNumber > 110 && cardNumber <= 120) {
|
if (cardNumber > 110 && cardNumber <= 120) {
|
||||||
this.cards.push(new Card(cardNumber, 9));
|
this.cards.push(9);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cardNumber > 120 && cardNumber <= 130) {
|
if (cardNumber > 120 && cardNumber <= 130) {
|
||||||
this.cards.push(new Card(cardNumber, 10));
|
this.cards.push(10);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cardNumber > 130 && cardNumber <= 140) {
|
if (cardNumber > 130 && cardNumber <= 140) {
|
||||||
this.cards.push(new Card(cardNumber, 11));
|
this.cards.push(11);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cardNumber > 140 && cardNumber <= 150) {
|
if (cardNumber > 140 && cardNumber <= 150) {
|
||||||
this.cards.push(new Card(cardNumber, 12));
|
this.cards.push(12);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,27 @@ import { Game } from "./game";
|
||||||
|
|
||||||
import { allGames } from "./game";
|
import { allGames } from "./game";
|
||||||
|
|
||||||
export const handleJoinSession = (socket: Socket, sessionId: string) => {
|
type SessionResponse = "success" | "error:full" | "error:running";
|
||||||
|
|
||||||
|
export const handleJoinSession = (
|
||||||
|
socket: Socket,
|
||||||
|
sessionId: string,
|
||||||
|
callback: Function
|
||||||
|
) => {
|
||||||
|
const isSessionRunning = allGames.some(
|
||||||
|
(game) => game.sessionId === sessionId
|
||||||
|
);
|
||||||
|
if (isSessionRunning) {
|
||||||
|
callback("error:running" satisfies SessionResponse);
|
||||||
|
socket.emit(
|
||||||
|
"message",
|
||||||
|
"A Game is already running in this session. Please join another session."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
socket.join(sessionId);
|
socket.join(sessionId);
|
||||||
|
callback("success" satisfies SessionResponse);
|
||||||
console.log("User joined session:", sessionId);
|
console.log("User joined session:", sessionId);
|
||||||
|
|
||||||
const numberOfClients = io.sockets.adapter.rooms.get(sessionId)?.size ?? 0;
|
const numberOfClients = io.sockets.adapter.rooms.get(sessionId)?.size ?? 0;
|
||||||
|
|
@ -41,6 +60,7 @@ export const handleNewGame = (
|
||||||
|
|
||||||
if (players && players.size > 1) {
|
if (players && players.size > 1) {
|
||||||
const game = new Game(socket, sessionId, players);
|
const game = new Game(socket, sessionId, players);
|
||||||
|
removeOldGame(sessionId);
|
||||||
allGames.push(game);
|
allGames.push(game);
|
||||||
console.log(`New Game created with ${game.playerCount} players!`);
|
console.log(`New Game created with ${game.playerCount} players!`);
|
||||||
game.gameLoop();
|
game.gameLoop();
|
||||||
|
|
@ -62,3 +82,12 @@ export const handleDisconnect = (socket: Socket) => {
|
||||||
io.to(sessionName).emit("clients-in-session", socketIds.size);
|
io.to(sessionName).emit("clients-in-session", socketIds.size);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const removeOldGame = (sessionId: string) => {
|
||||||
|
const oldGameIndex = allGames.findIndex(
|
||||||
|
(game) => game.sessionId === sessionId
|
||||||
|
);
|
||||||
|
if (oldGameIndex !== -1) {
|
||||||
|
allGames.splice(oldGameIndex, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
121
src/game/game.ts
121
src/game/game.ts
|
|
@ -1,6 +1,6 @@
|
||||||
import { Player, ObfuscatedPlayer } from "./player";
|
import { Player, ObfuscatedPlayer, ConcealableColumn } from "./player";
|
||||||
import { CardStack } from "./card";
|
import { CardStack, ConcealableCard } from "./card";
|
||||||
import { Card, ObfuscatedCardStack } from "./card";
|
import { Card, ConcealableCardStack } from "./card";
|
||||||
import { Socket } from "socket.io";
|
import { Socket } from "socket.io";
|
||||||
import { io } from "../server";
|
import { io } from "../server";
|
||||||
|
|
||||||
|
|
@ -18,14 +18,14 @@ type PlayerAction<ActionDataType> = {
|
||||||
data: ActionDataType;
|
data: ActionDataType;
|
||||||
};
|
};
|
||||||
|
|
||||||
type CardPosition = number;
|
type CardPosition = [number, number];
|
||||||
|
|
||||||
// obfuscated types are used to send only necessary data to the client
|
// obfuscated types are used to send only necessary data to the client
|
||||||
export type ObfuscatedGame = {
|
export type ObfuscatedGame = {
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
playerCount: number;
|
playerCount: number;
|
||||||
players: ObfuscatedPlayer[];
|
players: ObfuscatedPlayer[];
|
||||||
cardStack: ObfuscatedCardStack;
|
cardStack: ConcealableCardStack;
|
||||||
discardPile: Card[];
|
discardPile: Card[];
|
||||||
phase: string;
|
phase: string;
|
||||||
round: number;
|
round: number;
|
||||||
|
|
@ -78,13 +78,8 @@ export class Game {
|
||||||
let index = 0;
|
let index = 0;
|
||||||
playerIds.forEach((socketId) => {
|
playerIds.forEach((socketId) => {
|
||||||
index++;
|
index++;
|
||||||
const playerCards = cardStack.cards.splice(0, 12);
|
|
||||||
const player = new Player(
|
const player = new Player(index, socketId, `Player ${index}`, cardStack);
|
||||||
index,
|
|
||||||
socketId,
|
|
||||||
`Player ${index}`,
|
|
||||||
playerCards
|
|
||||||
);
|
|
||||||
players.push(player);
|
players.push(player);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -98,8 +93,8 @@ export class Game {
|
||||||
this.cardStack = new CardStack();
|
this.cardStack = new CardStack();
|
||||||
this.cardStack.shuffleCards();
|
this.cardStack.shuffleCards();
|
||||||
this.players.forEach((player) => {
|
this.players.forEach((player) => {
|
||||||
player.cards = this.cardStack.cards.splice(0, 12);
|
player.deck = player.generateDeck(this.cardStack);
|
||||||
player.knownCardPositions = new Array(12).fill(false);
|
player.knownCardPositions = player.createUnknownCardPositions();
|
||||||
player.playersTurn = true;
|
player.playersTurn = true;
|
||||||
player.cardCache = null;
|
player.cardCache = null;
|
||||||
player.tookDispiledCard = false;
|
player.tookDispiledCard = false;
|
||||||
|
|
@ -117,6 +112,7 @@ export class Game {
|
||||||
this.sendObfuscatedGameUpdate();
|
this.sendObfuscatedGameUpdate();
|
||||||
while (this.phase !== gamePhase.gameEnded) {
|
while (this.phase !== gamePhase.gameEnded) {
|
||||||
this.checkForFullRevealedCards();
|
this.checkForFullRevealedCards();
|
||||||
|
this.removeThreeOfAKinds();
|
||||||
switch (this.phase) {
|
switch (this.phase) {
|
||||||
case gamePhase.revealTwoCards:
|
case gamePhase.revealTwoCards:
|
||||||
console.log("\nGame phase: revealTwoCards");
|
console.log("\nGame phase: revealTwoCards");
|
||||||
|
|
@ -245,12 +241,15 @@ export class Game {
|
||||||
|
|
||||||
// Player Action Callbacks
|
// Player Action Callbacks
|
||||||
|
|
||||||
revealCardAction(playerSocketId: string, cardPosition: number) {
|
revealCardAction(playerSocketId: string, cardPosition: CardPosition) {
|
||||||
const player = this.getPlayerBySocketId(playerSocketId);
|
const player = this.getPlayerBySocketId(playerSocketId);
|
||||||
const revealedCard = player.cards[cardPosition];
|
const [columnIndex, cardIndex] = cardPosition;
|
||||||
console.log(`Revealed card ${revealedCard} at position ${cardPosition}`);
|
const revealedCard = player.deck[columnIndex][cardIndex];
|
||||||
|
console.log(
|
||||||
|
`Revealed card ${revealedCard} at column ${columnIndex} card ${cardIndex}`
|
||||||
|
);
|
||||||
const playerIndex = this.players.indexOf(player!);
|
const playerIndex = this.players.indexOf(player!);
|
||||||
this.players[playerIndex].knownCardPositions[cardPosition] = true;
|
this.players[playerIndex].knownCardPositions[columnIndex][cardIndex] = true;
|
||||||
this.sendObfuscatedGameUpdate();
|
this.sendObfuscatedGameUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -281,15 +280,17 @@ export class Game {
|
||||||
this.sendObfuscatedGameUpdate();
|
this.sendObfuscatedGameUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
placeCardAction(playerSocketId: string, cardPosition: number) {
|
placeCardAction(playerSocketId: string, cardPosition: CardPosition) {
|
||||||
const player = this.getPlayerBySocketId(playerSocketId);
|
const player = this.getPlayerBySocketId(playerSocketId);
|
||||||
console.log(`Player ${player.name} placed a card.`);
|
console.log(`Player ${player.name} placed a card.`);
|
||||||
const placedCard = player.cardCache!;
|
const placedCard = player.cardCache!;
|
||||||
player.cardCache = null;
|
player.cardCache = null;
|
||||||
const replacedCard = player.cards[cardPosition];
|
const [columnIndex, cardIndex] = cardPosition;
|
||||||
|
const replacedCard = player.deck[columnIndex][cardIndex];
|
||||||
this.discardPile.push(replacedCard);
|
this.discardPile.push(replacedCard);
|
||||||
player.cards[cardPosition] = placedCard;
|
player.deck[columnIndex][cardIndex] = placedCard;
|
||||||
player.knownCardPositions[cardPosition] = true;
|
player.knownCardPositions[columnIndex][cardIndex] = true;
|
||||||
|
// TODO: check for three of a kind
|
||||||
this.nextPlayersTurn();
|
this.nextPlayersTurn();
|
||||||
this.phase = gamePhase.pickUpCard;
|
this.phase = gamePhase.pickUpCard;
|
||||||
this.sendObfuscatedGameUpdate();
|
this.sendObfuscatedGameUpdate();
|
||||||
|
|
@ -379,32 +380,24 @@ export class Game {
|
||||||
phase: this.phase,
|
phase: this.phase,
|
||||||
round: this.round,
|
round: this.round,
|
||||||
discardPile: this.discardPile,
|
discardPile: this.discardPile,
|
||||||
players: this.players.map(({ cards, ...player }) => {
|
players: this.players.map(({ deck, ...player }) => {
|
||||||
return {
|
return {
|
||||||
...player,
|
...player,
|
||||||
cards: cards.map((card: Card, index: number) => {
|
deck: deck.map((column, columnIndex) => {
|
||||||
// unknown cards are obfuscated to X
|
const concealableColumn = column.map((card, cardIndex) => {
|
||||||
return {
|
// unknown cards are obfuscated to null
|
||||||
id: player.knownCardPositions[index] ? card.id : 0,
|
return player.knownCardPositions[columnIndex][cardIndex]
|
||||||
value: player.knownCardPositions[index] ? card.value : "X",
|
? card
|
||||||
name: player.knownCardPositions[index]
|
: (null as ConcealableCard);
|
||||||
? card.name
|
});
|
||||||
: "Facedown Card",
|
return concealableColumn as ConcealableColumn;
|
||||||
color: player.knownCardPositions[index] ? card.color : "black",
|
|
||||||
matchColorToCardValue: card.matchColorToCardValue,
|
|
||||||
};
|
|
||||||
}),
|
}),
|
||||||
};
|
} satisfies ObfuscatedPlayer;
|
||||||
}),
|
}),
|
||||||
cardStack: {
|
cardStack: {
|
||||||
cards: this.cardStack.cards.map((card: Card) => {
|
cards: this.cardStack.cards.map((card: Card) => {
|
||||||
// player may not see the value of the facedown cards in the cardStack
|
// player may not see the value of the facedown cards in the cardStack
|
||||||
return {
|
return null;
|
||||||
id: 0,
|
|
||||||
value: "X",
|
|
||||||
name: "Facedown Card",
|
|
||||||
color: "black",
|
|
||||||
};
|
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -419,13 +412,7 @@ export class Game {
|
||||||
updatePlayerRoundPoints() {
|
updatePlayerRoundPoints() {
|
||||||
this.players.forEach((player) => {
|
this.players.forEach((player) => {
|
||||||
const revealedCardValuesSum = player.getRevealedCardsValueSum();
|
const revealedCardValuesSum = player.getRevealedCardsValueSum();
|
||||||
const threeOfAKinds = player.getThreeOfAKinds();
|
player.roundPoints = revealedCardValuesSum;
|
||||||
const threeOfAKindPoints = threeOfAKinds.reduce(
|
|
||||||
(points, threeOfAKind) => points + threeOfAKind.value * 3,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
|
|
||||||
player.roundPoints = revealedCardValuesSum - threeOfAKindPoints;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -481,8 +468,8 @@ export class Game {
|
||||||
if (alreadyClosedPlayers.length > 0) return; // TODO: check if this is correct with more than 2 players
|
if (alreadyClosedPlayers.length > 0) return; // TODO: check if this is correct with more than 2 players
|
||||||
|
|
||||||
const playerWithAllCardsRevealed = this.players.find((player) =>
|
const playerWithAllCardsRevealed = this.players.find((player) =>
|
||||||
player.knownCardPositions.every(
|
player.knownCardPositions.every((knownCardsColumn) =>
|
||||||
(knownCardPosition) => knownCardPosition === true
|
knownCardsColumn.every((knownCard) => knownCard === true)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
if (playerWithAllCardsRevealed) {
|
if (playerWithAllCardsRevealed) {
|
||||||
|
|
@ -490,6 +477,22 @@ export class Game {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeThreeOfAKinds() {
|
||||||
|
this.players.forEach((player) => {
|
||||||
|
const threeOfAKinds = player.getThreeOfAKinds();
|
||||||
|
if (threeOfAKinds.length == 0) return;
|
||||||
|
threeOfAKinds.forEach((threeOfAKind) => {
|
||||||
|
const { columnIndex, value } = threeOfAKind;
|
||||||
|
this.discardPile.push(value as Card);
|
||||||
|
this.discardPile.push(value as Card);
|
||||||
|
this.discardPile.push(value as Card);
|
||||||
|
player.deck.splice(columnIndex, 1);
|
||||||
|
player.knownCardPositions.splice(columnIndex, 1);
|
||||||
|
});
|
||||||
|
this.sendObfuscatedGameUpdate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
checkIfPointLimitReached() {
|
checkIfPointLimitReached() {
|
||||||
const highestPoints = Math.max(
|
const highestPoints = Math.max(
|
||||||
...this.players.map((player) => player.totalPoints)
|
...this.players.map((player) => player.totalPoints)
|
||||||
|
|
@ -553,9 +556,11 @@ export class Game {
|
||||||
|
|
||||||
revealAllCards() {
|
revealAllCards() {
|
||||||
this.players.forEach((player) => {
|
this.players.forEach((player) => {
|
||||||
player.knownCardPositions = player.knownCardPositions.map(
|
player.knownCardPositions.forEach((column, columnIndex) => {
|
||||||
(knownCardPosition) => true
|
column.forEach((card, cardIndex) => {
|
||||||
);
|
player.knownCardPositions[columnIndex][cardIndex] = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -650,16 +655,6 @@ export class Game {
|
||||||
else throw new Error(`No player with socketId ${playerSocketId} found!`);
|
else throw new Error(`No player with socketId ${playerSocketId} found!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
listPlayerSocketListeners() {
|
|
||||||
console.log("Player socket listeners:");
|
|
||||||
io.sockets.sockets.forEach((socket) => {
|
|
||||||
console.log(socket.id);
|
|
||||||
socket.eventNames().forEach((eventName) => {
|
|
||||||
console.log(`${eventName.toString()}`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
sendMessageToAllPlayers(message: string) {
|
sendMessageToAllPlayers(message: string) {
|
||||||
io.to(this.sessionId).emit("message", message);
|
io.to(this.sessionId).emit("message", message);
|
||||||
console.log(`Sent Message (Session): ${message}`);
|
console.log(`Sent Message (Session): ${message}`);
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,30 @@
|
||||||
import { Card, ObfuscatedCard } from "./card";
|
import { Card, CardStack, ConcealableCard } from "./card";
|
||||||
|
|
||||||
|
type Column = [Card, Card, Card];
|
||||||
|
type Deck = Column[];
|
||||||
|
|
||||||
|
export type ConcealableColumn = [
|
||||||
|
ConcealableCard,
|
||||||
|
ConcealableCard,
|
||||||
|
ConcealableCard
|
||||||
|
];
|
||||||
|
type ConcealableDeck = ConcealableColumn[];
|
||||||
|
|
||||||
|
type ColumnIndex = number;
|
||||||
|
|
||||||
|
type ThreeOfAKind = {
|
||||||
|
columnIndex: ColumnIndex;
|
||||||
|
value: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type KnownCardsColumn = [boolean, boolean, boolean];
|
||||||
|
|
||||||
export type ObfuscatedPlayer = {
|
export type ObfuscatedPlayer = {
|
||||||
id: number;
|
id: number;
|
||||||
socketId: string;
|
socketId: string;
|
||||||
name: string;
|
name: string;
|
||||||
cards: ObfuscatedCard[];
|
deck: ConcealableDeck;
|
||||||
knownCardPositions: boolean[];
|
knownCardPositions: KnownCardsColumn[];
|
||||||
playersTurn: boolean;
|
playersTurn: boolean;
|
||||||
cardCache: Card | null;
|
cardCache: Card | null;
|
||||||
tookDispiledCard: boolean;
|
tookDispiledCard: boolean;
|
||||||
|
|
@ -14,26 +33,12 @@ export type ObfuscatedPlayer = {
|
||||||
closedRound: boolean;
|
closedRound: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ColumnPosition = [number, number, number];
|
|
||||||
|
|
||||||
type ThreeOfAKind = {
|
|
||||||
position: ColumnPosition;
|
|
||||||
value: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
const COLUMN_POSITIONS: ColumnPosition[] = [
|
|
||||||
[0, 4, 8],
|
|
||||||
[1, 5, 9],
|
|
||||||
[2, 6, 10],
|
|
||||||
[3, 7, 11],
|
|
||||||
];
|
|
||||||
|
|
||||||
export class Player {
|
export class Player {
|
||||||
id: number;
|
id: number;
|
||||||
socketId: string;
|
socketId: string;
|
||||||
name: string;
|
name: string;
|
||||||
cards: Card[];
|
deck: Deck;
|
||||||
knownCardPositions: boolean[];
|
knownCardPositions: KnownCardsColumn[];
|
||||||
playersTurn: boolean;
|
playersTurn: boolean;
|
||||||
cardCache: Card | null; // this is where the card is temporarily stored when a player draws a card
|
cardCache: Card | null; // this is where the card is temporarily stored when a player draws a card
|
||||||
tookDispiledCard: boolean; // this is used to check if a player took a dispiled card in the current turn
|
tookDispiledCard: boolean; // this is used to check if a player took a dispiled card in the current turn
|
||||||
|
|
@ -41,12 +46,17 @@ export class Player {
|
||||||
totalPoints: number;
|
totalPoints: number;
|
||||||
closedRound: boolean;
|
closedRound: boolean;
|
||||||
place: number | null; // indicates the place the player got in the last round
|
place: number | null; // indicates the place the player got in the last round
|
||||||
constructor(id: number, socketId: string, name: string, cards: Card[]) {
|
constructor(
|
||||||
|
id: number,
|
||||||
|
socketId: string,
|
||||||
|
name: string,
|
||||||
|
cardStack: CardStack
|
||||||
|
) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.socketId = socketId;
|
this.socketId = socketId;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.cards = cards;
|
this.deck = this.generateDeck(cardStack);
|
||||||
this.knownCardPositions = new Array(12).fill(false);
|
this.knownCardPositions = this.createUnknownCardPositions();
|
||||||
this.playersTurn = true;
|
this.playersTurn = true;
|
||||||
this.cardCache = null;
|
this.cardCache = null;
|
||||||
this.tookDispiledCard = false;
|
this.tookDispiledCard = false;
|
||||||
|
|
@ -56,74 +66,79 @@ export class Player {
|
||||||
this.place = null;
|
this.place = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generateDeck(cardStack: CardStack): Deck {
|
||||||
|
const deck: Deck = [];
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
deck.push(cardStack.cards.splice(0, 3) as Column);
|
||||||
|
}
|
||||||
|
return deck;
|
||||||
|
}
|
||||||
|
|
||||||
|
createUnknownCardPositions(): KnownCardsColumn[] {
|
||||||
|
const knownCardPositions: KnownCardsColumn[] = [];
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
knownCardPositions.push([false, false, false]);
|
||||||
|
}
|
||||||
|
return knownCardPositions;
|
||||||
|
}
|
||||||
|
|
||||||
hasInitialCardsRevealed(): boolean {
|
hasInitialCardsRevealed(): boolean {
|
||||||
const revealedCards = this.knownCardPositions.filter(
|
const flattenedKnownCardPositions = this.knownCardPositions.flat();
|
||||||
(knownCard) => knownCard === true
|
const revealedCards = flattenedKnownCardPositions.filter(
|
||||||
|
(position) => position
|
||||||
);
|
);
|
||||||
if (revealedCards.length > 1) return true;
|
if (revealedCards.length > 1) return true;
|
||||||
else return false;
|
else return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
getRevealedCardCount(): number {
|
getRevealedCardCount(): number {
|
||||||
return this.knownCardPositions.filter((position) => position).length;
|
return this.knownCardPositions.flat().filter((position) => position == true)
|
||||||
|
.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
getThreeOfAKinds(): ThreeOfAKind[] {
|
getThreeOfAKinds(): ThreeOfAKind[] {
|
||||||
const revealedCardPositions = this.getRevealedCardPositions();
|
|
||||||
const columns: ColumnPosition[] = COLUMN_POSITIONS;
|
|
||||||
const threeOfAKinds: ThreeOfAKind[] = [];
|
const threeOfAKinds: ThreeOfAKind[] = [];
|
||||||
columns.forEach((column) => {
|
this.deck.forEach((column, index) => {
|
||||||
const columnValues = column.map((position) => this.cards[position].value);
|
const firstCard = column[0];
|
||||||
const columnHasSameValues = columnValues.every(
|
const columnIndex = index;
|
||||||
(value) => value === columnValues[0]
|
|
||||||
|
const columnHasSameCards = column.every((card) => card === firstCard);
|
||||||
|
const columnIsRevealed = this.knownCardPositions[columnIndex].every(
|
||||||
|
(isCardRevealed) =>
|
||||||
|
isCardRevealed === this.knownCardPositions[columnIndex][0]
|
||||||
);
|
);
|
||||||
const columnIsRevealed = column.every((position) =>
|
|
||||||
revealedCardPositions.includes(position)
|
if (columnHasSameCards && columnIsRevealed) {
|
||||||
);
|
|
||||||
if (columnHasSameValues && columnIsRevealed) {
|
|
||||||
threeOfAKinds.push({
|
threeOfAKinds.push({
|
||||||
position: column,
|
columnIndex: columnIndex as ColumnIndex,
|
||||||
value: columnValues[0],
|
value: firstCard,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return threeOfAKinds;
|
return threeOfAKinds;
|
||||||
}
|
}
|
||||||
|
|
||||||
getRevealedCardPositions(): number[] {
|
|
||||||
const revealedCardPositions: number[] = [];
|
|
||||||
this.knownCardPositions.forEach((position, index) => {
|
|
||||||
if (position) revealedCardPositions.push(index);
|
|
||||||
});
|
|
||||||
return revealedCardPositions;
|
|
||||||
}
|
|
||||||
|
|
||||||
getRevealedCards(): Card[] {
|
getRevealedCards(): Card[] {
|
||||||
const revealedCards: Card[] = [];
|
const revealedCards: Card[] = [];
|
||||||
this.knownCardPositions.forEach((position, index) => {
|
this.knownCardPositions.forEach((column, columnIndex) => {
|
||||||
if (position) revealedCards.push(this.cards[index]);
|
column.forEach((isCardRevealed, cardIndex) => {
|
||||||
|
if (isCardRevealed)
|
||||||
|
revealedCards.push(this.deck[columnIndex][cardIndex]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return revealedCards;
|
return revealedCards;
|
||||||
}
|
}
|
||||||
|
|
||||||
getRevealedCardsValueSum(): number {
|
getRevealedCardsValueSum(): number {
|
||||||
const revealedCards = this.getRevealedCards();
|
const revealedCards = this.getRevealedCards();
|
||||||
const revealedCardsValueSum = revealedCards.reduce(
|
let revealedCardsValueSum = 0;
|
||||||
(sum, card) => sum + card.value,
|
revealedCards.forEach((card) => (revealedCardsValueSum += card));
|
||||||
0
|
|
||||||
);
|
|
||||||
return revealedCardsValueSum;
|
return revealedCardsValueSum;
|
||||||
}
|
}
|
||||||
|
|
||||||
getHighestRevealedCardValue(): number {
|
getHighestRevealedCardValue(): number {
|
||||||
const revealedCards = this.getRevealedCards();
|
const revealedCards = this.getRevealedCards();
|
||||||
const highestRevealedCardValue = revealedCards.reduce(
|
const highestRevealedCardValue = Math.max(...revealedCards);
|
||||||
(highestValue, card) => {
|
|
||||||
if (card.value > highestValue) return card.value;
|
|
||||||
else return highestValue;
|
|
||||||
},
|
|
||||||
0
|
|
||||||
);
|
|
||||||
return highestRevealedCardValue;
|
return highestRevealedCardValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,8 @@ export const io = new SocketIOServer(httpServer, {
|
||||||
io.on("connection", (socket: Socket) => {
|
io.on("connection", (socket: Socket) => {
|
||||||
console.log("A user connected:", socket.id);
|
console.log("A user connected:", socket.id);
|
||||||
|
|
||||||
socket.on("join-session", (sessionId: string) =>
|
socket.on("join-session", (sessionId: string, callback) =>
|
||||||
handleJoinSession(socket, sessionId)
|
handleJoinSession(socket, sessionId, callback)
|
||||||
);
|
);
|
||||||
|
|
||||||
socket.on("leave-session", (sessionId: string) => {
|
socket.on("leave-session", (sessionId: string) => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue