errors
This commit is contained in:
5
Makefile
5
Makefile
@@ -8,3 +8,8 @@ build:
|
||||
|
||||
start:
|
||||
PORT=$(PORT) pnpm start
|
||||
|
||||
note:
|
||||
./notes/newfile
|
||||
# touch ./notes/$$file.md
|
||||
# code -r ./notes/$$file.md
|
||||
5
notes/2025-09-03-224857.md
Normal file
5
notes/2025-09-03-224857.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# 2025-09-03-224857
|
||||
|
||||
The backend has a pretty good abstraction of a `Game`, that it can swap in implemenations for.
|
||||
The frontend is still mostly hardcoded to the test `simple` game.
|
||||
Will need to refine the concept of `Game` to include the necessary components of displaying and interacting with a game.
|
||||
7
notes/newfile
Executable file
7
notes/newfile
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
ts=$(date +"%Y-%m-%d-%H%M%S")
|
||||
file=./notes/$ts.md
|
||||
touch $file
|
||||
echo -e "# $ts\n" > $file
|
||||
echo "$file:end"
|
||||
code --goto "$file:2"
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
cx,
|
||||
} from "~/fn";
|
||||
import { me, mePromise } from "~/profile";
|
||||
import Game from "./Game";
|
||||
import Simple from "./games/simple";
|
||||
import Player from "./Player";
|
||||
import games from "@games/shared/games/index";
|
||||
import { createStore, Store } from "solid-js/store";
|
||||
@@ -157,7 +157,7 @@ export default (props: { tableKey: string }) => {
|
||||
</Show>
|
||||
</div>
|
||||
<Show when={view() != null}>
|
||||
<Game />
|
||||
<Simple />
|
||||
</Show>
|
||||
<Show when={results() != null}>
|
||||
<span class="bg-[var(--light)] text-[var(--dark)] rounded-[24px] border-2 border-[var(--dark)] absolute center p-4 shadow-lg text-[4em] text-center">
|
||||
|
||||
@@ -5,11 +5,11 @@ import type {
|
||||
SimpleResult,
|
||||
} from "@games/shared/games/simple";
|
||||
import { me } from "~/profile";
|
||||
import Hand from "./Hand";
|
||||
import Pile from "./Pile";
|
||||
import { TableContext } from "./Table";
|
||||
import Hand from "../Hand";
|
||||
import Pile from "../Pile";
|
||||
import { TableContext } from "../Table";
|
||||
import { Portal } from "solid-js/web";
|
||||
import FannedHand from "./FannedHand";
|
||||
import FannedHand from "../FannedHand";
|
||||
|
||||
export const GameContext = createContext<{
|
||||
view: Accessor<SimplePlayerView>;
|
||||
@@ -202,7 +202,7 @@ export const liveTable = <
|
||||
|
||||
const gameImpl = gameConfig
|
||||
.filter((cfg) => cfg.game in GAMES)
|
||||
.map((config) => GAMES[config.game as GameKey](config))
|
||||
.map((config) => GAMES[config.game as GameKey].impl(config))
|
||||
.toProperty();
|
||||
|
||||
const withGame = <T>(obs: Observable<T, any>) =>
|
||||
@@ -226,7 +226,7 @@ export const liveTable = <
|
||||
prev,
|
||||
[{ action, humanKey }, game]: [
|
||||
Attributed & { action: GameAction },
|
||||
Game
|
||||
ReturnType<Game["impl"]>
|
||||
]
|
||||
) =>
|
||||
prev &&
|
||||
|
||||
@@ -3,21 +3,28 @@ import simple from "./simple";
|
||||
export type Game<
|
||||
S = unknown, // state
|
||||
A = unknown, // action
|
||||
E extends { error: any } = { error: any }, // error
|
||||
E = unknown, // error
|
||||
V = unknown, // view
|
||||
R = unknown // results
|
||||
R = unknown, // results
|
||||
C extends { game: string; players: string[] } = {
|
||||
game: string;
|
||||
players: string[];
|
||||
}
|
||||
> = {
|
||||
title: string;
|
||||
rules: string;
|
||||
init: () => S;
|
||||
resolveAction: (p: { state: S; action: A; humanKey: string }) => S | E;
|
||||
getView: (p: { state: S; humanKey: string }) => V;
|
||||
resolveQuit: (p: { state: S; humanKey: string }) => S;
|
||||
getResult: (state: S) => R | undefined;
|
||||
impl: (config: C) => {
|
||||
title: string;
|
||||
rules: string;
|
||||
init: () => S;
|
||||
resolveAction: (p: { state: S; action: A; humanKey: string }) => S | E;
|
||||
getView: (p: { state: S; humanKey: string }) => V;
|
||||
resolveQuit: (p: { state: S; humanKey: string }) => S;
|
||||
getResult: (state: S) => R | undefined;
|
||||
};
|
||||
defaultConfig: C;
|
||||
};
|
||||
|
||||
export const GAMES: {
|
||||
[key: string]: (config: { game: string; players: string[] }) => Game;
|
||||
[key: string]: Game;
|
||||
} = {
|
||||
// renaissance,
|
||||
simple,
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { Card, Hand, newDeck, Pile, shuffle, vCard } from "@games/shared/cards";
|
||||
import { heq } from "@games/shared/utils";
|
||||
import type { Game } from ".";
|
||||
import { XOR } from "ts-xor";
|
||||
|
||||
export type SimpleConfiguration = {
|
||||
game: "simple";
|
||||
players: string[];
|
||||
"can discard": boolean;
|
||||
"cards to win": number;
|
||||
};
|
||||
|
||||
// omniscient game state
|
||||
@@ -50,6 +53,16 @@ export const getSimplePlayerView = (
|
||||
),
|
||||
});
|
||||
|
||||
// type SimpleError = XOR<
|
||||
// { "go away": string },
|
||||
// { chill: string },
|
||||
// { "ah ah": string }
|
||||
// >;
|
||||
type SimpleError = {
|
||||
class: "go away" | "chill" | "ah ah";
|
||||
message: string;
|
||||
};
|
||||
|
||||
export const resolveSimpleAction = ({
|
||||
config,
|
||||
state,
|
||||
@@ -62,13 +75,18 @@ export const resolveSimpleAction = ({
|
||||
humanKey: string;
|
||||
}): SimpleGameState => {
|
||||
const playerHand = state.playerHands[humanKey];
|
||||
|
||||
if (playerHand == null) {
|
||||
throw new Error(
|
||||
`${humanKey} is not a player in this game; they cannot perform actions`
|
||||
);
|
||||
throw {
|
||||
message: "You are not a part of this game!",
|
||||
class: "go away",
|
||||
} satisfies SimpleError;
|
||||
}
|
||||
if (humanKey != config.players[state.turnIdx]) {
|
||||
throw new Error(`It's not ${humanKey}'s turn!`);
|
||||
throw {
|
||||
message: "It's not your turn!",
|
||||
class: "chill",
|
||||
} satisfies SimpleError;
|
||||
}
|
||||
|
||||
const numPlayers = Object.keys(state.playerHands).length;
|
||||
@@ -87,6 +105,13 @@ export const resolveSimpleAction = ({
|
||||
};
|
||||
} else {
|
||||
// action.type == discard
|
||||
if (config["can discard"] == false) {
|
||||
throw {
|
||||
message: "You're not allowed to discard!",
|
||||
class: "ah ah",
|
||||
} satisfies SimpleError;
|
||||
}
|
||||
|
||||
const cardIndex = playerHand.findIndex(heq(action.card));
|
||||
return {
|
||||
deck: [action.card, ...state.deck],
|
||||
@@ -103,10 +128,14 @@ export const resolveSimpleAction = ({
|
||||
|
||||
export type SimpleResult = string;
|
||||
|
||||
type SimpleError = { error: "whoops!" };
|
||||
|
||||
export default (config: SimpleConfiguration) =>
|
||||
({
|
||||
export default {
|
||||
defaultConfig: {
|
||||
game: "simple",
|
||||
players: [],
|
||||
"can discard": true,
|
||||
"cards to win": 5,
|
||||
},
|
||||
impl: (config: SimpleConfiguration) => ({
|
||||
title: "Simple",
|
||||
rules: "You can draw, or you can discard. Then your turn is up.",
|
||||
init: () => newSimpleGameState(config),
|
||||
@@ -118,10 +147,12 @@ export default (config: SimpleConfiguration) =>
|
||||
Object.entries(state.playerHands).find(
|
||||
([_, hand]) => hand.length === 52
|
||||
)?.[0],
|
||||
} satisfies Game<
|
||||
SimpleGameState,
|
||||
SimpleAction,
|
||||
SimpleError,
|
||||
SimplePlayerView,
|
||||
SimpleResult
|
||||
>);
|
||||
}),
|
||||
} satisfies Game<
|
||||
SimpleGameState,
|
||||
SimpleAction,
|
||||
SimpleError,
|
||||
SimplePlayerView,
|
||||
SimpleResult,
|
||||
SimpleConfiguration
|
||||
>;
|
||||
|
||||
Reference in New Issue
Block a user