starting to abstract the game

This commit is contained in:
2025-08-28 22:20:24 -04:00
parent d69336027a
commit 0d6d3d6d32
8 changed files with 59 additions and 21 deletions

View File

@@ -4,7 +4,7 @@ import {
SimpleAction,
SimpleConfiguration,
SimpleGameState,
} from "./games/simple";
} from "@games/shared/games/simple";
import { human } from "./human";
import dayjs from "dayjs";
import db from "./db";

View File

@@ -1,10 +0,0 @@
import * as renaissance from "./renaissance";
import * as simple from "./simple";
const games = {
renaissance,
simple,
};
export default games;
export type Game = keyof typeof games;

View File

@@ -1 +0,0 @@
export default {};

View File

@@ -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,
};
}
};

View File

@@ -7,7 +7,7 @@ import {
SimpleAction,
SimpleConfiguration,
SimpleGameState,
} from "./games/simple";
} from "@games/shared/games/simple";
import { transform } from "./kefir-extension";
export const WsOut = t.Object({