end to end!

This commit is contained in:
2025-08-23 20:32:50 -04:00
parent a2e8887a0b
commit 3347452ec4
12 changed files with 114 additions and 73 deletions

View File

@@ -62,31 +62,6 @@ const api = new Elysia({ prefix: "/api" })
)
.get("/games", () => [{ key: "simple", name: "simple" }])
.ws("/ws/:tableKey", {
response: WsOut,
body: WsIn,
message(
{
data: {
humanKey,
params: { tableKey },
},
},
body
) {
const {
inputs: { gameProposals, gameStarts, gameActions },
} = liveTable(tableKey);
if ("proposeGame" in body) {
gameProposals.emit(body);
} else if ("startGame" in body) {
gameStarts.emit(body);
} else if ("action" in body) {
gameActions.emit(body);
}
},
async open({
data: {
params: { tableKey },
@@ -108,6 +83,32 @@ const api = new Elysia({ prefix: "/api" })
);
table.inputs.presenceChanges.emit({ humanKey, presence: "joined" });
},
response: WsOut,
body: WsIn,
message(
{
data: {
humanKey,
params: { tableKey },
},
},
body
) {
const {
inputs: { gameProposals, gameStarts, gameActions },
} = liveTable(tableKey);
if ("proposeGame" in body) {
gameProposals.emit(body);
} else if ("startGame" in body) {
gameStarts.emit(body);
} else if ("action" in body) {
gameActions.emit({ humanKey, ...body.action });
}
},
async close({
data: {
params: { tableKey },

View File

@@ -71,9 +71,16 @@ export const resolveAction = (
humanId: string,
action: SimpleAction
): SimpleGameState => {
console.log("attempting to resolve action", JSON.stringify(action));
if (!(humanId in state.players)) {
throw Error(
`${humanId} is not a player in this game; they cannot perform actions`
);
}
const playerHand = state.players[humanId];
if (action.type == "draw") {
const [drawn, ...rest] = state.deck;
console.log("drew card", JSON.stringify(drawn));
return {
deck: rest,
players: {

View File

@@ -21,15 +21,16 @@ export const WsIn = t.Union([
]);
export type TWsIn = typeof WsIn.static;
type Attributed = { humanKey: string };
type TablePayload<GameState, GameAction> = {
inputs: {
presenceChanges: TBus<
{ humanKey: string; presence: "joined" | "left" },
Attributed & { presence: "joined" | "left" },
never
>;
gameProposals: TBus<{ proposeGame: string }, never>;
gameStarts: TBus<{ startGame: true }, never>;
gameActions: TBus<GameAction, never>;
gameActions: TBus<Attributed & GameAction, never>;
};
outputs: {
playersPresent: Property<string[], never>;
@@ -63,26 +64,22 @@ export const liveTable = <GameState, GameAction>(key: string) => {
}, [] as string[]);
const gameState = transform(
null as GameState | null,
null as SimpleGameState | null,
[
gameStarts.thru((Evt) =>
combine([Evt], [playersPresent], (evt, players) => ({
...evt,
players,
}))
),
combine([gameStarts], [playersPresent], (evt, players) => ({
...evt,
players,
})),
(prev, evt: { players: string[] }) =>
prev == null ? (newGame(evt.players) as GameState) : prev,
prev == null
? (newGame(evt.players) as SimpleGameState)
: prev,
],
[
gameActions,
(prev, evt: GameAction) =>
(prev, evt: Attributed & SimpleAction) =>
prev != null
? (resolveAction(
prev as unknown as SimpleGameState,
"evt",
evt as SimpleAction
) as GameState)
? resolveAction(prev, evt.humanKey, evt)
: prev,
]
).toProperty();