working e2e
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { Component, createResource, JSX, Suspense } from "solid-js";
|
import { Component, createResource, JSX, Suspense } from "solid-js";
|
||||||
import { Card, newDeck } from "../types/cards";
|
|
||||||
import { Clickable, Stylable } from "./toolbox";
|
import { Clickable, Stylable } from "./toolbox";
|
||||||
|
import { Card } from "../../../shared/cards";
|
||||||
|
|
||||||
const cardToSvgFilename = (card: Card) => {
|
const cardToSvgFilename = (card: Card) => {
|
||||||
if (card.kind == "joker") {
|
if (card.kind == "joker") {
|
||||||
@@ -37,9 +38,13 @@ export default ((props) => {
|
|||||||
</Suspense>
|
</Suspense>
|
||||||
);
|
);
|
||||||
}) satisfies Component<
|
}) satisfies Component<
|
||||||
{
|
(
|
||||||
card: Card;
|
| {
|
||||||
face?: "up" | "down";
|
card: Card;
|
||||||
} & Stylable &
|
face?: "up";
|
||||||
|
}
|
||||||
|
| { card?: Card; face: "down" }
|
||||||
|
) &
|
||||||
|
Stylable &
|
||||||
Clickable
|
Clickable
|
||||||
>;
|
>;
|
||||||
|
|||||||
@@ -3,46 +3,45 @@ import {
|
|||||||
GameState,
|
GameState,
|
||||||
Action,
|
Action,
|
||||||
vGameState,
|
vGameState,
|
||||||
|
PlayerView,
|
||||||
} from "../../../server/src/games/simple";
|
} from "../../../server/src/games/simple";
|
||||||
import api from "../api";
|
import api from "../api";
|
||||||
import Hand from "./Hand";
|
import Hand from "./Hand";
|
||||||
import Pile from "./Pile";
|
import Pile from "./Pile";
|
||||||
|
|
||||||
export const GameContext = createContext<{
|
export const GameContext = createContext<{
|
||||||
gameState: Accessor<GameState | undefined>;
|
view: Accessor<PlayerView | undefined>;
|
||||||
submitAction: (action: Action) => Promise<any>;
|
submitAction: (action: Action) => Promise<any>;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
export default (props: { instanceId: string }) => {
|
export default (props: { instanceId: string }) => {
|
||||||
const [gameState, { mutate }] = createResource(() =>
|
const [view, { mutate }] = createResource(() =>
|
||||||
api
|
api
|
||||||
.simple(props)
|
.simple(props)
|
||||||
.get()
|
.get()
|
||||||
.then((res) => res.data as GameState)
|
.then((res) => res.data as PlayerView)
|
||||||
);
|
);
|
||||||
const submitAction = (action: Action) =>
|
const submitAction = (action: Action) =>
|
||||||
api
|
api
|
||||||
.simple(props)
|
.simple(props)
|
||||||
.post({ action })
|
.post({ action })
|
||||||
.then((res) => res.status == 200 && mutate(res.data as vGameState));
|
.then((res) => res.status == 200 && mutate(res.data as PlayerView));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GameContext.Provider value={{ gameState, submitAction }}>
|
<GameContext.Provider value={{ view, submitAction }}>
|
||||||
<Show when={gameState.latest != undefined}>
|
<Show when={view.latest != undefined}>
|
||||||
<div
|
<div
|
||||||
class="full column center"
|
class="full column center"
|
||||||
style={{ "row-gap": "20px", "font-size": "32px" }}
|
style={{ "row-gap": "20px", "font-size": "32px" }}
|
||||||
>
|
>
|
||||||
<div class="full center">
|
<div class="full center">
|
||||||
<Pile
|
<Pile
|
||||||
pile={gameState.latest!.deck}
|
count={view.latest!.deckCount}
|
||||||
style={{ cursor: "pointer" }}
|
style={{ cursor: "pointer" }}
|
||||||
onClick={() =>
|
onClick={() => submitAction({ type: "draw" })}
|
||||||
api.simple({ instanceId: props.instanceId })
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Hand hand={gameState.latest!.players["daniel"]} />
|
<Hand hand={view.latest!.myHand} />
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
</GameContext.Provider>
|
</GameContext.Provider>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { GameContext } from "./Game";
|
|||||||
import { produce } from "solid-js/store";
|
import { produce } from "solid-js/store";
|
||||||
|
|
||||||
export default ((props) => {
|
export default ((props) => {
|
||||||
const { submitAction, gameState } = useContext(GameContext)!;
|
const { submitAction, view } = useContext(GameContext)!;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Component, For, JSX } from "solid-js";
|
import { Component, For, JSX, Show } from "solid-js";
|
||||||
import Card from "./Card";
|
import Card from "./Card";
|
||||||
import { Pile } from "../types/cards";
|
|
||||||
import { type ComponentProps } from "solid-js";
|
|
||||||
import { Clickable, Stylable } from "./toolbox";
|
import { Clickable, Stylable } from "./toolbox";
|
||||||
|
|
||||||
export default ((props) => {
|
export default ((props) => {
|
||||||
@@ -16,31 +15,14 @@ export default ((props) => {
|
|||||||
}}
|
}}
|
||||||
onClick={props.onClick}
|
onClick={props.onClick}
|
||||||
>
|
>
|
||||||
<For each={props.pile}>
|
<Show when={props.count > 0}>
|
||||||
{(card, i) => (
|
<Card face="down" />
|
||||||
<Card
|
</Show>
|
||||||
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>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}) satisfies Component<
|
}) satisfies Component<
|
||||||
{
|
{
|
||||||
pile: Pile;
|
count: number;
|
||||||
} & Stylable &
|
} & Stylable &
|
||||||
Clickable
|
Clickable
|
||||||
>;
|
>;
|
||||||
|
|||||||
@@ -47,7 +47,10 @@ export const newGame = (players: string[]) => {
|
|||||||
} as GameState;
|
} as GameState;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getKnowledge = (state: GameState, humanId: string) => ({
|
export const getKnowledge = (
|
||||||
|
state: GameState,
|
||||||
|
humanId: string
|
||||||
|
): vGameState => ({
|
||||||
humanId,
|
humanId,
|
||||||
deck: state.deck.map((_) => null),
|
deck: state.deck.map((_) => null),
|
||||||
players: Object.fromEntries(
|
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 = (
|
export const resolveAction = (
|
||||||
state: GameState,
|
state: GameState,
|
||||||
humanId: string,
|
humanId: string,
|
||||||
@@ -112,21 +126,31 @@ export const simpleApi = new Elysia({ prefix: "/simple" })
|
|||||||
)
|
)
|
||||||
.group("/:instanceId", (app) =>
|
.group("/:instanceId", (app) =>
|
||||||
app
|
app
|
||||||
.get("/", ({ params: { instanceId } }) =>
|
.get(
|
||||||
prisma.instance
|
"/",
|
||||||
.findUnique({
|
({ params: { instanceId }, headers: { human: humanId } }) =>
|
||||||
where: {
|
prisma.instance
|
||||||
id: instanceId,
|
.findUnique({
|
||||||
},
|
where: {
|
||||||
})
|
id: instanceId,
|
||||||
.then((game) => game?.gameState)
|
},
|
||||||
|
})
|
||||||
|
.then((game) =>
|
||||||
|
getView(
|
||||||
|
getKnowledge(
|
||||||
|
game!.gameState as GameState,
|
||||||
|
humanId!
|
||||||
|
),
|
||||||
|
humanId!
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.post(
|
.post(
|
||||||
"/",
|
"/",
|
||||||
({
|
({
|
||||||
params: { instanceId },
|
params: { instanceId },
|
||||||
body: { action },
|
body: { action },
|
||||||
headers: { Human: humanId },
|
headers: { human: humanId },
|
||||||
}) =>
|
}) =>
|
||||||
prisma.instance
|
prisma.instance
|
||||||
.findUniqueOrThrow({
|
.findUniqueOrThrow({
|
||||||
@@ -134,20 +158,22 @@ export const simpleApi = new Elysia({ prefix: "/simple" })
|
|||||||
id: instanceId,
|
id: instanceId,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then((game) => {
|
.then(async (game) => {
|
||||||
const newState = resolveAction(
|
const newState = resolveAction(
|
||||||
game.gameState as GameState,
|
game.gameState as GameState,
|
||||||
humanId!,
|
humanId!,
|
||||||
action
|
action
|
||||||
);
|
);
|
||||||
const knownState = getKnowledge(newState, humanId!);
|
await prisma.instance.update({
|
||||||
void prisma.instance.update({
|
|
||||||
data: { gameState: newState },
|
data: { gameState: newState },
|
||||||
where: {
|
where: {
|
||||||
id: instanceId,
|
id: instanceId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return knownState;
|
return getView(
|
||||||
|
getKnowledge(newState, humanId!),
|
||||||
|
humanId!
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
body: t.Object({
|
body: t.Object({
|
||||||
|
|||||||
Reference in New Issue
Block a user