This commit is contained in:
Gavin McDonald
2025-04-12 15:17:02 -04:00
parent 1734eec436
commit 6508d40b2d
19 changed files with 1415 additions and 1344 deletions

5
.prettierignore Normal file
View File

@@ -0,0 +1,5 @@
node_modules
.next
dist
build
coverage

7
.prettierrc Normal file
View File

@@ -0,0 +1,7 @@
{
"semi": true,
"singleQuote": true,
"useTabs": true,
"trailingComma": "all",
"printWidth": 100,
}

View File

@@ -1,8 +1,8 @@
"use client"; 'use client';
import { useEffect, useState } from "react"; import { useEffect, useState } from 'react';
import { useParams } from 'next/navigation'; import { useParams } from 'next/navigation';
import { socket } from "@/socket"; import { socket } from '@/socket';
import Card from '@/components/Card'; import Card from '@/components/Card';
@@ -18,7 +18,7 @@ export default function GamePage() {
if (gameIDParam) { if (gameIDParam) {
setGameID(Array.isArray(gameIDParam) ? gameIDParam[0] : gameIDParam); setGameID(Array.isArray(gameIDParam) ? gameIDParam[0] : gameIDParam);
} }
}, [gameIDParam]) }, [gameIDParam]);
useEffect(() => { useEffect(() => {
if (gameID) { if (gameID) {
@@ -35,10 +35,12 @@ export default function GamePage() {
}); });
} }
return gameID ? () => { return gameID
? () => {
socket.off('init'); socket.off('init');
socket.off('card-flipped'); socket.off('card-flipped');
} : undefined; }
: undefined;
}, [gameID]); }, [gameID]);
const flipCard = (cardIndex: number) => { const flipCard = (cardIndex: number) => {
@@ -58,9 +60,11 @@ export default function GamePage() {
return ( return (
<div key={i} className="aspect-[2/3]}"> <div key={i} className="aspect-[2/3]}">
{cardIndex !== -1 && <Card card={cards[cardIndex]} flipAction={() => flipCard(cardIndex)} />} {cardIndex !== -1 && (
<Card card={cards[cardIndex]} flipAction={() => flipCard(cardIndex)} />
)}
</div> </div>
) );
})} })}
</div> </div>
</main> </main>

View File

@@ -1,3 +0,0 @@
export default function B() {
return <div>b</div>;
}

View File

@@ -1,4 +1,4 @@
@import "tailwindcss"; @import 'tailwindcss';
:root { :root {
--background: #ffffff; --background: #ffffff;

View File

@@ -1,20 +1,20 @@
import type { Metadata } from "next"; import type { Metadata } from 'next';
import { Geist, Geist_Mono } from "next/font/google"; import { Geist, Geist_Mono } from 'next/font/google';
import "./globals.css"; import './globals.css';
const geistSans = Geist({ const geistSans = Geist({
variable: "--font-geist-sans", variable: '--font-geist-sans',
subsets: ["latin"], subsets: ['latin'],
}); });
const geistMono = Geist_Mono({ const geistMono = Geist_Mono({
variable: "--font-geist-mono", variable: '--font-geist-mono',
subsets: ["latin"], subsets: ['latin'],
}); });
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Create Next App", title: 'Create Next App',
description: "Generated by create next app", description: 'Generated by create next app',
}; };
export default function RootLayout({ export default function RootLayout({
@@ -24,11 +24,7 @@ export default function RootLayout({
}>) { }>) {
return ( return (
<html lang="en"> <html lang="en">
<body <body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>{children}</body>
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html> </html>
); );
} }

View File

@@ -21,4 +21,3 @@ export default function Home() {
</main> </main>
); );
} }

View File

@@ -1,6 +1,6 @@
'use client'; 'use client';
import { StandardGameCard, TarokkaGameCard } from "@/types"; import { StandardGameCard, TarokkaGameCard } from '@/types';
type CardProps = { type CardProps = {
card: StandardGameCard | TarokkaGameCard; card: StandardGameCard | TarokkaGameCard;
@@ -13,12 +13,7 @@ export default function Card({ card: { aria, url }, flipAction }: CardProps) {
className="h-[21vh] w-[15vh] flex items-center justify-center cursor-pointer transform transition-transform duration-200 hover:scale-150 relative z-0 hover:z-10" className="h-[21vh] w-[15vh] flex items-center justify-center cursor-pointer transform transition-transform duration-200 hover:scale-150 relative z-0 hover:z-10"
onClick={flipAction} onClick={flipAction}
> >
<img <img className="rounded-xl" src={url} alt={aria} />
className="rounded-xl"
src={url}
alt={aria}
/>
</div> </div>
); );
} }

View File

