basic drawing

This commit is contained in:
2025-08-03 14:43:29 -04:00
parent e4f6e1899d
commit 0124b69440
9 changed files with 200 additions and 38 deletions

BIN
assets/sources/Card_back_01.svg LFS Normal file

Binary file not shown.

1
assets/views/back.svg Symbolic link
View File

@@ -0,0 +1 @@
../sources/Card_back_01.svg

View File

@@ -28,6 +28,10 @@ body::before {
width: 100%; width: 100%;
} }
.w-full {
width: 100%;
}
.center { .center {
display: flex; display: flex;
justify-content: center; justify-content: center;

View File

@@ -1,4 +1,4 @@
import { Component, createResource, Suspense } from "solid-js"; import { Component, createResource, JSX, Suspense } from "solid-js";
import { Card } from "../types/cards"; import { Card } from "../types/cards";
const cardToSvgFilename = (card: Card) => { const cardToSvgFilename = (card: Card) => {
@@ -7,24 +7,43 @@ const cardToSvgFilename = (card: Card) => {
} }
const value = const value =
{ ace: 1, jack: 11, queen: 12, king: 13 }[card.rank] ?? { ace: 1, jack: 11, queen: 12, king: 13 }[
(card.rank as number); card.rank as "ace" | "jack" | "queen" | "king" // fuck you typescript
] ?? (card.rank as number);
return `${card.suit.toUpperCase()}-${value}${ return `${card.suit.toUpperCase()}-${value}${
value >= 11 ? "-" + (card.rank as string).toUpperCase() : "" value >= 11 ? "-" + (card.rank as string).toUpperCase() : ""
}`; }`;
}; };
export default ((props) => { export default ((props) => {
const [svgPath] = createResource( const [svgPath] = createResource(() =>
() => props.face == "down"
import( ? // @ts-ignore
`~/../assets/views/cards/${cardToSvgFilename(props.card)}.svg` import("~/../assets/views/back.svg")
) : import(
`~/../assets/views/cards/${cardToSvgFilename(
props.card
)}.svg`
)
); );
return ( return (
<Suspense> <Suspense>
<img src={svgPath()?.default} /> <img
draggable={false}
class={props.class}
style={{
"border-radius": "5px",
...props.style,
}}
width="100px"
src={svgPath()?.default}
/>
</Suspense> </Suspense>
); );
}) satisfies Component<{ card: Card }>; }) satisfies Component<{
class?: string;
style?: JSX.CSSProperties;
card: Card;
face?: "up" | "down";
}>;

View File

@@ -1,15 +1,40 @@
import { JSX } from "solid-js"; import { createContext, JSX } from "solid-js";
import Card from "./Card"; import Card from "./Card";
import Hand from "./Hand";
import Pile from "./Pile";
import { newDeck, shuffle, Hand as THand } from "../types/cards";
import { createStore, produce } from "solid-js/store";
const GameContext = createContext();
export default () => { export default () => {
const [gameState, setGameState] = createStore({
pile: shuffle(newDeck()),
hand: [] as THand,
});
return ( return (
<div <GameContext.Provider value={{ gameState, setGameState }}>
class="full center column" <div
style={{ "row-gap": "20px", "font-size": "32px" }} onClick={() => {}}
> class="full column"
games style={{ "row-gap": "20px", "font-size": "32px" }}
<Card card={{ kind: "joker", color: "red" }} /> >
coming soon <div class="full center">
</div> <Pile
pile={gameState.pile}
style={{ cursor: "pointer" }}
onClick={() =>
setGameState(
produce((state) => {
state.hand.push(state.pile.pop()!);
})
)
}
/>
</div>
<Hand hand={gameState.hand} />
</div>
</GameContext.Provider>
); );
}; };

View File

@@ -0,0 +1,24 @@
import { Component, For } from "solid-js";
import Card from "./Card";
import { Hand } from "../types/cards";
export default ((props) => {
return (
<div
style={{
border: "2px dashed white",
"border-radius": "12px",
margin: "10px",
"margin-bottom": "25px",
padding: "10px",
height: "200px",
overflow: "scroll",
"scrollbar-width": "none",
display: "flex",
gap: "5px",
}}
>
<For each={props.hand}>{(card) => <Card card={card} />}</For>
</div>
);
}) satisfies Component<{ hand: Hand }>;

View File

@@ -0,0 +1,44 @@
import { Component, For, JSX } from "solid-js";
import Card from "./Card";
import { Pile } from "../types/cards";
export default ((props) => {
return (
<div
class="center"
style={{ width: "200px", height: "400px", ...props.style }}
onClick={props.onClick}
>
<For each={props.pile}>
{(card, i) => (
<Card
card={card}
face="down"
style={{
position: "absolute",
transform: `translate(${i() * 0.8}px, ${
-i() * 0.4
}px)`,
"z-index": 100 - i(),
border: `0.1px solid rgb(${
60 - i() + Math.random() * 10
}, ${60 - i() + Math.random() * 10}, ${
60 - i() + Math.random() * 10
});`,
}}
/>
)}
</For>
</div>
);
}) satisfies Component<{
pile: Pile;
style?: JSX.CSSProperties;
onClick?:
| JSX.EventHandlerUnion<
HTMLDivElement,
MouseEvent,
JSX.EventHandler<HTMLDivElement, MouseEvent>
>
| undefined;
}>;

View File

@@ -1,6 +1,23 @@
export type Suit = "heart" | "diamond" | "spade" | "club"; const suits = ["heart", "diamond", "spade", "club"] as const;
export type Suit = (typeof suits)[number];
const ranks = [
2,
3,
4,
5,
6,
7,
8,
9,
10,
"jack",
"queen",
"king",
"ace",
] as const;
export type Rank = (typeof ranks)[number];
type Rank = number | "jack" | "queen" | "king" | "ace";
export type Card = export type Card =
| { | {
kind: "normal"; kind: "normal";
@@ -13,3 +30,28 @@ export type Pile = Card[];
export type Stack = Card[]; export type Stack = Card[];
export type Hand = Card[]; export type Hand = Card[];
export type Board = Card[]; export type Board = Card[];
export const newDeck = (withJokers = false): Pile =>
suits
.map((suit) =>
ranks.map((rank) => ({ kind: "normal", suit, rank } as Card))
)
.flat()
.concat(
withJokers
? [
{ kind: "joker", color: "red" },
{ kind: "joker", color: "black" },
]
: []
);
export const shuffle = (cards: Card[]) => {
let i = cards.length;
while (i > 0) {
const j = Math.floor(Math.random() * i);
i--;
[cards[i], cards[j]] = [cards[j], cards[i]];
}
return cards;
};

View File

@@ -1,19 +1,19 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ESNext", "target": "ESNext",
"module": "ESNext", "module": "ESNext",
"moduleResolution": "bundler", "moduleResolution": "bundler",
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"esModuleInterop": true, "esModuleInterop": true,
"jsx": "preserve", "jsx": "preserve",
"jsxImportSource": "solid-js", "jsxImportSource": "solid-js",
"allowJs": true, "allowJs": true,
"strict": true, "strict": true,
"noEmit": true, "noEmit": true,
"types": ["vinxi/types/client"], "types": ["vinxi/types/client"],
"isolatedModules": true, "isolatedModules": true,
"paths": { "paths": {
"~/*": ["./src/*"] "~/*": ["./src/*"]
} }
} }
} }