Compare commits
2 Commits
e6cee7c2fa
...
0124b69440
| Author | SHA1 | Date | |
|---|---|---|---|
| 0124b69440 | |||
| e4f6e1899d |
1
Makefile
1
Makefile
@@ -5,6 +5,7 @@ build:
|
|||||||
--cache-from=type=local,src=/var/cache/docker-build \
|
--cache-from=type=local,src=/var/cache/docker-build \
|
||||||
--cache-to=type=local,dest=/var/cache/docker-build \
|
--cache-to=type=local,dest=/var/cache/docker-build \
|
||||||
--platform linux/arm64 \
|
--platform linux/arm64 \
|
||||||
|
--progress=plain \
|
||||||
--tag games .
|
--tag games .
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
BIN
assets/sources/Card_back_01.svg
LFS
Normal file
BIN
assets/sources/Card_back_01.svg
LFS
Normal file
Binary file not shown.
1
assets/views/back.svg
Symbolic link
1
assets/views/back.svg
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../sources/Card_back_01.svg
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "games",
|
"name": "games",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.0.1",
|
"version": "0.0.2",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vinxi dev",
|
"dev": "vinxi dev",
|
||||||
"build": "vinxi build",
|
"build": "vinxi build",
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -50,3 +54,7 @@ body::before {
|
|||||||
bottom: 0;
|
bottom: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clear {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|||||||
12
src/app.tsx
12
src/app.tsx
@@ -5,8 +5,16 @@ import { Suspense } from "solid-js";
|
|||||||
import pkg from "~/../package.json";
|
import pkg from "~/../package.json";
|
||||||
|
|
||||||
const Version = () => (
|
const Version = () => (
|
||||||
<div class="full free">
|
<div class="full free clear">
|
||||||
<span style={{ margin: "5px", "font-size": "0.8rem" }} class="fixed-br">
|
<span
|
||||||
|
style={{
|
||||||
|
margin: "5px",
|
||||||
|
"font-size": "0.8rem",
|
||||||
|
"font-family": "monospace",
|
||||||
|
"pointer-events": "all",
|
||||||
|
}}
|
||||||
|
class="fixed-br"
|
||||||
|
>
|
||||||
v{pkg.version}
|
v{pkg.version}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +1,49 @@
|
|||||||
import { Component, createResource, Suspense } from "solid-js";
|
import { Component, createResource, JSX, Suspense } from "solid-js";
|
||||||
|
import { Card } from "../types/cards";
|
||||||
|
|
||||||
export default (() => {
|
const cardToSvgFilename = (card: Card) => {
|
||||||
const [svgPath] = createResource(
|
if (card.kind == "joker") {
|
||||||
() => import(`~/../assets/views/cards/CLUB-1.svg`)
|
return `JOKER-${card.color == "black" ? "2" : "3"}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value =
|
||||||
|
{ ace: 1, jack: 11, queen: 12, king: 13 }[
|
||||||
|
card.rank as "ace" | "jack" | "queen" | "king" // fuck you typescript
|
||||||
|
] ?? (card.rank as number);
|
||||||
|
return `${card.suit.toUpperCase()}-${value}${
|
||||||
|
value >= 11 ? "-" + (card.rank as string).toUpperCase() : ""
|
||||||
|
}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ((props) => {
|
||||||
|
const [svgPath] = createResource(() =>
|
||||||
|
props.face == "down"
|
||||||
|
? // @ts-ignore
|
||||||
|
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;
|
}) satisfies Component<{
|
||||||
|
class?: string;
|
||||||
|
style?: JSX.CSSProperties;
|
||||||
|
card: Card;
|
||||||
|
face?: "up" | "down";
|
||||||
|
}>;
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
<GameContext.Provider value={{ gameState, setGameState }}>
|
||||||
<div
|
<div
|
||||||
class="full center column"
|
onClick={() => {}}
|
||||||
|
class="full column"
|
||||||
style={{ "row-gap": "20px", "font-size": "32px" }}
|
style={{ "row-gap": "20px", "font-size": "32px" }}
|
||||||
>
|
>
|
||||||
games
|
<div class="full center">
|
||||||
<Card />
|
<Pile
|
||||||
coming soon
|
pile={gameState.pile}
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
onClick={() =>
|
||||||
|
setGameState(
|
||||||
|
produce((state) => {
|
||||||
|
state.hand.push(state.pile.pop()!);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<Hand hand={gameState.hand} />
|
||||||
|
</div>
|
||||||
|
</GameContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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 }>;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}>;
|
||||||
|
|||||||
@@ -1,11 +1,57 @@
|
|||||||
export type Suit = "hearts" | "diamonds" | "spades" | "clubs";
|
const suits = ["heart", "diamond", "spade", "club"] as const;
|
||||||
|
export type Suit = (typeof suits)[number];
|
||||||
|
|
||||||
export type Card = {
|
const ranks = [
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
10,
|
||||||
|
"jack",
|
||||||
|
"queen",
|
||||||
|
"king",
|
||||||
|
"ace",
|
||||||
|
] as const;
|
||||||
|
export type Rank = (typeof ranks)[number];
|
||||||
|
|
||||||
|
export type Card =
|
||||||
|
| {
|
||||||
|
kind: "normal";
|
||||||
suit: Suit;
|
suit: Suit;
|
||||||
value: number;
|
rank: Rank;
|
||||||
};
|
}
|
||||||
|
| { kind: "joker"; color: "red" | "black" };
|
||||||
|
|
||||||
export type Pile = Card[];
|
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;
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user