@@ -8,7 +8,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: null, suit: null,
url: '/cards/1B.svg' url: '/cards/1B.svg',
}, },
{ {
id: '2B', id: '2B',
@@ -17,7 +17,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: null, suit: null,
url: '/cards/2B.svg' url: '/cards/2B.svg',
}, },
{ {
id: '1J', id: '1J',
@@ -26,7 +26,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: true, joker: true,
suit: null, suit: null,
url: '/cards/1J.svg' url: '/cards/1J.svg',
}, },
{ {
id: '2J', id: '2J',
@@ -35,7 +35,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: true, joker: true,
suit: null, suit: null,
url: '/cards/2J.svg' url: '/cards/2J.svg',
}, },
{ {
id: 'AC', id: 'AC',
@@ -44,7 +44,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Clubs', suit: 'Clubs',
url: '/cards/AC.svg' url: '/cards/AC.svg',
}, },
{ {
id: 'AD', id: 'AD',
@@ -53,7 +53,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Diamonds', suit: 'Diamonds',
url: '/cards/AD.svg' url: '/cards/AD.svg',
}, },
{ {
id: 'AH', id: 'AH',
@@ -62,7 +62,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Hearts', suit: 'Hearts',
url: '/cards/AH.svg' url: '/cards/AH.svg',
}, },
{ {
id: 'AS', id: 'AS',
@@ -71,7 +71,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Spades', suit: 'Spades',
url: '/cards/AS.svg' url: '/cards/AS.svg',
}, },
{ {
id: '2C', id: '2C',
@@ -80,7 +80,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Clubs', suit: 'Clubs',
url: '/cards/2C.svg' url: '/cards/2C.svg',
}, },
{ {
id: '2D', id: '2D',
@@ -89,7 +89,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Diamonds', suit: 'Diamonds',
url: '/cards/2D.svg' url: '/cards/2D.svg',
}, },
{ {
id: '2H', id: '2H',
@@ -98,7 +98,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Hearts', suit: 'Hearts',
url: '/cards/2H.svg' url: '/cards/2H.svg',
}, },
{ {
id: '2S', id: '2S',
@@ -107,7 +107,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Spades', suit: 'Spades',
url: '/cards/2S.svg' url: '/cards/2S.svg',
}, },
{ {
id: '3C', id: '3C',
@@ -116,7 +116,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Clubs', suit: 'Clubs',
url: '/cards/3C.svg' url: '/cards/3C.svg',
}, },
{ {
id: '3D', id: '3D',
@@ -125,7 +125,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Diamonds', suit: 'Diamonds',
url: '/cards/3D.svg' url: '/cards/3D.svg',
}, },
{ {
id: '3H', id: '3H',
@@ -134,7 +134,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Hearts', suit: 'Hearts',
url: '/cards/3H.svg' url: '/cards/3H.svg',
}, },
{ {
id: '3S', id: '3S',
@@ -143,7 +143,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Spades', suit: 'Spades',
url: '/cards/3S.svg' url: '/cards/3S.svg',
}, },
{ {
id: '4C', id: '4C',
@@ -152,7 +152,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Clubs', suit: 'Clubs',
url: '/cards/4C.svg' url: '/cards/4C.svg',
}, },
{ {
id: '4D', id: '4D',
@@ -161,7 +161,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Diamonds', suit: 'Diamonds',
url: '/cards/4D.svg' url: '/cards/4D.svg',
}, },
{ {
id: '4H', id: '4H',
@@ -170,7 +170,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Hearts', suit: 'Hearts',
url: '/cards/4H.svg' url: '/cards/4H.svg',
}, },
{ {
id: '4S', id: '4S',
@@ -179,7 +179,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Spades', suit: 'Spades',
url: '/cards/4S.svg' url: '/cards/4S.svg',
}, },
{ {
id: '5C', id: '5C',
@@ -188,7 +188,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Clubs', suit: 'Clubs',
url: '/cards/5C.svg' url: '/cards/5C.svg',
}, },
{ {
id: '5D', id: '5D',
@@ -197,7 +197,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Diamonds', suit: 'Diamonds',
url: '/cards/5D.svg' url: '/cards/5D.svg',
}, },
{ {
id: '5H', id: '5H',
@@ -206,7 +206,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Hearts', suit: 'Hearts',
url: '/cards/5H.svg' url: '/cards/5H.svg',
}, },
{ {
id: '5S', id: '5S',
@@ -215,7 +215,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Spades', suit: 'Spades',
url: '/cards/5S.svg' url: '/cards/5S.svg',
}, },
{ {
id: '6C', id: '6C',
@@ -224,7 +224,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Clubs', suit: 'Clubs',
url: '/cards/6C.svg' url: '/cards/6C.svg',
}, },
{ {
id: '6D', id: '6D',
@@ -233,7 +233,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Diamonds', suit: 'Diamonds',
url: '/cards/6D.svg' url: '/cards/6D.svg',
}, },
{ {
id: '6H', id: '6H',
@@ -242,7 +242,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Hearts', suit: 'Hearts',
url: '/cards/6H.svg' url: '/cards/6H.svg',
}, },
{ {
id: '6S', id: '6S',
@@ -251,7 +251,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Spades', suit: 'Spades',
url: '/cards/6S.svg' url: '/cards/6S.svg',
}, },
{ {
id: '7C', id: '7C',
@@ -260,7 +260,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Clubs', suit: 'Clubs',
url: '/cards/7C.svg' url: '/cards/7C.svg',
}, },
{ {
id: '7D', id: '7D',
@@ -269,7 +269,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Diamonds', suit: 'Diamonds',
url: '/cards/7D.svg' url: '/cards/7D.svg',
}, },
{ {
id: '7H', id: '7H',
@@ -278,7 +278,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Hearts', suit: 'Hearts',
url: '/cards/7H.svg' url: '/cards/7H.svg',
}, },
{ {
id: '7S', id: '7S',
@@ -287,7 +287,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Spades', suit: 'Spades',
url: '/cards/7S.svg' url: '/cards/7S.svg',
}, },
{ {
id: '8C', id: '8C',
@@ -296,7 +296,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Clubs', suit: 'Clubs',
url: '/cards/8C.svg' url: '/cards/8C.svg',
}, },
{ {
id: '8D', id: '8D',
@@ -305,7 +305,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Diamonds', suit: 'Diamonds',
url: '/cards/8D.svg' url: '/cards/8D.svg',
}, },
{ {
id: '8H', id: '8H',
@@ -314,7 +314,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Hearts', suit: 'Hearts',
url: '/cards/8H.svg' url: '/cards/8H.svg',
}, },
{ {
id: '8S', id: '8S',
@@ -323,7 +323,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Spades', suit: 'Spades',
url: '/cards/8S.svg' url: '/cards/8S.svg',
}, },
{ {
id: '9C', id: '9C',
@@ -332,7 +332,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Clubs', suit: 'Clubs',
url: '/cards/9C.svg' url: '/cards/9C.svg',
}, },
{ {
id: '9D', id: '9D',
@@ -341,7 +341,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Diamonds', suit: 'Diamonds',
url: '/cards/9D.svg' url: '/cards/9D.svg',
}, },
{ {
id: '9H', id: '9H',
@@ -350,7 +350,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Hearts', suit: 'Hearts',
url: '/cards/9H.svg' url: '/cards/9H.svg',
}, },
{ {
id: '9S', id: '9S',
@@ -359,7 +359,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Spades', suit: 'Spades',
url: '/cards/9S.svg' url: '/cards/9S.svg',
}, },
{ {
id: '10C', id: '10C',
@@ -368,7 +368,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Clubs', suit: 'Clubs',
url: '/cards/10C.svg' url: '/cards/10C.svg',
}, },
{ {
id: '10D', id: '10D',
@@ -377,7 +377,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Diamonds', suit: 'Diamonds',
url: '/cards/10D.svg' url: '/cards/10D.svg',
}, },
{ {
id: '10H', id: '10H',
@@ -386,7 +386,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Hearts', suit: 'Hearts',
url: '/cards/10H.svg' url: '/cards/10H.svg',
}, },
{ {
id: '10S', id: '10S',
@@ -395,7 +395,7 @@ const cards: StandardCard[] = [
face: false, face: false,
joker: false, joker: false,
suit: 'Spades', suit: 'Spades',
url: '/cards/10S.svg' url: '/cards/10S.svg',
}, },
{ {
id: 'JC', id: 'JC',
@@ -404,7 +404,7 @@ const cards: StandardCard[] = [
face: true, face: true,
joker: false, joker: false,
suit: 'Clubs', suit: 'Clubs',
url: '/cards/JC.svg' url: '/cards/JC.svg',
}, },
{ {
id: 'JD', id: 'JD',
@@ -413,7 +413,7 @@ const cards: StandardCard[] = [
face: true, face: true,
joker: false, joker: false,
suit: 'Diamonds', suit: 'Diamonds',
url: '/cards/JD.svg' url: '/cards/JD.svg',
}, },
{ {
id: 'JH', id: 'JH',
@@ -422,7 +422,7 @@ const cards: StandardCard[] = [
face: true, face: true,
joker: false, joker: false,
suit: 'Hearts', suit: 'Hearts',
url: '/cards/JH.svg' url: '/cards/JH.svg',
}, },
{ {
id: 'JS', id: 'JS',
@@ -431,7 +431,7 @@ const cards: StandardCard[] = [
face: true, face: true,
joker: false, joker: false,
suit: 'Spades', suit: 'Spades',
url: '/cards/JS.svg' url: '/cards/JS.svg',
}, },
{ {
id: 'KC', id: 'KC',
@@ -440,7 +440,7 @@ const cards: StandardCard[] = [
face: true, face: true,
joker: false, joker: false,
suit: 'Clubs', suit: 'Clubs',
url: '/cards/KC.svg' url: '/cards/KC.svg',
}, },
{ {
id: 'KD', id: 'KD',
@@ -449,7 +449,7 @@ const cards: StandardCard[] = [
face: true, face: true,
joker: false, joker: false,
suit: 'Diamonds', suit: 'Diamonds',
url: '/cards/KD.svg' url: '/cards/KD.svg',
}, },
{ {
id: 'KH', id: 'KH',
@@ -458,7 +458,7 @@ const cards: StandardCard[] = [
face: true, face: true,
joker: false, joker: false,
suit: 'Hearts', suit: 'Hearts',
url: '/cards/KH.svg' url: '/cards/KH.svg',
}, },
{ {
id: 'KS', id: 'KS',
@@ -467,7 +467,7 @@ const cards: StandardCard[] = [
face: true, face: true,
joker: false, joker: false,
suit: 'Spades', suit: 'Spades',
url: '/cards/KS.svg' url: '/cards/KS.svg',
}, },
{ {
id: 'QC', id: 'QC',
@@ -476,7 +476,7 @@ const cards: StandardCard[] = [
face: true, face: true,
joker: false, joker: false,
suit: 'Clubs', suit: 'Clubs',
url: '/cards/QC.svg' url: '/cards/QC.svg',
}, },
{ {
id: 'QD', id: 'QD',
@@ -485,7 +485,7 @@ const cards: StandardCard[] = [
face: true, face: true,
joker: false, joker: false,
suit: 'Diamonds', suit: 'Diamonds',
url: '/cards/QD.svg' url: '/cards/QD.svg',
}, },
{ {
id: 'QH', id: 'QH',
@@ -494,7 +494,7 @@ const cards: StandardCard[] = [
face: true, face: true,
joker: false, joker: false,
suit: 'Hearts', suit: 'Hearts',
url: '/cards/QH.svg' url: '/cards/QH.svg',
}, },
{ {
id: 'QS', id: 'QS',
@@ -503,8 +503,8 @@ const cards: StandardCard[] = [
face: true, face: true,
joker: false, joker: false,
suit: 'Spades', suit: 'Spades',
url: '/cards/QS.svg' url: '/cards/QS.svg',
} },
]; ];
export default cards; export default cards;

View File

@@ -25,7 +25,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Coins_02_Philanthropist', id: 'Coins_02_Philanthropist',
name: 'Philanthropist', name: 'Philanthropist',
card: 'Two of Coins', card: 'Two of Coins',
description: 'Charity and giving on a grand scale; those who use wealth to fight evil and sickness', description:
'Charity and giving on a grand scale; those who use wealth to fight evil and sickness',
aria: 'Coins 02 Philanthropist', aria: 'Coins 02 Philanthropist',
back: false, back: false,
suit: 'Coins', suit: 'Coins',
@@ -45,7 +46,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Coins_04_Merchant', id: 'Coins_04_Merchant',
name: 'Merchant', name: 'Merchant',
card: 'Four of Coins', card: 'Four of Coins',
description: 'A rare commodity or business opportunity; deceitful or dangerous business transactions', description:
'A rare commodity or business opportunity; deceitful or dangerous business transactions',
aria: 'Coins 04 Merchant', aria: 'Coins 04 Merchant',
back: false, back: false,
suit: 'Coins', suit: 'Coins',
@@ -55,7 +57,7 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Coins_05_GuildMember', id: 'Coins_05_GuildMember',
name: 'Guild Member', name: 'Guild Member',
card: 'Five of Coins', card: 'Five of Coins',
description: 'Like-minded individuals joined together in a common goal; pride in one\'s work', description: "Like-minded individuals joined together in a common goal; pride in one's work",
aria: 'Coins 05 Guild Member', aria: 'Coins 05 Guild Member',
back: false, back: false,
suit: 'Coins', suit: 'Coins',
@@ -75,7 +77,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Coins_07_Thief', id: 'Coins_07_Thief',
name: 'Thief', name: 'Thief',
card: 'Seven of Coins', card: 'Seven of Coins',
description: 'Those who steal or burgle; a loss of property, beauty, innocence, friendship, or reputation', description:
'Those who steal or burgle; a loss of property, beauty, innocence, friendship, or reputation',
aria: 'Coins 07 Thief', aria: 'Coins 07 Thief',
back: false, back: false,
suit: 'Coins', suit: 'Coins',
@@ -95,7 +98,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Coins_09_Miser', id: 'Coins_09_Miser',
name: 'Miser', name: 'Miser',
card: 'Nine of Coins', card: 'Nine of Coins',
description: 'Hoarded wealth; those who are irreversibly unhappy or who think money is meaningless', description:
'Hoarded wealth; those who are irreversibly unhappy or who think money is meaningless',
aria: 'Coins 09 Miser', aria: 'Coins 09 Miser',
back: false, back: false,
suit: 'Coins', suit: 'Coins',
@@ -105,7 +109,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Coins_10_Rogue', id: 'Coins_10_Rogue',
name: 'Rogue', name: 'Rogue',
card: 'Master of Coins', card: 'Master of Coins',
description: 'Anyone for whom money is important; those who believe money is the key to their success', description:
'Anyone for whom money is important; those who believe money is the key to their success',
aria: 'Coins 10 Rogue', aria: 'Coins 10 Rogue',
back: false, back: false,
suit: 'Coins', suit: 'Coins',
@@ -115,7 +120,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Glyphs_01_Monk', id: 'Glyphs_01_Monk',
name: 'Monk', name: 'Monk',
card: 'One of Glyphs', card: 'One of Glyphs',
description: 'Serenity; inner strength and self-reliance; supreme confidence bereft of arrogance', description:
'Serenity; inner strength and self-reliance; supreme confidence bereft of arrogance',
aria: 'Glyphs 01 Monk', aria: 'Glyphs 01 Monk',
back: false, back: false,
suit: 'Glyphs', suit: 'Glyphs',
@@ -125,7 +131,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Glyphs_02_Missionary', id: 'Glyphs_02_Missionary',
name: 'Missionary', name: 'Missionary',
card: 'Two of Glyphs', card: 'Two of Glyphs',
description: 'Those who spread wisdom and faith to others; warnings of the spread of fear and ignorance', description:
'Those who spread wisdom and faith to others; warnings of the spread of fear and ignorance',
aria: 'Glyphs 02 Missionary', aria: 'Glyphs 02 Missionary',
back: false, back: false,
suit: 'Glyphs', suit: 'Glyphs',
@@ -135,7 +142,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Glyphs_03_Healer', id: 'Glyphs_03_Healer',
name: 'Healer', name: 'Healer',
card: 'Three of Glyphs', card: 'Three of Glyphs',
description: 'Healing; a contagious illness, disease, or curse; those who practice the healing arts', description:
'Healing; a contagious illness, disease, or curse; those who practice the healing arts',
aria: 'Glyphs 03 Healer', aria: 'Glyphs 03 Healer',
back: false, back: false,
suit: 'Glyphs', suit: 'Glyphs',
@@ -145,7 +153,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Glyphs_04_Shepherd', id: 'Glyphs_04_Shepherd',
name: 'Shepherd', name: 'Shepherd',
card: 'Four of Glyphs', card: 'Four of Glyphs',
description: 'Those who protect others; one who bears a burden far too great to be shouldered alone', description:
'Those who protect others; one who bears a burden far too great to be shouldered alone',
aria: 'Glyphs 04 Shepherd', aria: 'Glyphs 04 Shepherd',
back: false, back: false,
suit: 'Glyphs', suit: 'Glyphs',
@@ -155,7 +164,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Glyphs_05_Druid', id: 'Glyphs_05_Druid',
name: 'Druid', name: 'Druid',
card: 'Five of Glyphs', card: 'Five of Glyphs',
description: 'The ambivalence and cruelty of nature and those who feel drawn to it; inner turmoil', description:
'The ambivalence and cruelty of nature and those who feel drawn to it; inner turmoil',
aria: 'Glyphs 05 Druid', aria: 'Glyphs 05 Druid',
back: false, back: false,
suit: 'Glyphs', suit: 'Glyphs',
@@ -215,7 +225,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Stars_01_Transmuter', id: 'Stars_01_Transmuter',
name: 'Transmuter', name: 'Transmuter',
card: 'One of Stars', card: 'One of Stars',
description: 'A new discovery; the coming of unexpected things; unforeseen consequences and chaos', description:
'A new discovery; the coming of unexpected things; unforeseen consequences and chaos',
aria: 'Stars 01 Transmuter', aria: 'Stars 01 Transmuter',
back: false, back: false,
suit: 'Stars', suit: 'Stars',
@@ -225,7 +236,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Stars_02_Diviner', id: 'Stars_02_Diviner',
name: 'Diviner', name: 'Diviner',
card: 'Two of Stars', card: 'Two of Stars',
description: 'The pursuit of knowledge tempered by wisdom; truth and honesty; sages and prophecy', description:
'The pursuit of knowledge tempered by wisdom; truth and honesty; sages and prophecy',
aria: 'Stars 02 Diviner', aria: 'Stars 02 Diviner',
back: false, back: false,
suit: 'Stars', suit: 'Stars',
@@ -245,7 +257,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Stars_04_Abjurer', id: 'Stars_04_Abjurer',
name: 'Abjurer', name: 'Abjurer',
card: 'Four of Stars', card: 'Four of Stars',
description: 'Those guided by logic and reasoning; warns of an overlooked clue or piece of information', description:
'Those guided by logic and reasoning; warns of an overlooked clue or piece of information',
aria: 'Stars 04 Abjurer', aria: 'Stars 04 Abjurer',
back: false, back: false,
suit: 'Stars', suit: 'Stars',
@@ -255,7 +268,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Stars_05_Elementalist', id: 'Stars_05_Elementalist',
name: 'Elementalist', name: 'Elementalist',
card: 'Five of Stars', card: 'Five of Stars',
description: 'The triumph of nature over civilization; natural disasters and bountiful harvests', description:
'The triumph of nature over civilization; natural disasters and bountiful harvests',
aria: 'Stars 05 Elementalist', aria: 'Stars 05 Elementalist',
back: false, back: false,
suit: 'Stars', suit: 'Stars',
@@ -265,7 +279,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Stars_06_Evoker', id: 'Stars_06_Evoker',
name: 'Evoker', name: 'Evoker',
card: 'Six of Stars', card: 'Six of Stars',
description: 'Magical or supernatural power that can\'t be controlled; magic for destructive ends', description:
"Magical or supernatural power that can't be controlled; magic for destructive ends",
aria: 'Stars 06 Evoker', aria: 'Stars 06 Evoker',
back: false, back: false,
suit: 'Stars', suit: 'Stars',
@@ -275,7 +290,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Stars_07_Illusionist', id: 'Stars_07_Illusionist',
name: 'Illusionist', name: 'Illusionist',
card: 'Seven of Stars', card: 'Seven of Stars',
description: 'Lies and deceit; grand conspiracies; secret societies; the presence of a dupe or a saboteur', description:
'Lies and deceit; grand conspiracies; secret societies; the presence of a dupe or a saboteur',
aria: 'Stars 07 Illusionist', aria: 'Stars 07 Illusionist',
back: false, back: false,
suit: 'Stars', suit: 'Stars',
@@ -295,7 +311,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Stars_09_Conjurer', id: 'Stars_09_Conjurer',
name: 'Conjurer', name: 'Conjurer',
card: 'Nine of Stars', card: 'Nine of Stars',
description: 'The coming of an unexpected supernatural threat; those who think of themselves as gods', description:
'The coming of an unexpected supernatural threat; those who think of themselves as gods',
aria: 'Stars 09 Conjurer', aria: 'Stars 09 Conjurer',
back: false, back: false,
suit: 'Stars', suit: 'Stars',
@@ -305,7 +322,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Stars_10_Wizard', id: 'Stars_10_Wizard',
name: 'Wizard', name: 'Wizard',
card: 'Master of Stars', card: 'Master of Stars',
description: 'Mystery and riddles; the unknown; those who crave magical power and great knowledge', description:
'Mystery and riddles; the unknown; those who crave magical power and great knowledge',
aria: 'Stars 10 Wizard', aria: 'Stars 10 Wizard',
back: false, back: false,
suit: 'Stars', suit: 'Stars',
@@ -315,7 +333,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Swords_01_Avenger', id: 'Swords_01_Avenger',
name: 'Avenger', name: 'Avenger',
card: 'One of Swords', card: 'One of Swords',
description: 'Justice and revenge for great wrongs; those on a quest to rid the world of great evil', description:
'Justice and revenge for great wrongs; those on a quest to rid the world of great evil',
aria: 'Swords 01 Avenger', aria: 'Swords 01 Avenger',
back: false, back: false,
suit: 'Swords', suit: 'Swords',
@@ -355,7 +374,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Swords_05_Myrmidon', id: 'Swords_05_Myrmidon',
name: 'Myrmidon', name: 'Myrmidon',
card: 'Five of Swords', card: 'Five of Swords',
description: 'Great heroes; a sudden reversal of fate; the triumph of the underdog over a mighty enemy', description:
'Great heroes; a sudden reversal of fate; the triumph of the underdog over a mighty enemy',
aria: 'Swords 05 Myrmidon', aria: 'Swords 05 Myrmidon',
back: false, back: false,
suit: 'Swords', suit: 'Swords',
@@ -385,7 +405,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Swords_08_Dictator', id: 'Swords_08_Dictator',
name: 'Dictator', name: 'Dictator',
card: 'Eight of Swords', card: 'Eight of Swords',
description: 'All that is wrong with government and leadership; those who rule through fear and violence', description:
'All that is wrong with government and leadership; those who rule through fear and violence',
aria: 'Swords 08 Dictator', aria: 'Swords 08 Dictator',
back: false, back: false,
suit: 'Swords', suit: 'Swords',
@@ -395,7 +416,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Swords_09_Torturer', id: 'Swords_09_Torturer',
name: 'Torturer', name: 'Torturer',
card: 'Nine of Swords', card: 'Nine of Swords',
description: 'The coming of suffering or merciless cruelty; one who is irredeemably evil or sadistic', description:
'The coming of suffering or merciless cruelty; one who is irredeemably evil or sadistic',
aria: 'Swords 09 Torturer', aria: 'Swords 09 Torturer',
back: false, back: false,
suit: 'Swords', suit: 'Swords',
@@ -405,7 +427,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Swords_10_Warrior', id: 'Swords_10_Warrior',
name: 'Warrior', name: 'Warrior',
card: 'Master of Swords', card: 'Master of Swords',
description: 'Strength and force personified; violence; those who use force to accomplish their goals', description:
'Strength and force personified; violence; those who use force to accomplish their goals',
aria: 'Swords 10 Warrior', aria: 'Swords 10 Warrior',
back: false, back: false,
suit: 'Swords', suit: 'Swords',
@@ -415,7 +438,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Crowns_Artifact', id: 'Crowns_Artifact',
name: 'Artifact', name: 'Artifact',
card: 'The Artifact', card: 'The Artifact',
description: 'The importance of some physical object that must be obtained, protected, or destroyed at all costs', description:
'The importance of some physical object that must be obtained, protected, or destroyed at all costs',
aria: 'High Deck Artifact', aria: 'High Deck Artifact',
back: false, back: false,
suit: 'High Deck', suit: 'High Deck',
@@ -425,7 +449,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Crowns_Beast', id: 'Crowns_Beast',
name: 'Beast', name: 'Beast',
card: 'The Beast', card: 'The Beast',
description: 'Great rage or passion; something bestial or malevolent hiding in plain sight or lurking just below the surface', description:
'Great rage or passion; something bestial or malevolent hiding in plain sight or lurking just below the surface',
aria: 'High Deck Beast', aria: 'High Deck Beast',
back: false, back: false,
suit: 'High Deck', suit: 'High Deck',
@@ -435,7 +460,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Crowns_BrokenOne', id: 'Crowns_BrokenOne',
name: 'Broken One', name: 'Broken One',
card: 'The Broken One', card: 'The Broken One',
description: 'Defeat, failure, and despair; the loss of something or someone important, without which one feels incomplete', description:
'Defeat, failure, and despair; the loss of something or someone important, without which one feels incomplete',
aria: 'High Deck Broken One', aria: 'High Deck Broken One',
back: false, back: false,
suit: 'High Deck', suit: 'High Deck',
@@ -445,7 +471,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Crowns_Darklord', id: 'Crowns_Darklord',
name: 'Darklord', name: 'Darklord',
card: 'The Darklord', card: 'The Darklord',
description: 'A single, powerful individual of an evil nature, one whose goals have enormous and far-reaching consequences', description:
'A single, powerful individual of an evil nature, one whose goals have enormous and far-reaching consequences',
aria: 'High Deck Darklord', aria: 'High Deck Darklord',
back: false, back: false,
suit: 'High Deck', suit: 'High Deck',
@@ -455,7 +482,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Crowns_Donjon', id: 'Crowns_Donjon',
name: 'Donjon', name: 'Donjon',
card: 'The Donjon', card: 'The Donjon',
description: 'Isolation and imprisonment; being so conservative in thinking as to be a prisoner of one\'s own beliefs', description:
"Isolation and imprisonment; being so conservative in thinking as to be a prisoner of one's own beliefs",
aria: 'High Deck Donjon', aria: 'High Deck Donjon',
back: false, back: false,
suit: 'High Deck', suit: 'High Deck',
@@ -465,7 +493,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Crowns_Executioner', id: 'Crowns_Executioner',
name: 'Executioner', name: 'Executioner',
card: 'The Executioner', card: 'The Executioner',
description: 'The imminent death of one rightly or wrongly convicted of a crime; false accusations and unjust prosecution', description:
'The imminent death of one rightly or wrongly convicted of a crime; false accusations and unjust prosecution',
aria: 'High Deck Executioner', aria: 'High Deck Executioner',
back: false, back: false,
suit: 'High Deck', suit: 'High Deck',
@@ -475,7 +504,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Crowns_Ghost', id: 'Crowns_Ghost',
name: 'Ghost', name: 'Ghost',
card: 'The Ghost', card: 'The Ghost',
description: 'The looming past; the return of an old enemy or the discovery of a secret buried long ago', description:
'The looming past; the return of an old enemy or the discovery of a secret buried long ago',
aria: 'High Deck Ghost', aria: 'High Deck Ghost',
back: false, back: false,
suit: 'High Deck', suit: 'High Deck',
@@ -485,7 +515,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Crowns_Horseman', id: 'Crowns_Horseman',
name: 'Horseman', name: 'Horseman',
card: 'The Horseman', card: 'The Horseman',
description: 'Death; disaster in the form of the loss of wealth or property, a horrible defeat, or the end of a bloodline', description:
'Death; disaster in the form of the loss of wealth or property, a horrible defeat, or the end of a bloodline',
aria: 'High Deck Horseman', aria: 'High Deck Horseman',
back: false, back: false,
suit: 'High Deck', suit: 'High Deck',
@@ -495,7 +526,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Crowns_Innoncent', id: 'Crowns_Innoncent',
name: 'Innocent', name: 'Innocent',
card: 'The Innoncent', card: 'The Innoncent',
description: 'A being of great importance whose life is in danger (who might be helpless or simply unaware of the peril)', description:
'A being of great importance whose life is in danger (who might be helpless or simply unaware of the peril)',
aria: 'High Deck Innocent', aria: 'High Deck Innocent',
back: false, back: false,
suit: 'High Deck', suit: 'High Deck',
@@ -505,7 +537,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Crowns_Marionette', id: 'Crowns_Marionette',
name: 'Marionette', name: 'Marionette',
card: 'The Marionette', card: 'The Marionette',
description: 'The presence of a spy or a minion of some greater power; an encounter with a puppet or an underling', description:
'The presence of a spy or a minion of some greater power; an encounter with a puppet or an underling',
aria: 'High Deck Marionette', aria: 'High Deck Marionette',
back: false, back: false,
suit: 'High Deck', suit: 'High Deck',
@@ -515,7 +548,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Crowns_Mists', id: 'Crowns_Mists',
name: 'Mists', name: 'Mists',
card: 'The Mists', card: 'The Mists',
description: 'Something unexpected or mysterious that can\'t be avoided; a great quest or journey that will try one\'s spirit', description:
"Something unexpected or mysterious that can't be avoided; a great quest or journey that will try one's spirit",
aria: 'High Deck Mists', aria: 'High Deck Mists',
back: false, back: false,
suit: 'High Deck', suit: 'High Deck',
@@ -525,7 +559,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Crowns_Raven', id: 'Crowns_Raven',
name: 'Raven', name: 'Raven',
card: 'The Raven', card: 'The Raven',
description: 'A hidden source of information; a fortunate turn of events; a secret potential for good', description:
'A hidden source of information; a fortunate turn of events; a secret potential for good',
aria: 'High Deck Raven', aria: 'High Deck Raven',
back: false, back: false,
suit: 'High Deck', suit: 'High Deck',
@@ -535,7 +570,8 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Crowns_Seer', id: 'Crowns_Seer',
name: 'Seer', name: 'Seer',
card: 'The Seer', card: 'The Seer',
description: 'Inspiration and keen intellect; a future event, the outcome of which will hinge on a clever mind', description:
'Inspiration and keen intellect; a future event, the outcome of which will hinge on a clever mind',
aria: 'High Deck Seer', aria: 'High Deck Seer',
back: false, back: false,
suit: 'High Deck', suit: 'High Deck',
@@ -545,13 +581,13 @@ const tarokkaCards: TarokkaCard[] = [
id: 'Crowns_Tempter', id: 'Crowns_Tempter',
name: 'Tempter', name: 'Tempter',
card: 'The Tempter', card: 'The Tempter',
description: 'One who has been compromised or led astray by temptation or foolishness; one who tempts others for evil ends', description:
'One who has been compromised or led astray by temptation or foolishness; one who tempts others for evil ends',
aria: 'High Deck Tempter', aria: 'High Deck Tempter',
back: false, back: false,
suit: 'High Deck', suit: 'High Deck',
url: '/img/tarokka/Crowns_Tempter.png', url: '/img/tarokka/Crowns_Tempter.png',
} },
]; ];
export default tarokkaCards; export default tarokkaCards;

View File

@@ -1,6 +1,6 @@
import Deck from './TarokkaDeck' import Deck from './TarokkaDeck';
import { GameState, GameUpdate, TarokkaGameCard } from '../types' import { GameState, GameUpdate, TarokkaGameCard } from '../types';
const deck = new Deck(); const deck = new Deck();
@@ -17,7 +17,7 @@ export default class GameStore {
const newGame: GameState = { const newGame: GameState = {
id, id,
players: new Set(), players: new Set(),
cards: deck.select(5).map(card => ({ ...card, flipped: false })), cards: deck.select(5).map((card) => ({ ...card, flipped: false })),
lastUpdated: Date.now(), lastUpdated: Date.now(),
}; };
@@ -66,7 +66,12 @@ export default class GameStore {
gameUpdate(game: GameState): GameUpdate { gameUpdate(game: GameState): GameUpdate {
const { id, cards } = game; const { id, cards } = game;
return { id, cards: (cards as TarokkaGameCard[]).map((card: TarokkaGameCard) => card.flipped ? card : { ...deck.getBack(), flipped: false }) }; return {
id,
cards: (cards as TarokkaGameCard[]).map((card: TarokkaGameCard) =>
card.flipped ? card : { ...deck.getBack(), flipped: false },
),
};
} }
deleteGame(gameID: string): void { deleteGame(gameID: string): void {

View File

@@ -27,9 +27,9 @@ export default class Cards {
constructor(options: OptionProps = {}) { constructor(options: OptionProps = {}) {
this.options = { ...DEFAULT_OPTIONS, ...options }; this.options = { ...DEFAULT_OPTIONS, ...options };
this.deck = cards.filter(card => !card.back && (this.options.jokers || !card.joker)); this.deck = cards.filter((card) => !card.back && (this.options.jokers || !card.joker));
this.backs = cards.filter(card => card.back); this.backs = cards.filter((card) => card.back);
this.jokers = cards.filter(card => card.joker); this.jokers = cards.filter((card) => card.joker);
} }
select(count: number): StandardCard[] { select(count: number): StandardCard[] {
@@ -38,7 +38,7 @@ export default class Cards {
getBack(style: number): StandardCard { getBack(style: number): StandardCard {
style = style || this.options.back; style = style || this.options.back;
return this.backs.find(card => card.id.startsWith(String(style))) || this.backs[0]; return this.backs.find((card) => card.id.startsWith(String(style))) || this.backs[0];
} }
getJokers(): StandardCard[] { getJokers(): StandardCard[] {

View File

@@ -7,8 +7,8 @@ export default class TarokkaDeck {
private backs: TarokkaCard[] = []; private backs: TarokkaCard[] = [];
constructor() { constructor() {
this.deck = cards.filter(card => !card.back); this.deck = cards.filter((card) => !card.back);
this.backs = cards.filter(card => card.back); this.backs = cards.filter((card) => card.back);
} }
select(count: number): TarokkaCard[] { select(count: number): TarokkaCard[] {

50
package-lock.json generated
View File

@@ -1,5 +1,5 @@
{ {
"name": "custom-server-app", "name": "tarokka",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
@@ -14,10 +14,11 @@
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/postcss": "^4", "@tailwindcss/postcss": "^4",
"@types/node": "^22.14.0", "@types/node": "^22.14.1",
"@types/react": "^19.1.0", "@types/react": "^19.1.1",
"@types/react-dom": "^19.1.2", "@types/react-dom": "^19.1.2",
"nodemon": "^3.1.9", "nodemon": "^3.1.9",
"prettier": "^3.5.3",
"tailwindcss": "^4", "tailwindcss": "^4",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"typescript": "^5.8.3" "typescript": "^5.8.3"
@@ -822,18 +823,20 @@
} }
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "22.14.0", "version": "22.14.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.0.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz",
"integrity": "sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==", "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==",
"license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~6.21.0" "undici-types": "~6.21.0"
} }
}, },
"node_modules/@types/react": { "node_modules/@types/react": {
"version": "19.1.0", "version": "19.1.1",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.0.tgz", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.1.tgz",
"integrity": "sha512-UaicktuQI+9UKyA4njtDOGBD/67t8YEBt2xdfqu8+gP9hqPUPsiXlNPcpS2gVdjmis5GKPG3fCxbQLVgxsQZ8w==", "integrity": "sha512-ePapxDL7qrgqSF67s0h9m412d9DbXyC1n59O2st+9rjuuamWsZuD2w55rqY12CbzsZ7uVXb5Nw0gEp9Z8MMutQ==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
@@ -843,6 +846,7 @@
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.2.tgz", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.2.tgz",
"integrity": "sha512-XGJkWF41Qq305SKWEILa1O8vzhb3aOo3ogBlSmiqNko/WmRb6QIaweuZCXjKygVDXpzXb5wyxKTSOsmkuqj+Qw==", "integrity": "sha512-XGJkWF41Qq305SKWEILa1O8vzhb3aOo3ogBlSmiqNko/WmRb6QIaweuZCXjKygVDXpzXb5wyxKTSOsmkuqj+Qw==",
"dev": true, "dev": true,
"license": "MIT",
"peerDependencies": { "peerDependencies": {
"@types/react": "^19.0.0" "@types/react": "^19.0.0"
} }
@@ -1123,6 +1127,7 @@
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"ms": "^2.1.3" "ms": "^2.1.3"
}, },
@@ -1738,6 +1743,7 @@
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz",
"integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==", "integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"chokidar": "^3.5.2", "chokidar": "^3.5.2",
"debug": "^4", "debug": "^4",
@@ -1831,6 +1837,22 @@
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
} }
}, },
"node_modules/prettier": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
"dev": true,
"license": "MIT",
"bin": {
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/pstree.remy": { "node_modules/pstree.remy": {
"version": "1.1.8", "version": "1.1.8",
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
@@ -1841,6 +1863,7 @@
"version": "19.1.0", "version": "19.1.0",
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
"license": "MIT",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@@ -1849,6 +1872,7 @@
"version": "19.1.0", "version": "19.1.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
"license": "MIT",
"dependencies": { "dependencies": {
"scheduler": "^0.26.0" "scheduler": "^0.26.0"
}, },
@@ -1871,7 +1895,8 @@
"node_modules/scheduler": { "node_modules/scheduler": {
"version": "0.26.0", "version": "0.26.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
"integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==" "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
"license": "MIT"
}, },
"node_modules/semver": { "node_modules/semver": {
"version": "7.7.1", "version": "7.7.1",
@@ -1957,6 +1982,7 @@
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
"integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"semver": "^7.5.3" "semver": "^7.5.3"
}, },
@@ -2219,6 +2245,7 @@
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"dev": true, "dev": true,
"license": "Apache-2.0",
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"
@@ -2236,7 +2263,8 @@
"node_modules/undici-types": { "node_modules/undici-types": {
"version": "6.21.0", "version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"license": "MIT"
}, },
"node_modules/v8-compile-cache-lib": { "node_modules/v8-compile-cache-lib": {
"version": "3.0.1", "version": "3.0.1",

View File

@@ -8,19 +8,20 @@
"dependencies": { "dependencies": {
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"next": "latest", "next": "latest",
"react": "^18.2.0", "react": "^19.1.0",
"react-dom": "^18.2.0", "react-dom": "^19.1.0",
"socket.io": "^4.8.1", "socket.io": "^4.8.1",
"socket.io-client": "^4.8.1" "socket.io-client": "^4.8.1"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/postcss": "^4", "@tailwindcss/postcss": "^4",
"@types/node": "^18.11.5", "@types/node": "^22.14.1",
"@types/react": "^18.0.23", "@types/react": "^19.1.1",
"@types/react-dom": "^18.0.7", "@types/react-dom": "^19.1.2",
"nodemon": "^2.0.20", "nodemon": "^3.1.9",
"prettier": "^3.5.3",
"tailwindcss": "^4", "tailwindcss": "^4",
"ts-node": "^10.9.1", "ts-node": "^10.9.2",
"typescript": "^4.8.4" "typescript": "^5.8.3"
} }
} }

View File

@@ -6,10 +6,9 @@ import GameStore from './lib/GameStore';
import type { ClientUpdate } from './types'; import type { ClientUpdate } from './types';
const dev = process.env.NODE_ENV !== 'production'; const dev = process.env.NODE_ENV !== 'production';
const hostname = 'localhost'; const hostname = '0.0.0.0';
const port = 3000; const port = 3000;
// when using middleware `hostname` and `port` must be provided
const app = next({ dev, hostname, port }); const app = next({ dev, hostname, port });
const handler = app.getRequestHandler(); const handler = app.getRequestHandler();
@@ -27,10 +26,10 @@ app.prepare().then(() => {
socket.join(gameID); socket.join(gameID);
const gameUpdate = gameStore.joinGame(gameID, socket.id); const gameUpdate = gameStore.joinGame(gameID, socket.id);
console.log(`Socket ${socket.id} joined game ${gameID}`) console.log(`Socket ${socket.id} joined game ${gameID}`);
socket.emit('init', gameUpdate); socket.emit('init', gameUpdate);
}) });
socket.on('flip-card', ({ gameID, cardIndex }: ClientUpdate) => { socket.on('flip-card', ({ gameID, cardIndex }: ClientUpdate) => {
console.log('Card flipped:', { gameID, cardIndex }); console.log('Card flipped:', { gameID, cardIndex });

View File

@@ -1,7 +1,6 @@
import getRandomItems from '@/tools/getRandomItems'; import getRandomItems from '@/tools/getRandomItems';
const alphabet = const alphabet = '0123456789abcdefghijklmnopqrstuvwxyz';
'0123456789abcdefghijklmnopqrstuvwxyz';
const generateID = (length: number = 6) => { const generateID = (length: number = 6) => {
return getRandomItems(alphabet.split(''), length).join(''); return getRandomItems(alphabet.split(''), length).join('');