working e2e

This commit is contained in:
2025-08-10 12:40:02 -04:00
parent 5e8978c550
commit 32c516bf37
5 changed files with 67 additions and 55 deletions

View File

@@ -1,6 +1,7 @@
import { Component, createResource, JSX, Suspense } from "solid-js";
import { Card, newDeck } from "../types/cards";
import { Clickable, Stylable } from "./toolbox";
import { Card } from "../../../shared/cards";
const cardToSvgFilename = (card: Card) => {
if (card.kind == "joker") {
@@ -37,9 +38,13 @@ export default ((props) => {
</Suspense>
);
}) satisfies Component<
{
(
| {
card: Card;
face?: "up" | "down";
} & Stylable &
face?: "up";
}
| { card?: Card; face: "down" }
) &
Stylable &
Clickable
>;

View File

@@ -3,46 +3,45 @@ import {
GameState,
Action,
vGameState,
PlayerView,
} from "../../../server/src/games/simple";
import api from "../api";
import Hand from "./Hand";
import Pile from "./Pile";
export const GameContext = createContext<{
gameState: Accessor<GameState | undefined>;
view: Accessor<PlayerView | undefined>;
submitAction: (action: Action) => Promise<any>;
}>();
export default (props: { instanceId: string }) => {
const [gameState, { mutate }] = createResource(() =>
const [view, { mutate }] = createResource(() =>
api
.simple(props)
.get()
.then((res) => res.data as GameState)
.then((res) => res.data as PlayerView)
);
const submitAction = (action: Action) =>
api
.simple(props)
.post({ action })
.then((res) => res.status == 200 && mutate(res.data as vGameState));
.then((res) => res.status == 200 && mutate(res.data as PlayerView));
return (
<GameContext.Provider value={{ gameState, submitAction }}>
<Show when={gameState.latest != undefined}>
<GameContext.Provider value={{ view, submitAction }}>
<Show when={view.latest != undefined}>
<div
class="full column center"
style={{ "row-gap": "20px", "font-size": "32px" }}
>
<div class="full center">
<Pile
pile={gameState.latest!.deck}
count={view.latest!.deckCount}
style={{ cursor: "pointer" }}
onClick={() =>
api.simple({ instanceId: props.instanceId })
}
onClick={() => submitAction({ type: "draw" })}
/>
</div>
<Hand hand={gameState.latest!.players["daniel"]} />
<Hand hand={view.latest!.myHand} />
</div>
</Show>
</GameContext.Provider>

View File

@@ -5,7 +5,7 @@ import { GameContext } from "./Game";
import { produce } from "solid-js/store";
export default ((props) => {
const { submitAction, gameState } = useContext(GameContext)!;
const { submitAction, view } = useContext(GameContext)!;
return (
<div

View File

@@ -1,7 +1,6 @@
import { Component, For, JSX } from "solid-js";
import { Component, For, JSX, Show } from "solid-js";
import Card from "./Card";
import { Pile } from "../types/cards";
import { type ComponentProps } from "solid-js";
import { Clickable, Stylable } from "./toolbox";
export default ((props) => {
@@ -16,31 +15,14 @@ export default ((props) => {
}}
onClick={props.onClick}
>
<For each={props.pile}>
{(card, i) => (
<Card
card={card}
face="down"
style={{
position: "absolute",
transform: `translate(${i() * 0.5}px, ${
i() * 0.2
}px)`,
// "z-index": 100 - i(),
border: `0.1px solid rgb(${
10 + i() + Math.random() * 50
}, ${10 + i() + Math.random() * 50}, ${
10 + i() + Math.random() * 50
});`,
}}
/>
)}
</For>
<Show when={props.count > 0}>
<Card face="down" />
</Show>
</div>
);
}) satisfies Component<
{
pile: Pile;
count: number;
} & Stylable &
Clickable
>;

View File

@@ -47,7 +47,10 @@ export const newGame = (players: string[]) => {
} as GameState;
};
export const getKnowledge = (state: GameState, humanId: string) => ({
export const getKnowledge = (
state: GameState,
humanId: string
): vGameState => ({
humanId,
deck: state.deck.map((_) => null),
players: Object.fromEntries(
@@ -58,6 +61,17 @@ export const getKnowledge = (state: GameState, humanId: string) => ({
),
});
const getView = (state: vGameState, humanId: string): PlayerView => ({
humanId,
deckCount: state.deck.length,
myHand: state.players[humanId] as Hand,
playerHandCounts: Object.fromEntries(
Object.entries(state.players)
.filter(([id]) => id != humanId)
.map(([id, hand]) => [id, hand.length])
),
});
export const resolveAction = (
state: GameState,
humanId: string,
@@ -112,21 +126,31 @@ export const simpleApi = new Elysia({ prefix: "/simple" })
)
.group("/:instanceId", (app) =>
app
.get("/", ({ params: { instanceId } }) =>
.get(
"/",
({ params: { instanceId }, headers: { human: humanId } }) =>
prisma.instance
.findUnique({
where: {
id: instanceId,
},
})
.then((game) => game?.gameState)
.then((game) =>
getView(
getKnowledge(
game!.gameState as GameState,
humanId!
),
humanId!
)
)
)
.post(
"/",
({
params: { instanceId },
body: { action },
headers: { Human: humanId },
headers: { human: humanId },
}) =>
prisma.instance
.findUniqueOrThrow({
@@ -134,20 +158,22 @@ export const simpleApi = new Elysia({ prefix: "/simple" })
id: instanceId,
},
})
.then((game) => {
.then(async (game) => {
const newState = resolveAction(
game.gameState as GameState,
humanId!,
action
);
const knownState = getKnowledge(newState, humanId!);
void prisma.instance.update({
await prisma.instance.update({
data: { gameState: newState },
where: {
id: instanceId,
},
});
return knownState;
return getView(
getKnowledge(newState, humanId!),
humanId!
);
}),
{
body: t.Object({