result states
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import GAMES, { Game, GameKey } from "@games/shared/games";
|
||||
import {
|
||||
invert,
|
||||
isEmpty,
|
||||
multiScan,
|
||||
partition,
|
||||
@@ -8,7 +9,7 @@ import {
|
||||
ValueWithin,
|
||||
} from "@games/shared/kefirs";
|
||||
import { t } from "elysia";
|
||||
import { combine, Observable, pool, Property } from "kefir";
|
||||
import { combine, constant, merge, Observable, pool, Property } from "kefir";
|
||||
import Bus, { type Bus as TBus } from "kefir-bus";
|
||||
import { log } from "./logging";
|
||||
|
||||
@@ -17,6 +18,7 @@ export const WsOut = t.Object({
|
||||
playersReady: t.Optional(t.Nullable(t.Record(t.String(), t.Boolean()))),
|
||||
gameConfig: t.Optional(t.Any()),
|
||||
view: t.Optional(t.Any()),
|
||||
results: t.Optional(t.Any()),
|
||||
});
|
||||
export type TWsOut = typeof WsOut.static;
|
||||
export const WsIn = t.Union([
|
||||
@@ -30,7 +32,8 @@ type Attributed = { humanKey: string };
|
||||
type TablePayload<
|
||||
GameConfig = unknown,
|
||||
GameView = unknown,
|
||||
GameAction = unknown
|
||||
GameAction = unknown,
|
||||
GameResult = unknown
|
||||
> = {
|
||||
inputs: {
|
||||
connectionChanges: TBus<
|
||||
@@ -51,6 +54,7 @@ type TablePayload<
|
||||
any
|
||||
>;
|
||||
gameConfig: Property<GameConfig | null, any>;
|
||||
results: Property<GameResult, any>;
|
||||
};
|
||||
player: {
|
||||
[key: string]: {
|
||||
@@ -71,12 +75,18 @@ export const liveTable = <
|
||||
},
|
||||
GameState,
|
||||
GameAction extends Attributed,
|
||||
GameView
|
||||
GameView,
|
||||
GameResult
|
||||
>(
|
||||
key: string
|
||||
) => {
|
||||
if (!(key in tables)) {
|
||||
const inputs: TablePayload<GameConfig, GameState, GameAction>["inputs"] = {
|
||||
const inputs: TablePayload<
|
||||
GameConfig,
|
||||
GameState,
|
||||
GameAction,
|
||||
GameResult
|
||||
>["inputs"] = {
|
||||
connectionChanges: Bus(),
|
||||
messages: Bus(),
|
||||
};
|
||||
@@ -110,7 +120,7 @@ export const liveTable = <
|
||||
.onValue(({ added, removed }) => {
|
||||
added.forEach((p) => {
|
||||
playerStreams[p] = {
|
||||
view: combine([gameState], [gameImpl], (a, b) => [a, b] as const)
|
||||
view: withGame(gameState)
|
||||
.map(
|
||||
([state, game]) =>
|
||||
state && (game.getView({ state, humanKey: p }) as GameView)
|
||||
@@ -178,16 +188,29 @@ export const liveTable = <
|
||||
.map((config) => GAMES[config.game as GameKey](config))
|
||||
.toProperty();
|
||||
|
||||
const withGame = <T>(obs: Observable<T, any>) =>
|
||||
combine([obs], [gameImpl], (o, game) => [o, game] as const);
|
||||
|
||||
const resultsPool = pool<GameResult | null, any>();
|
||||
const results = merge([constant(null), resultsPool]).toProperty();
|
||||
|
||||
const gameIsActivePool = pool<boolean, any>();
|
||||
const gameIsActive = merge([
|
||||
constant(false),
|
||||
gameIsActivePool,
|
||||
]).toProperty();
|
||||
|
||||
const gameState = multiScan(
|
||||
null as GameState | null,
|
||||
[
|
||||
// initialize game state when started
|
||||
gameImpl.sampledBy(gameStarts),
|
||||
gameImpl.sampledBy(gameStarts).filterBy(invert(gameIsActive)),
|
||||
(prev, game: ValueWithin<typeof gameImpl>) =>
|
||||
prev || (game.init() as GameState),
|
||||
],
|
||||
[
|
||||
combine([action], [gameImpl], (act, game) => [act, game] as const),
|
||||
// handle actions from players
|
||||
action.filterBy(gameIsActive).thru(withGame),
|
||||
(
|
||||
prev,
|
||||
[{ action, humanKey }, game]: [
|
||||
@@ -202,13 +225,25 @@ export const liveTable = <
|
||||
humanKey,
|
||||
}) as GameState),
|
||||
],
|
||||
[quit, () => null]
|
||||
[results.filterBy(gameIsActive), () => null], // handle game ending criteria
|
||||
[quit, () => null] // handle players leaving the room
|
||||
).toProperty();
|
||||
|
||||
const gameIsActive = gameState
|
||||
.map((gs) => gs != null)
|
||||
.skipDuplicates()
|
||||
.toProperty();
|
||||
resultsPool.plug(
|
||||
gameState
|
||||
.filter((state) => state != null)
|
||||
.thru(withGame)
|
||||
.map(([state, game]) => game.getResult(state) as unknown as GameResult)
|
||||
.filter((result) => result != null)
|
||||
.merge(quit.map(() => null))
|
||||
);
|
||||
|
||||
gameIsActivePool.plug(
|
||||
combine([gameState, results])
|
||||
.map(([state, result]) => state != null && result == null)
|
||||
.skipDuplicates()
|
||||
.toProperty()
|
||||
);
|
||||
|
||||
gameConfigPool.plug(
|
||||
multiScan(
|
||||
@@ -234,6 +269,7 @@ export const liveTable = <
|
||||
playersPresent,
|
||||
playersReady,
|
||||
gameConfig,
|
||||
results,
|
||||
},
|
||||
player: playerStreams,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user