|
|
|
|
@@ -1,103 +0,0 @@
|
|
|
|
|
import { Card, Hand, newDeck, Pile, shuffle, vCard } from "@games/shared/cards";
|
|
|
|
|
import { heq } from "@games/shared/utils";
|
|
|
|
|
|
|
|
|
|
export type SimpleConfiguration = {
|
|
|
|
|
game: "simple";
|
|
|
|
|
players: string[];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// omniscient game state
|
|
|
|
|
export type SimpleGameState = {
|
|
|
|
|
deck: Pile;
|
|
|
|
|
turnIdx: number;
|
|
|
|
|
playerHands: { [humanKey: string]: Hand };
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// a particular player's point of view in the game
|
|
|
|
|
export type SimplePlayerView = {
|
|
|
|
|
deckCount: number;
|
|
|
|
|
playerTurn: string;
|
|
|
|
|
playerHandCounts: { [humanKey: string]: number };
|
|
|
|
|
myHand: Hand<Card>;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type SimpleAction = { type: "draw" } | { type: "discard"; card: Card };
|
|
|
|
|
|
|
|
|
|
export const newSimpleGameState = (
|
|
|
|
|
config: SimpleConfiguration
|
|
|
|
|
): SimpleGameState => {
|
|
|
|
|
const { players } = config;
|
|
|
|
|
return {
|
|
|
|
|
deck: shuffle(newDeck()),
|
|
|
|
|
turnIdx: 0,
|
|
|
|
|
playerHands: Object.fromEntries(
|
|
|
|
|
players.map((humanKey) => [humanKey, []])
|
|
|
|
|
),
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const getSimplePlayerView = (
|
|
|
|
|
config: SimpleConfiguration,
|
|
|
|
|
state: SimpleGameState,
|
|
|
|
|
humanKey: string
|
|
|
|
|
): SimplePlayerView => ({
|
|
|
|
|
deckCount: state.deck.length,
|
|
|
|
|
playerTurn: config.players[state.turnIdx],
|
|
|
|
|
myHand: state.playerHands[humanKey] as Hand,
|
|
|
|
|
playerHandCounts: Object.fromEntries(
|
|
|
|
|
Object.entries(state.playerHands)
|
|
|
|
|
.filter(([id]) => id != humanKey)
|
|
|
|
|
.map(([id, hand]) => [id, hand.length])
|
|
|
|
|
),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
export const resolveSimpleAction = ({
|
|
|
|
|
config,
|
|
|
|
|
state,
|
|
|
|
|
humanKey,
|
|
|
|
|
action,
|
|
|
|
|
}: {
|
|
|
|
|
config: SimpleConfiguration;
|
|
|
|
|
state: SimpleGameState;
|
|
|
|
|
humanKey: string;
|
|
|
|
|
action: SimpleAction;
|
|
|
|
|
}): SimpleGameState => {
|
|
|
|
|
const playerHand = state.playerHands[humanKey];
|
|
|
|
|
if (playerHand == null) {
|
|
|
|
|
throw new Error(
|
|
|
|
|
`${humanKey} is not a player in this game; they cannot perform actions`
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (humanKey != config.players[state.turnIdx]) {
|
|
|
|
|
throw new Error(`It's not ${humanKey}'s turn!`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const numPlayers = Object.keys(state.playerHands).length;
|
|
|
|
|
const newTurnIdx = (state.turnIdx + 1) % numPlayers;
|
|
|
|
|
|
|
|
|
|
if (action.type == "draw") {
|
|
|
|
|
const [drawn, ...rest] = state.deck;
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
deck: rest,
|
|
|
|
|
playerHands: {
|
|
|
|
|
...state.playerHands,
|
|
|
|
|
[humanKey]: [drawn, ...playerHand],
|
|
|
|
|
},
|
|
|
|
|
turnIdx: newTurnIdx,
|
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
// action.type == discard
|
|
|
|
|
const cardIndex = playerHand.findIndex(heq(action.card));
|
|
|
|
|
return {
|
|
|
|
|
deck: [action.card, ...state.deck],
|
|
|
|
|
playerHands: {
|
|
|
|
|
...state.playerHands,
|
|
|
|
|
[humanKey]: playerHand
|
|
|
|
|
.slice(0, cardIndex)
|
|
|
|
|
.concat(playerHand.slice(cardIndex + 1)),
|
|
|
|
|
},
|
|
|
|
|
turnIdx: newTurnIdx,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
};
|