From 6508d40b2d37ad8702fbbdc6ba1341c0e263167c Mon Sep 17 00:00:00 2001 From: Gavin McDonald Date: Sat, 12 Apr 2025 15:17:02 -0400 Subject: [PATCH] prettier --- .prettierignore | 5 + .prettierrc | 7 + app/[gameID]/page.tsx | 102 ++-- app/b/page.tsx | 3 - app/globals.css | 28 +- app/layout.tsx | 36 +- app/page.tsx | 31 +- components/Card.tsx | 27 +- constants/standardCards.ts | 1008 ++++++++++++++++---------------- constants/tarokkaCards.ts | 1138 +++++++++++++++++++----------------- lib/GameStore.ts | 103 ++-- lib/StandardDeck.ts | 52 +- lib/TarokkaDeck.ts | 24 +- package-lock.json | 50 +- package.json | 17 +- server.ts | 57 +- tools/getRandomItems.ts | 16 +- tools/simpleID.ts | 5 +- types/index.ts | 50 +- 19 files changed, 1415 insertions(+), 1344 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc delete mode 100644 app/b/page.tsx diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..69e7baf --- /dev/null +++ b/.prettierignore @@ -0,0 +1,5 @@ +node_modules +.next +dist +build +coverage diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..ab0d36c --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "singleQuote": true, + "useTabs": true, + "trailingComma": "all", + "printWidth": 100, +} diff --git a/app/[gameID]/page.tsx b/app/[gameID]/page.tsx index 630d40d..e9914cd 100644 --- a/app/[gameID]/page.tsx +++ b/app/[gameID]/page.tsx @@ -1,68 +1,72 @@ -"use client"; +'use client'; -import { useEffect, useState } from "react"; +import { useEffect, useState } from 'react'; import { useParams } from 'next/navigation'; -import { socket } from "@/socket"; +import { socket } from '@/socket'; import Card from '@/components/Card'; import type { GameUpdate, ClientUpdate, StandardGameCard, TarokkaGameCard } from '@/types'; export default function GamePage() { - const { gameID: gameIDParam } = useParams(); + const { gameID: gameIDParam } = useParams(); - const [gameID, setGameID] = useState(''); - const [cards, setCards] = useState([]); + const [gameID, setGameID] = useState(''); + const [cards, setCards] = useState([]); - useEffect(() => { - if (gameIDParam) { - setGameID(Array.isArray(gameIDParam) ? gameIDParam[0] : gameIDParam); - } - }, [gameIDParam]) + useEffect(() => { + if (gameIDParam) { + setGameID(Array.isArray(gameIDParam) ? gameIDParam[0] : gameIDParam); + } + }, [gameIDParam]); - useEffect(() => { - if (gameID) { - socket.emit('join', gameID); + useEffect(() => { + if (gameID) { + socket.emit('join', gameID); - socket.on('init', (data: GameUpdate) => { - console.log('init', data); - setCards(data.cards); - }); + socket.on('init', (data: GameUpdate) => { + console.log('init', data); + setCards(data.cards); + }); - socket.on('card-flipped', (data: GameUpdate) => { - console.log('>>>', data); - setCards(data.cards); - }); - } + socket.on('card-flipped', (data: GameUpdate) => { + console.log('>>>', data); + setCards(data.cards); + }); + } - return gameID ? () => { - socket.off('init'); - socket.off('card-flipped'); - } : undefined; - }, [gameID]); + return gameID + ? () => { + socket.off('init'); + socket.off('card-flipped'); + } + : undefined; + }, [gameID]); - const flipCard = (cardIndex: number) => { - const flip: ClientUpdate = { - gameID, - cardIndex, - }; + const flipCard = (cardIndex: number) => { + const flip: ClientUpdate = { + gameID, + cardIndex, + }; - socket.emit('flip-card', flip); - }; + socket.emit('flip-card', flip); + }; - return cards.length ? ( -
-
- {Array.from({ length: 9 }).map((_, i) => { - const cardIndex = [1, 3, 4, 5, 7].indexOf(i); + return cards.length ? ( +
+
+ {Array.from({ length: 9 }).map((_, i) => { + const cardIndex = [1, 3, 4, 5, 7].indexOf(i); - return ( -
- {cardIndex !== -1 && flipCard(cardIndex)} />} -
- ) - })} -
-
- ) : null; + return ( +
+ {cardIndex !== -1 && ( + flipCard(cardIndex)} /> + )} +
+ ); + })} +
+
+ ) : null; } diff --git a/app/b/page.tsx b/app/b/page.tsx deleted file mode 100644 index bd162b8..0000000 --- a/app/b/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function B() { - return
b
; -} diff --git a/app/globals.css b/app/globals.css index a2dc41e..512ba52 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,26 +1,26 @@ -@import "tailwindcss"; +@import 'tailwindcss'; :root { - --background: #ffffff; - --foreground: #171717; + --background: #ffffff; + --foreground: #171717; } @theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); + --color-background: var(--background); + --color-foreground: var(--foreground); + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); } @media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } + :root { + --background: #0a0a0a; + --foreground: #ededed; + } } body { - background: var(--background); - color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; + background: var(--background); + color: var(--foreground); + font-family: Arial, Helvetica, sans-serif; } diff --git a/app/layout.tsx b/app/layout.tsx index f7fa87e..89d2abf 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,34 +1,30 @@ -import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; -import "./globals.css"; +import type { Metadata } from 'next'; +import { Geist, Geist_Mono } from 'next/font/google'; +import './globals.css'; const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], + variable: '--font-geist-sans', + subsets: ['latin'], }); const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], + variable: '--font-geist-mono', + subsets: ['latin'], }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: 'Create Next App', + description: 'Generated by create next app', }; export default function RootLayout({ - children, + children, }: Readonly<{ - children: React.ReactNode; + children: React.ReactNode; }>) { - return ( - - - {children} - - - ); + return ( + + {children} + + ); } diff --git a/app/page.tsx b/app/page.tsx index fff8164..48f279a 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -3,22 +3,21 @@ import { useRouter } from 'next/navigation'; import generateID from '@/tools/simpleID'; export default function Home() { - const router = useRouter(); + const router = useRouter(); - const handleCreateGame = () => { - const id = generateID(); - router.push(`/${id}`); - }; + const handleCreateGame = () => { + const id = generateID(); + router.push(`/${id}`); + }; - return ( -
- -
- ); + return ( +
+ +
+ ); } - diff --git a/components/Card.tsx b/components/Card.tsx index f73237d..53a9529 100644 --- a/components/Card.tsx +++ b/components/Card.tsx @@ -1,24 +1,19 @@ 'use client'; -import { StandardGameCard, TarokkaGameCard } from "@/types"; +import { StandardGameCard, TarokkaGameCard } from '@/types'; type CardProps = { - card: StandardGameCard | TarokkaGameCard; - flipAction: () => void; + card: StandardGameCard | TarokkaGameCard; + flipAction: () => void; }; export default function Card({ card: { aria, url }, flipAction }: CardProps) { - return ( -
- {aria} -
- ); + return ( +
+ {aria} +
+ ); } - diff --git a/constants/standardCards.ts b/constants/standardCards.ts index a1cf017..2de7707 100644 --- a/constants/standardCards.ts +++ b/constants/standardCards.ts @@ -1,510 +1,510 @@ import { StandardCard } from '@/types'; const cards: StandardCard[] = [ - { - id: '1B', - aria: '', - back: true, - face: false, - joker: false, - suit: null, - url: '/cards/1B.svg' - }, - { - id: '2B', - aria: '', - back: true, - face: false, - joker: false, - suit: null, - url: '/cards/2B.svg' - }, - { - id: '1J', - aria: '', - back: false, - face: false, - joker: true, - suit: null, - url: '/cards/1J.svg' - }, - { - id: '2J', - aria: '', - back: false, - face: false, - joker: true, - suit: null, - url: '/cards/2J.svg' - }, - { - id: 'AC', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Clubs', - url: '/cards/AC.svg' - }, - { - id: 'AD', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Diamonds', - url: '/cards/AD.svg' - }, - { - id: 'AH', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Hearts', - url: '/cards/AH.svg' - }, - { - id: 'AS', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Spades', - url: '/cards/AS.svg' - }, - { - id: '2C', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Clubs', - url: '/cards/2C.svg' - }, - { - id: '2D', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Diamonds', - url: '/cards/2D.svg' - }, - { - id: '2H', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Hearts', - url: '/cards/2H.svg' - }, - { - id: '2S', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Spades', - url: '/cards/2S.svg' - }, - { - id: '3C', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Clubs', - url: '/cards/3C.svg' - }, - { - id: '3D', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Diamonds', - url: '/cards/3D.svg' - }, - { - id: '3H', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Hearts', - url: '/cards/3H.svg' - }, - { - id: '3S', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Spades', - url: '/cards/3S.svg' - }, - { - id: '4C', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Clubs', - url: '/cards/4C.svg' - }, - { - id: '4D', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Diamonds', - url: '/cards/4D.svg' - }, - { - id: '4H', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Hearts', - url: '/cards/4H.svg' - }, - { - id: '4S', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Spades', - url: '/cards/4S.svg' - }, - { - id: '5C', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Clubs', - url: '/cards/5C.svg' - }, - { - id: '5D', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Diamonds', - url: '/cards/5D.svg' - }, - { - id: '5H', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Hearts', - url: '/cards/5H.svg' - }, - { - id: '5S', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Spades', - url: '/cards/5S.svg' - }, - { - id: '6C', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Clubs', - url: '/cards/6C.svg' - }, - { - id: '6D', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Diamonds', - url: '/cards/6D.svg' - }, - { - id: '6H', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Hearts', - url: '/cards/6H.svg' - }, - { - id: '6S', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Spades', - url: '/cards/6S.svg' - }, - { - id: '7C', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Clubs', - url: '/cards/7C.svg' - }, - { - id: '7D', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Diamonds', - url: '/cards/7D.svg' - }, - { - id: '7H', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Hearts', - url: '/cards/7H.svg' - }, - { - id: '7S', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Spades', - url: '/cards/7S.svg' - }, - { - id: '8C', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Clubs', - url: '/cards/8C.svg' - }, - { - id: '8D', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Diamonds', - url: '/cards/8D.svg' - }, - { - id: '8H', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Hearts', - url: '/cards/8H.svg' - }, - { - id: '8S', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Spades', - url: '/cards/8S.svg' - }, - { - id: '9C', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Clubs', - url: '/cards/9C.svg' - }, - { - id: '9D', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Diamonds', - url: '/cards/9D.svg' - }, - { - id: '9H', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Hearts', - url: '/cards/9H.svg' - }, - { - id: '9S', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Spades', - url: '/cards/9S.svg' - }, - { - id: '10C', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Clubs', - url: '/cards/10C.svg' - }, - { - id: '10D', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Diamonds', - url: '/cards/10D.svg' - }, - { - id: '10H', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Hearts', - url: '/cards/10H.svg' - }, - { - id: '10S', - aria: '', - back: false, - face: false, - joker: false, - suit: 'Spades', - url: '/cards/10S.svg' - }, - { - id: 'JC', - aria: '', - back: false, - face: true, - joker: false, - suit: 'Clubs', - url: '/cards/JC.svg' - }, - { - id: 'JD', - aria: '', - back: false, - face: true, - joker: false, - suit: 'Diamonds', - url: '/cards/JD.svg' - }, - { - id: 'JH', - aria: '', - back: false, - face: true, - joker: false, - suit: 'Hearts', - url: '/cards/JH.svg' - }, - { - id: 'JS', - aria: '', - back: false, - face: true, - joker: false, - suit: 'Spades', - url: '/cards/JS.svg' - }, - { - id: 'KC', - aria: '', - back: false, - face: true, - joker: false, - suit: 'Clubs', - url: '/cards/KC.svg' - }, - { - id: 'KD', - aria: '', - back: false, - face: true, - joker: false, - suit: 'Diamonds', - url: '/cards/KD.svg' - }, - { - id: 'KH', - aria: '', - back: false, - face: true, - joker: false, - suit: 'Hearts', - url: '/cards/KH.svg' - }, - { - id: 'KS', - aria: '', - back: false, - face: true, - joker: false, - suit: 'Spades', - url: '/cards/KS.svg' - }, - { - id: 'QC', - aria: '', - back: false, - face: true, - joker: false, - suit: 'Clubs', - url: '/cards/QC.svg' - }, - { - id: 'QD', - aria: '', - back: false, - face: true, - joker: false, - suit: 'Diamonds', - url: '/cards/QD.svg' - }, - { - id: 'QH', - aria: '', - back: false, - face: true, - joker: false, - suit: 'Hearts', - url: '/cards/QH.svg' - }, - { - id: 'QS', - aria: '', - back: false, - face: true, - joker: false, - suit: 'Spades', - url: '/cards/QS.svg' - } + { + id: '1B', + aria: '', + back: true, + face: false, + joker: false, + suit: null, + url: '/cards/1B.svg', + }, + { + id: '2B', + aria: '', + back: true, + face: false, + joker: false, + suit: null, + url: '/cards/2B.svg', + }, + { + id: '1J', + aria: '', + back: false, + face: false, + joker: true, + suit: null, + url: '/cards/1J.svg', + }, + { + id: '2J', + aria: '', + back: false, + face: false, + joker: true, + suit: null, + url: '/cards/2J.svg', + }, + { + id: 'AC', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Clubs', + url: '/cards/AC.svg', + }, + { + id: 'AD', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Diamonds', + url: '/cards/AD.svg', + }, + { + id: 'AH', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Hearts', + url: '/cards/AH.svg', + }, + { + id: 'AS', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Spades', + url: '/cards/AS.svg', + }, + { + id: '2C', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Clubs', + url: '/cards/2C.svg', + }, + { + id: '2D', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Diamonds', + url: '/cards/2D.svg', + }, + { + id: '2H', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Hearts', + url: '/cards/2H.svg', + }, + { + id: '2S', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Spades', + url: '/cards/2S.svg', + }, + { + id: '3C', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Clubs', + url: '/cards/3C.svg', + }, + { + id: '3D', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Diamonds', + url: '/cards/3D.svg', + }, + { + id: '3H', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Hearts', + url: '/cards/3H.svg', + }, + { + id: '3S', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Spades', + url: '/cards/3S.svg', + }, + { + id: '4C', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Clubs', + url: '/cards/4C.svg', + }, + { + id: '4D', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Diamonds', + url: '/cards/4D.svg', + }, + { + id: '4H', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Hearts', + url: '/cards/4H.svg', + }, + { + id: '4S', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Spades', + url: '/cards/4S.svg', + }, + { + id: '5C', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Clubs', + url: '/cards/5C.svg', + }, + { + id: '5D', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Diamonds', + url: '/cards/5D.svg', + }, + { + id: '5H', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Hearts', + url: '/cards/5H.svg', + }, + { + id: '5S', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Spades', + url: '/cards/5S.svg', + }, + { + id: '6C', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Clubs', + url: '/cards/6C.svg', + }, + { + id: '6D', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Diamonds', + url: '/cards/6D.svg', + }, + { + id: '6H', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Hearts', + url: '/cards/6H.svg', + }, + { + id: '6S', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Spades', + url: '/cards/6S.svg', + }, + { + id: '7C', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Clubs', + url: '/cards/7C.svg', + }, + { + id: '7D', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Diamonds', + url: '/cards/7D.svg', + }, + { + id: '7H', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Hearts', + url: '/cards/7H.svg', + }, + { + id: '7S', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Spades', + url: '/cards/7S.svg', + }, + { + id: '8C', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Clubs', + url: '/cards/8C.svg', + }, + { + id: '8D', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Diamonds', + url: '/cards/8D.svg', + }, + { + id: '8H', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Hearts', + url: '/cards/8H.svg', + }, + { + id: '8S', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Spades', + url: '/cards/8S.svg', + }, + { + id: '9C', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Clubs', + url: '/cards/9C.svg', + }, + { + id: '9D', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Diamonds', + url: '/cards/9D.svg', + }, + { + id: '9H', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Hearts', + url: '/cards/9H.svg', + }, + { + id: '9S', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Spades', + url: '/cards/9S.svg', + }, + { + id: '10C', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Clubs', + url: '/cards/10C.svg', + }, + { + id: '10D', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Diamonds', + url: '/cards/10D.svg', + }, + { + id: '10H', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Hearts', + url: '/cards/10H.svg', + }, + { + id: '10S', + aria: '', + back: false, + face: false, + joker: false, + suit: 'Spades', + url: '/cards/10S.svg', + }, + { + id: 'JC', + aria: '', + back: false, + face: true, + joker: false, + suit: 'Clubs', + url: '/cards/JC.svg', + }, + { + id: 'JD', + aria: '', + back: false, + face: true, + joker: false, + suit: 'Diamonds', + url: '/cards/JD.svg', + }, + { + id: 'JH', + aria: '', + back: false, + face: true, + joker: false, + suit: 'Hearts', + url: '/cards/JH.svg', + }, + { + id: 'JS', + aria: '', + back: false, + face: true, + joker: false, + suit: 'Spades', + url: '/cards/JS.svg', + }, + { + id: 'KC', + aria: '', + back: false, + face: true, + joker: false, + suit: 'Clubs', + url: '/cards/KC.svg', + }, + { + id: 'KD', + aria: '', + back: false, + face: true, + joker: false, + suit: 'Diamonds', + url: '/cards/KD.svg', + }, + { + id: 'KH', + aria: '', + back: false, + face: true, + joker: false, + suit: 'Hearts', + url: '/cards/KH.svg', + }, + { + id: 'KS', + aria: '', + back: false, + face: true, + joker: false, + suit: 'Spades', + url: '/cards/KS.svg', + }, + { + id: 'QC', + aria: '', + back: false, + face: true, + joker: false, + suit: 'Clubs', + url: '/cards/QC.svg', + }, + { + id: 'QD', + aria: '', + back: false, + face: true, + joker: false, + suit: 'Diamonds', + url: '/cards/QD.svg', + }, + { + id: 'QH', + aria: '', + back: false, + face: true, + joker: false, + suit: 'Hearts', + url: '/cards/QH.svg', + }, + { + id: 'QS', + aria: '', + back: false, + face: true, + joker: false, + suit: 'Spades', + url: '/cards/QS.svg', + }, ]; export default cards; diff --git a/constants/tarokkaCards.ts b/constants/tarokkaCards.ts index 7b42f04..c633d6b 100644 --- a/constants/tarokkaCards.ts +++ b/constants/tarokkaCards.ts @@ -1,557 +1,593 @@ import { TarokkaCard } from '@/types'; const tarokkaCards: TarokkaCard[] = [ - { - id: 'Back', - name: 'Card Back', - card: 'Back of card', - description: 'Back of card', - aria: 'Back of card', - back: true, - suit: null, - url: '/img/tarokka/Back.jpg', - }, - { - id: 'Coins_01_Swashbuckler', - name: 'Swashbuckler', - card: 'One of Coins', - description: 'Those who like money yet give it up freely; likable rogues and rapscallions', - aria: 'Coins 01 Swashbuckler', - back: false, - suit: 'Coins', - url: '/img/tarokka/Coins_01_Swashbuckler.jpeg', - }, - { - id: 'Coins_02_Philanthropist', - name: 'Philanthropist', - card: 'Two of Coins', - description: 'Charity and giving on a grand scale; those who use wealth to fight evil and sickness', - aria: 'Coins 02 Philanthropist', - back: false, - suit: 'Coins', - url: '/img/tarokka/Coins_02_Philanthropist.jpeg', - }, - { - id: 'Coins_03_Trader', - name: 'Trader', - card: 'Three of Coins', - description: 'Commerce; smuggling and black markets; fair and equitable trades', - aria: 'Coins 03 Trader', - back: false, - suit: 'Coins', - url: '/img/tarokka/Coins_03_Trader.jpeg', - }, - { - id: 'Coins_04_Merchant', - name: 'Merchant', - card: 'Four of Coins', - description: 'A rare commodity or business opportunity; deceitful or dangerous business transactions', - aria: 'Coins 04 Merchant', - back: false, - suit: 'Coins', - url: '/img/tarokka/Coins_04_Merchant.jpeg', - }, - { - id: 'Coins_05_GuildMember', - name: 'Guild Member', - card: 'Five of Coins', - description: 'Like-minded individuals joined together in a common goal; pride in one\'s work', - aria: 'Coins 05 Guild Member', - back: false, - suit: 'Coins', - url: '/img/tarokka/Coins_05_GuildMember.jpeg', - }, - { - id: 'Coins_06_Beggar', - name: 'Beggar', - card: 'Six of Coins', - description: 'Sudden change in economic status or fortune', - aria: 'Coins 06 Beggar', - back: false, - suit: 'Coins', - url: '/img/tarokka/Coins_06_Beggar.jpeg', - }, - { - id: 'Coins_07_Thief', - name: 'Thief', - card: 'Seven of Coins', - description: 'Those who steal or burgle; a loss of property, beauty, innocence, friendship, or reputation', - aria: 'Coins 07 Thief', - back: false, - suit: 'Coins', - url: '/img/tarokka/Coins_07_Thief.jpeg', - }, - { - id: 'Coins_08_TaxCollector', - name: 'Tax Collector', - card: 'Eight of Coins', - description: 'Corruption; honesty in an otherwise corrupt government or organization', - aria: 'Coins 08 Tax Collector', - back: false, - suit: 'Coins', - url: '/img/tarokka/Coins_08_TaxCollector.png', - }, - { - id: 'Coins_09_Miser', - name: 'Miser', - card: 'Nine of Coins', - description: 'Hoarded wealth; those who are irreversibly unhappy or who think money is meaningless', - aria: 'Coins 09 Miser', - back: false, - suit: 'Coins', - url: '/img/tarokka/Coins_09_Miser.jpeg', - }, - { - id: 'Coins_10_Rogue', - name: 'Rogue', - card: 'Master of Coins', - description: 'Anyone for whom money is important; those who believe money is the key to their success', - aria: 'Coins 10 Rogue', - back: false, - suit: 'Coins', - url: '/img/tarokka/Coins_10_Rogue.jpeg', - }, - { - id: 'Glyphs_01_Monk', - name: 'Monk', - card: 'One of Glyphs', - description: 'Serenity; inner strength and self-reliance; supreme confidence bereft of arrogance', - aria: 'Glyphs 01 Monk', - back: false, - suit: 'Glyphs', - url: '/img/tarokka/Glyphs_01_Monk.jpeg', - }, - { - id: 'Glyphs_02_Missionary', - name: 'Missionary', - card: 'Two of Glyphs', - description: 'Those who spread wisdom and faith to others; warnings of the spread of fear and ignorance', - aria: 'Glyphs 02 Missionary', - back: false, - suit: 'Glyphs', - url: '/img/tarokka/Glyphs_02_Missionary.jpeg', - }, - { - id: 'Glyphs_03_Healer', - name: 'Healer', - card: 'Three of Glyphs', - description: 'Healing; a contagious illness, disease, or curse; those who practice the healing arts', - aria: 'Glyphs 03 Healer', - back: false, - suit: 'Glyphs', - url: '/img/tarokka/Glyphs_03_Healer.jpeg', - }, - { - id: 'Glyphs_04_Shepherd', - name: 'Shepherd', - card: 'Four of Glyphs', - description: 'Those who protect others; one who bears a burden far too great to be shouldered alone', - aria: 'Glyphs 04 Shepherd', - back: false, - suit: 'Glyphs', - url: '/img/tarokka/Glyphs_04_Shepherd.jpeg', - }, - { - id: 'Glyphs_05_Druid', - name: 'Druid', - card: 'Five of Glyphs', - description: 'The ambivalence and cruelty of nature and those who feel drawn to it; inner turmoil', - aria: 'Glyphs 05 Druid', - back: false, - suit: 'Glyphs', - url: '/img/tarokka/Glyphs_05_Druid.jpeg', - }, - { - id: 'Glyphs_06_Anarchist', - name: 'Anarchist', - card: 'Six of Glyphs', - description: 'A fundamental change brought on by one whose beliefs are being put to the test', - aria: 'Glyphs 06 Anarchist', - back: false, - suit: 'Glyphs', - url: '/img/tarokka/Glyphs_06_Anarchist.jpeg', - }, - { - id: 'Glyphs_07_Charlatan', - name: 'Charlatan', - card: 'Seven of Glyphs', - description: 'Liars; those who profess to believe one thing but actually believe another', - aria: 'Glyphs 07 Charlatan', - back: false, - suit: 'Glyphs', - url: '/img/tarokka/Glyphs_07_Charlatan.jpeg', - }, - { - id: 'Glyphs_08_Bishop', - name: 'Bishop', - card: 'Eight of Glyphs', - description: 'Strict adherence to a code or a belief; those who plot, plan, and scheme', - aria: 'Glyphs 08 Bishop', - back: false, - suit: 'Glyphs', - url: '/img/tarokka/Glyphs_08_Bishop.jpeg', - }, - { - id: 'Glyphs_09_Traitor', - name: 'Traitor', - card: 'Nine of Glyphs', - description: 'Betrayal by someone close and trusted; a weakening or loss of faith', - aria: 'Glyphs 09 Traitor', - back: false, - suit: 'Glyphs', - url: '/img/tarokka/Glyphs_09_Traitor.jpeg', - }, - { - id: 'Glyphs_10_Priest', - name: 'Priest', - card: 'Master of Glyphs', - description: 'Enlightenment; those who follow a deity, a system of values, or a higher purpose', - aria: 'Glyphs 10 Priest', - back: false, - suit: 'Glyphs', - url: '/img/tarokka/Glyphs_10_Priest.jpeg', - }, - { - id: 'Stars_01_Transmuter', - name: 'Transmuter', - card: 'One of Stars', - description: 'A new discovery; the coming of unexpected things; unforeseen consequences and chaos', - aria: 'Stars 01 Transmuter', - back: false, - suit: 'Stars', - url: '/img/tarokka/Stars_01_Transmuter.jpeg', - }, - { - id: 'Stars_02_Diviner', - name: 'Diviner', - card: 'Two of Stars', - description: 'The pursuit of knowledge tempered by wisdom; truth and honesty; sages and prophecy', - aria: 'Stars 02 Diviner', - back: false, - suit: 'Stars', - url: '/img/tarokka/Stars_02_Diviner.jpeg', - }, - { - id: 'Stars_03_Enchanter', - name: 'Enchanter', - card: 'Three of Stars', - description: 'Inner turmoil that comes from confusion, fear of failure, or false information', - aria: 'Stars 03 Enchanter', - back: false, - suit: 'Stars', - url: '/img/tarokka/Stars_03_Enchanter.png', - }, - { - id: 'Stars_04_Abjurer', - name: 'Abjurer', - card: 'Four of Stars', - description: 'Those guided by logic and reasoning; warns of an overlooked clue or piece of information', - aria: 'Stars 04 Abjurer', - back: false, - suit: 'Stars', - url: '/img/tarokka/Stars_04_Abjurer.jpeg', - }, - { - id: 'Stars_05_Elementalist', - name: 'Elementalist', - card: 'Five of Stars', - description: 'The triumph of nature over civilization; natural disasters and bountiful harvests', - aria: 'Stars 05 Elementalist', - back: false, - suit: 'Stars', - url: '/img/tarokka/Stars_05_Elementalist.jpeg', - }, - { - id: 'Stars_06_Evoker', - name: 'Evoker', - card: 'Six of Stars', - description: 'Magical or supernatural power that can\'t be controlled; magic for destructive ends', - aria: 'Stars 06 Evoker', - back: false, - suit: 'Stars', - url: '/img/tarokka/Stars_06_Evoker.jpeg', - }, - { - id: 'Stars_07_Illusionist', - name: 'Illusionist', - card: 'Seven of Stars', - description: 'Lies and deceit; grand conspiracies; secret societies; the presence of a dupe or a saboteur', - aria: 'Stars 07 Illusionist', - back: false, - suit: 'Stars', - url: '/img/tarokka/Stars_07_Illusionist.jpeg', - }, - { - id: 'Stars_08_Necromancer', - name: 'Necromancer', - card: 'Eight of Stars', - description: 'Unnatural events and unhealthy obsessions; those who follow a destructive path', - aria: 'Stars 08 Necromancer', - back: false, - suit: 'Stars', - url: '/img/tarokka/Stars_08_Necromancer.jpeg', - }, - { - id: 'Stars_09_Conjurer', - name: 'Conjurer', - card: 'Nine of Stars', - description: 'The coming of an unexpected supernatural threat; those who think of themselves as gods', - aria: 'Stars 09 Conjurer', - back: false, - suit: 'Stars', - url: '/img/tarokka/Stars_09_Conjurer.jpeg', - }, - { - id: 'Stars_10_Wizard', - name: 'Wizard', - card: 'Master of Stars', - description: 'Mystery and riddles; the unknown; those who crave magical power and great knowledge', - aria: 'Stars 10 Wizard', - back: false, - suit: 'Stars', - url: '/img/tarokka/Stars_10_Wizard.jpeg', - }, - { - id: 'Swords_01_Avenger', - name: 'Avenger', - card: 'One of Swords', - description: 'Justice and revenge for great wrongs; those on a quest to rid the world of great evil', - aria: 'Swords 01 Avenger', - back: false, - suit: 'Swords', - url: '/img/tarokka/Swords_01_Avenger.jpeg', - }, - { - id: 'Swords_02_Paladin', - name: 'Paladin', - card: 'Two of Swords', - description: 'Just and noble warriors; those who live by a code of honor and integrity', - aria: 'Swords 02 Paladin', - back: false, - suit: 'Swords', - url: '/img/tarokka/Swords_02_Paladin.jpeg', - }, - { - id: 'Swords_03_Soldier', - name: 'Soldier', - card: 'Three of Swords', - description: 'War and sacrifice; the stamina to endure great hardship', - aria: 'Swords 03 Soldier', - back: false, - suit: 'Swords', - url: '/img/tarokka/Swords_03_Soldier.jpeg', - }, - { - id: 'Swords_04_Mercenary', - name: 'Mercenary', - card: 'Four of Swords', - description: 'Inner strength and fortitude; those who fight for power or wealth', - aria: 'Swords 04 Mercenary', - back: false, - suit: 'Swords', - url: '/img/tarokka/Swords_04_Mercenary.jpeg', - }, - { - id: 'Swords_05_Myrmidon', - name: 'Myrmidon', - card: 'Five of Swords', - description: 'Great heroes; a sudden reversal of fate; the triumph of the underdog over a mighty enemy', - aria: 'Swords 05 Myrmidon', - back: false, - suit: 'Swords', - url: '/img/tarokka/Swords_05_Myrmidon.jpeg', - }, - { - id: 'Swords_06_Berserker', - name: 'Berserker', - card: 'Six of Swords', - description: 'The brutal and barbaric side of warfare; bloodlust; those with a bestial nature', - aria: 'Swords 06 Berserker', - back: false, - suit: 'Swords', - url: '/img/tarokka/Swords_06_Berserker.jpeg', - }, - { - id: 'Swords_07_HoodedOne', - name: 'Hooded One', - card: 'Seven of Swords', - description: 'Bigotry, intolerance, and xenophobia; a mysterious presence or newcomer', - aria: 'Swords 07 Hooded One', - back: false, - suit: 'Swords', - url: '/img/tarokka/Swords_07_HoodedOne.jpeg', - }, - { - id: 'Swords_08_Dictator', - name: 'Dictator', - card: 'Eight of Swords', - description: 'All that is wrong with government and leadership; those who rule through fear and violence', - aria: 'Swords 08 Dictator', - back: false, - suit: 'Swords', - url: '/img/tarokka/Swords_08_Dictator.jpeg', - }, - { - id: 'Swords_09_Torturer', - name: 'Torturer', - card: 'Nine of Swords', - description: 'The coming of suffering or merciless cruelty; one who is irredeemably evil or sadistic', - aria: 'Swords 09 Torturer', - back: false, - suit: 'Swords', - url: '/img/tarokka/Swords_09_Torturer.jpeg', - }, - { - id: 'Swords_10_Warrior', - name: 'Warrior', - card: 'Master of Swords', - description: 'Strength and force personified; violence; those who use force to accomplish their goals', - aria: 'Swords 10 Warrior', - back: false, - suit: 'Swords', - url: '/img/tarokka/Swords_10_Warrior.jpeg', - }, - { - id: 'Crowns_Artifact', - name: 'Artifact', - card: 'The Artifact', - description: 'The importance of some physical object that must be obtained, protected, or destroyed at all costs', - aria: 'High Deck Artifact', - back: false, - suit: 'High Deck', - url: '/img/tarokka/Crowns_Artifact.jpeg', - }, - { - id: 'Crowns_Beast', - name: 'Beast', - card: 'The Beast', - description: 'Great rage or passion; something bestial or malevolent hiding in plain sight or lurking just below the surface', - aria: 'High Deck Beast', - back: false, - suit: 'High Deck', - url: '/img/tarokka/Crowns_Beast.jpeg', - }, - { - id: 'Crowns_BrokenOne', - name: 'Broken One', - card: 'The Broken One', - description: 'Defeat, failure, and despair; the loss of something or someone important, without which one feels incomplete', - aria: 'High Deck Broken One', - back: false, - suit: 'High Deck', - url: '/img/tarokka/Crowns_BrokenOne.jpeg', - }, - { - id: 'Crowns_Darklord', - name: 'Darklord', - card: 'The Darklord', - description: 'A single, powerful individual of an evil nature, one whose goals have enormous and far-reaching consequences', - aria: 'High Deck Darklord', - back: false, - suit: 'High Deck', - url: '/img/tarokka/Crowns_Darklord.jpeg', - }, - { - id: 'Crowns_Donjon', - name: 'Donjon', - card: 'The Donjon', - description: 'Isolation and imprisonment; being so conservative in thinking as to be a prisoner of one\'s own beliefs', - aria: 'High Deck Donjon', - back: false, - suit: 'High Deck', - url: '/img/tarokka/Crowns_Donjon.jpeg', - }, - { - id: 'Crowns_Executioner', - name: 'Executioner', - card: 'The Executioner', - description: 'The imminent death of one rightly or wrongly convicted of a crime; false accusations and unjust prosecution', - aria: 'High Deck Executioner', - back: false, - suit: 'High Deck', - url: '/img/tarokka/Crowns_Executioner.jpeg', - }, - { - id: 'Crowns_Ghost', - name: 'Ghost', - card: 'The Ghost', - description: 'The looming past; the return of an old enemy or the discovery of a secret buried long ago', - aria: 'High Deck Ghost', - back: false, - suit: 'High Deck', - url: '/img/tarokka/Crowns_Ghost.jpeg', - }, - { - id: 'Crowns_Horseman', - name: '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', - aria: 'High Deck Horseman', - back: false, - suit: 'High Deck', - url: '/img/tarokka/Crowns_Horseman.jpeg', - }, - { - id: 'Crowns_Innoncent', - name: 'Innocent', - card: 'The Innoncent', - 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', - back: false, - suit: 'High Deck', - url: '/img/tarokka/Crowns_Innoncent.jpeg', - }, - { - id: 'Crowns_Marionette', - name: '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', - aria: 'High Deck Marionette', - back: false, - suit: 'High Deck', - url: '/img/tarokka/Crowns_Marionette.jpeg', - }, - { - id: 'Crowns_Mists', - name: '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', - aria: 'High Deck Mists', - back: false, - suit: 'High Deck', - url: '/img/tarokka/Crowns_Mists.jpeg', - }, - { - id: 'Crowns_Raven', - name: 'Raven', - card: 'The Raven', - description: 'A hidden source of information; a fortunate turn of events; a secret potential for good', - aria: 'High Deck Raven', - back: false, - suit: 'High Deck', - url: '/img/tarokka/Crowns_Raven.jpeg', - }, - { - id: 'Crowns_Seer', - name: 'Seer', - card: 'The Seer', - description: 'Inspiration and keen intellect; a future event, the outcome of which will hinge on a clever mind', - aria: 'High Deck Seer', - back: false, - suit: 'High Deck', - url: '/img/tarokka/Crowns_Seer.jpeg', - }, - { - id: 'Crowns_Tempter', - name: 'Tempter', - card: 'The Tempter', - description: 'One who has been compromised or led astray by temptation or foolishness; one who tempts others for evil ends', - aria: 'High Deck Tempter', - back: false, - suit: 'High Deck', - url: '/img/tarokka/Crowns_Tempter.png', - } + { + id: 'Back', + name: 'Card Back', + card: 'Back of card', + description: 'Back of card', + aria: 'Back of card', + back: true, + suit: null, + url: '/img/tarokka/Back.jpg', + }, + { + id: 'Coins_01_Swashbuckler', + name: 'Swashbuckler', + card: 'One of Coins', + description: 'Those who like money yet give it up freely; likable rogues and rapscallions', + aria: 'Coins 01 Swashbuckler', + back: false, + suit: 'Coins', + url: '/img/tarokka/Coins_01_Swashbuckler.jpeg', + }, + { + id: 'Coins_02_Philanthropist', + name: 'Philanthropist', + card: 'Two of Coins', + description: + 'Charity and giving on a grand scale; those who use wealth to fight evil and sickness', + aria: 'Coins 02 Philanthropist', + back: false, + suit: 'Coins', + url: '/img/tarokka/Coins_02_Philanthropist.jpeg', + }, + { + id: 'Coins_03_Trader', + name: 'Trader', + card: 'Three of Coins', + description: 'Commerce; smuggling and black markets; fair and equitable trades', + aria: 'Coins 03 Trader', + back: false, + suit: 'Coins', + url: '/img/tarokka/Coins_03_Trader.jpeg', + }, + { + id: 'Coins_04_Merchant', + name: 'Merchant', + card: 'Four of Coins', + description: + 'A rare commodity or business opportunity; deceitful or dangerous business transactions', + aria: 'Coins 04 Merchant', + back: false, + suit: 'Coins', + url: '/img/tarokka/Coins_04_Merchant.jpeg', + }, + { + id: 'Coins_05_GuildMember', + name: 'Guild Member', + card: 'Five of Coins', + description: "Like-minded individuals joined together in a common goal; pride in one's work", + aria: 'Coins 05 Guild Member', + back: false, + suit: 'Coins', + url: '/img/tarokka/Coins_05_GuildMember.jpeg', + }, + { + id: 'Coins_06_Beggar', + name: 'Beggar', + card: 'Six of Coins', + description: 'Sudden change in economic status or fortune', + aria: 'Coins 06 Beggar', + back: false, + suit: 'Coins', + url: '/img/tarokka/Coins_06_Beggar.jpeg', + }, + { + id: 'Coins_07_Thief', + name: 'Thief', + card: 'Seven of Coins', + description: + 'Those who steal or burgle; a loss of property, beauty, innocence, friendship, or reputation', + aria: 'Coins 07 Thief', + back: false, + suit: 'Coins', + url: '/img/tarokka/Coins_07_Thief.jpeg', + }, + { + id: 'Coins_08_TaxCollector', + name: 'Tax Collector', + card: 'Eight of Coins', + description: 'Corruption; honesty in an otherwise corrupt government or organization', + aria: 'Coins 08 Tax Collector', + back: false, + suit: 'Coins', + url: '/img/tarokka/Coins_08_TaxCollector.png', + }, + { + id: 'Coins_09_Miser', + name: 'Miser', + card: 'Nine of Coins', + description: + 'Hoarded wealth; those who are irreversibly unhappy or who think money is meaningless', + aria: 'Coins 09 Miser', + back: false, + suit: 'Coins', + url: '/img/tarokka/Coins_09_Miser.jpeg', + }, + { + id: 'Coins_10_Rogue', + name: 'Rogue', + card: 'Master of Coins', + description: + 'Anyone for whom money is important; those who believe money is the key to their success', + aria: 'Coins 10 Rogue', + back: false, + suit: 'Coins', + url: '/img/tarokka/Coins_10_Rogue.jpeg', + }, + { + id: 'Glyphs_01_Monk', + name: 'Monk', + card: 'One of Glyphs', + description: + 'Serenity; inner strength and self-reliance; supreme confidence bereft of arrogance', + aria: 'Glyphs 01 Monk', + back: false, + suit: 'Glyphs', + url: '/img/tarokka/Glyphs_01_Monk.jpeg', + }, + { + id: 'Glyphs_02_Missionary', + name: 'Missionary', + card: 'Two of Glyphs', + description: + 'Those who spread wisdom and faith to others; warnings of the spread of fear and ignorance', + aria: 'Glyphs 02 Missionary', + back: false, + suit: 'Glyphs', + url: '/img/tarokka/Glyphs_02_Missionary.jpeg', + }, + { + id: 'Glyphs_03_Healer', + name: 'Healer', + card: 'Three of Glyphs', + description: + 'Healing; a contagious illness, disease, or curse; those who practice the healing arts', + aria: 'Glyphs 03 Healer', + back: false, + suit: 'Glyphs', + url: '/img/tarokka/Glyphs_03_Healer.jpeg', + }, + { + id: 'Glyphs_04_Shepherd', + name: 'Shepherd', + card: 'Four of Glyphs', + description: + 'Those who protect others; one who bears a burden far too great to be shouldered alone', + aria: 'Glyphs 04 Shepherd', + back: false, + suit: 'Glyphs', + url: '/img/tarokka/Glyphs_04_Shepherd.jpeg', + }, + { + id: 'Glyphs_05_Druid', + name: 'Druid', + card: 'Five of Glyphs', + description: + 'The ambivalence and cruelty of nature and those who feel drawn to it; inner turmoil', + aria: 'Glyphs 05 Druid', + back: false, + suit: 'Glyphs', + url: '/img/tarokka/Glyphs_05_Druid.jpeg', + }, + { + id: 'Glyphs_06_Anarchist', + name: 'Anarchist', + card: 'Six of Glyphs', + description: 'A fundamental change brought on by one whose beliefs are being put to the test', + aria: 'Glyphs 06 Anarchist', + back: false, + suit: 'Glyphs', + url: '/img/tarokka/Glyphs_06_Anarchist.jpeg', + }, + { + id: 'Glyphs_07_Charlatan', + name: 'Charlatan', + card: 'Seven of Glyphs', + description: 'Liars; those who profess to believe one thing but actually believe another', + aria: 'Glyphs 07 Charlatan', + back: false, + suit: 'Glyphs', + url: '/img/tarokka/Glyphs_07_Charlatan.jpeg', + }, + { + id: 'Glyphs_08_Bishop', + name: 'Bishop', + card: 'Eight of Glyphs', + description: 'Strict adherence to a code or a belief; those who plot, plan, and scheme', + aria: 'Glyphs 08 Bishop', + back: false, + suit: 'Glyphs', + url: '/img/tarokka/Glyphs_08_Bishop.jpeg', + }, + { + id: 'Glyphs_09_Traitor', + name: 'Traitor', + card: 'Nine of Glyphs', + description: 'Betrayal by someone close and trusted; a weakening or loss of faith', + aria: 'Glyphs 09 Traitor', + back: false, + suit: 'Glyphs', + url: '/img/tarokka/Glyphs_09_Traitor.jpeg', + }, + { + id: 'Glyphs_10_Priest', + name: 'Priest', + card: 'Master of Glyphs', + description: 'Enlightenment; those who follow a deity, a system of values, or a higher purpose', + aria: 'Glyphs 10 Priest', + back: false, + suit: 'Glyphs', + url: '/img/tarokka/Glyphs_10_Priest.jpeg', + }, + { + id: 'Stars_01_Transmuter', + name: 'Transmuter', + card: 'One of Stars', + description: + 'A new discovery; the coming of unexpected things; unforeseen consequences and chaos', + aria: 'Stars 01 Transmuter', + back: false, + suit: 'Stars', + url: '/img/tarokka/Stars_01_Transmuter.jpeg', + }, + { + id: 'Stars_02_Diviner', + name: 'Diviner', + card: 'Two of Stars', + description: + 'The pursuit of knowledge tempered by wisdom; truth and honesty; sages and prophecy', + aria: 'Stars 02 Diviner', + back: false, + suit: 'Stars', + url: '/img/tarokka/Stars_02_Diviner.jpeg', + }, + { + id: 'Stars_03_Enchanter', + name: 'Enchanter', + card: 'Three of Stars', + description: 'Inner turmoil that comes from confusion, fear of failure, or false information', + aria: 'Stars 03 Enchanter', + back: false, + suit: 'Stars', + url: '/img/tarokka/Stars_03_Enchanter.png', + }, + { + id: 'Stars_04_Abjurer', + name: 'Abjurer', + card: 'Four of Stars', + description: + 'Those guided by logic and reasoning; warns of an overlooked clue or piece of information', + aria: 'Stars 04 Abjurer', + back: false, + suit: 'Stars', + url: '/img/tarokka/Stars_04_Abjurer.jpeg', + }, + { + id: 'Stars_05_Elementalist', + name: 'Elementalist', + card: 'Five of Stars', + description: + 'The triumph of nature over civilization; natural disasters and bountiful harvests', + aria: 'Stars 05 Elementalist', + back: false, + suit: 'Stars', + url: '/img/tarokka/Stars_05_Elementalist.jpeg', + }, + { + id: 'Stars_06_Evoker', + name: 'Evoker', + card: 'Six of Stars', + description: + "Magical or supernatural power that can't be controlled; magic for destructive ends", + aria: 'Stars 06 Evoker', + back: false, + suit: 'Stars', + url: '/img/tarokka/Stars_06_Evoker.jpeg', + }, + { + id: 'Stars_07_Illusionist', + name: 'Illusionist', + card: 'Seven of Stars', + description: + 'Lies and deceit; grand conspiracies; secret societies; the presence of a dupe or a saboteur', + aria: 'Stars 07 Illusionist', + back: false, + suit: 'Stars', + url: '/img/tarokka/Stars_07_Illusionist.jpeg', + }, + { + id: 'Stars_08_Necromancer', + name: 'Necromancer', + card: 'Eight of Stars', + description: 'Unnatural events and unhealthy obsessions; those who follow a destructive path', + aria: 'Stars 08 Necromancer', + back: false, + suit: 'Stars', + url: '/img/tarokka/Stars_08_Necromancer.jpeg', + }, + { + id: 'Stars_09_Conjurer', + name: 'Conjurer', + card: 'Nine of Stars', + description: + 'The coming of an unexpected supernatural threat; those who think of themselves as gods', + aria: 'Stars 09 Conjurer', + back: false, + suit: 'Stars', + url: '/img/tarokka/Stars_09_Conjurer.jpeg', + }, + { + id: 'Stars_10_Wizard', + name: 'Wizard', + card: 'Master of Stars', + description: + 'Mystery and riddles; the unknown; those who crave magical power and great knowledge', + aria: 'Stars 10 Wizard', + back: false, + suit: 'Stars', + url: '/img/tarokka/Stars_10_Wizard.jpeg', + }, + { + id: 'Swords_01_Avenger', + name: 'Avenger', + card: 'One of Swords', + description: + 'Justice and revenge for great wrongs; those on a quest to rid the world of great evil', + aria: 'Swords 01 Avenger', + back: false, + suit: 'Swords', + url: '/img/tarokka/Swords_01_Avenger.jpeg', + }, + { + id: 'Swords_02_Paladin', + name: 'Paladin', + card: 'Two of Swords', + description: 'Just and noble warriors; those who live by a code of honor and integrity', + aria: 'Swords 02 Paladin', + back: false, + suit: 'Swords', + url: '/img/tarokka/Swords_02_Paladin.jpeg', + }, + { + id: 'Swords_03_Soldier', + name: 'Soldier', + card: 'Three of Swords', + description: 'War and sacrifice; the stamina to endure great hardship', + aria: 'Swords 03 Soldier', + back: false, + suit: 'Swords', + url: '/img/tarokka/Swords_03_Soldier.jpeg', + }, + { + id: 'Swords_04_Mercenary', + name: 'Mercenary', + card: 'Four of Swords', + description: 'Inner strength and fortitude; those who fight for power or wealth', + aria: 'Swords 04 Mercenary', + back: false, + suit: 'Swords', + url: '/img/tarokka/Swords_04_Mercenary.jpeg', + }, + { + id: 'Swords_05_Myrmidon', + name: 'Myrmidon', + card: 'Five of Swords', + description: + 'Great heroes; a sudden reversal of fate; the triumph of the underdog over a mighty enemy', + aria: 'Swords 05 Myrmidon', + back: false, + suit: 'Swords', + url: '/img/tarokka/Swords_05_Myrmidon.jpeg', + }, + { + id: 'Swords_06_Berserker', + name: 'Berserker', + card: 'Six of Swords', + description: 'The brutal and barbaric side of warfare; bloodlust; those with a bestial nature', + aria: 'Swords 06 Berserker', + back: false, + suit: 'Swords', + url: '/img/tarokka/Swords_06_Berserker.jpeg', + }, + { + id: 'Swords_07_HoodedOne', + name: 'Hooded One', + card: 'Seven of Swords', + description: 'Bigotry, intolerance, and xenophobia; a mysterious presence or newcomer', + aria: 'Swords 07 Hooded One', + back: false, + suit: 'Swords', + url: '/img/tarokka/Swords_07_HoodedOne.jpeg', + }, + { + id: 'Swords_08_Dictator', + name: 'Dictator', + card: 'Eight of Swords', + description: + 'All that is wrong with government and leadership; those who rule through fear and violence', + aria: 'Swords 08 Dictator', + back: false, + suit: 'Swords', + url: '/img/tarokka/Swords_08_Dictator.jpeg', + }, + { + id: 'Swords_09_Torturer', + name: 'Torturer', + card: 'Nine of Swords', + description: + 'The coming of suffering or merciless cruelty; one who is irredeemably evil or sadistic', + aria: 'Swords 09 Torturer', + back: false, + suit: 'Swords', + url: '/img/tarokka/Swords_09_Torturer.jpeg', + }, + { + id: 'Swords_10_Warrior', + name: 'Warrior', + card: 'Master of Swords', + description: + 'Strength and force personified; violence; those who use force to accomplish their goals', + aria: 'Swords 10 Warrior', + back: false, + suit: 'Swords', + url: '/img/tarokka/Swords_10_Warrior.jpeg', + }, + { + id: 'Crowns_Artifact', + name: 'Artifact', + card: 'The Artifact', + description: + 'The importance of some physical object that must be obtained, protected, or destroyed at all costs', + aria: 'High Deck Artifact', + back: false, + suit: 'High Deck', + url: '/img/tarokka/Crowns_Artifact.jpeg', + }, + { + id: 'Crowns_Beast', + name: 'Beast', + card: 'The Beast', + description: + 'Great rage or passion; something bestial or malevolent hiding in plain sight or lurking just below the surface', + aria: 'High Deck Beast', + back: false, + suit: 'High Deck', + url: '/img/tarokka/Crowns_Beast.jpeg', + }, + { + id: 'Crowns_BrokenOne', + name: 'Broken One', + card: 'The Broken One', + description: + 'Defeat, failure, and despair; the loss of something or someone important, without which one feels incomplete', + aria: 'High Deck Broken One', + back: false, + suit: 'High Deck', + url: '/img/tarokka/Crowns_BrokenOne.jpeg', + }, + { + id: 'Crowns_Darklord', + name: 'Darklord', + card: 'The Darklord', + description: + 'A single, powerful individual of an evil nature, one whose goals have enormous and far-reaching consequences', + aria: 'High Deck Darklord', + back: false, + suit: 'High Deck', + url: '/img/tarokka/Crowns_Darklord.jpeg', + }, + { + id: 'Crowns_Donjon', + name: 'Donjon', + card: 'The Donjon', + description: + "Isolation and imprisonment; being so conservative in thinking as to be a prisoner of one's own beliefs", + aria: 'High Deck Donjon', + back: false, + suit: 'High Deck', + url: '/img/tarokka/Crowns_Donjon.jpeg', + }, + { + id: 'Crowns_Executioner', + name: 'Executioner', + card: 'The Executioner', + description: + 'The imminent death of one rightly or wrongly convicted of a crime; false accusations and unjust prosecution', + aria: 'High Deck Executioner', + back: false, + suit: 'High Deck', + url: '/img/tarokka/Crowns_Executioner.jpeg', + }, + { + id: 'Crowns_Ghost', + name: 'Ghost', + card: 'The Ghost', + description: + 'The looming past; the return of an old enemy or the discovery of a secret buried long ago', + aria: 'High Deck Ghost', + back: false, + suit: 'High Deck', + url: '/img/tarokka/Crowns_Ghost.jpeg', + }, + { + id: 'Crowns_Horseman', + name: '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', + aria: 'High Deck Horseman', + back: false, + suit: 'High Deck', + url: '/img/tarokka/Crowns_Horseman.jpeg', + }, + { + id: 'Crowns_Innoncent', + name: 'Innocent', + card: 'The Innoncent', + 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', + back: false, + suit: 'High Deck', + url: '/img/tarokka/Crowns_Innoncent.jpeg', + }, + { + id: 'Crowns_Marionette', + name: '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', + aria: 'High Deck Marionette', + back: false, + suit: 'High Deck', + url: '/img/tarokka/Crowns_Marionette.jpeg', + }, + { + id: 'Crowns_Mists', + name: '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", + aria: 'High Deck Mists', + back: false, + suit: 'High Deck', + url: '/img/tarokka/Crowns_Mists.jpeg', + }, + { + id: 'Crowns_Raven', + name: 'Raven', + card: 'The Raven', + description: + 'A hidden source of information; a fortunate turn of events; a secret potential for good', + aria: 'High Deck Raven', + back: false, + suit: 'High Deck', + url: '/img/tarokka/Crowns_Raven.jpeg', + }, + { + id: 'Crowns_Seer', + name: 'Seer', + card: 'The Seer', + description: + 'Inspiration and keen intellect; a future event, the outcome of which will hinge on a clever mind', + aria: 'High Deck Seer', + back: false, + suit: 'High Deck', + url: '/img/tarokka/Crowns_Seer.jpeg', + }, + { + id: 'Crowns_Tempter', + name: 'Tempter', + card: 'The Tempter', + description: + 'One who has been compromised or led astray by temptation or foolishness; one who tempts others for evil ends', + aria: 'High Deck Tempter', + back: false, + suit: 'High Deck', + url: '/img/tarokka/Crowns_Tempter.png', + }, ]; export default tarokkaCards; - diff --git a/lib/GameStore.ts b/lib/GameStore.ts index 91b1413..07e2f53 100644 --- a/lib/GameStore.ts +++ b/lib/GameStore.ts @@ -1,75 +1,80 @@ -import Deck from './TarokkaDeck' +import Deck from './TarokkaDeck'; -import { GameState, GameUpdate, TarokkaGameCard } from '../types' +import { GameState, GameUpdate, TarokkaGameCard } from '../types'; const deck = new Deck(); export default class GameStore { - private games: Map; + private games: Map; - constructor() { - this.games = new Map(); - } + constructor() { + this.games = new Map(); + } - createGame(id: string): GameState { - if (this.games.has(id)) throw new Error(`Game ${id} already exists`); + createGame(id: string): GameState { + if (this.games.has(id)) throw new Error(`Game ${id} already exists`); - const newGame: GameState = { - id, - players: new Set(), - cards: deck.select(5).map(card => ({ ...card, flipped: false })), - lastUpdated: Date.now(), - }; + const newGame: GameState = { + id, + players: new Set(), + cards: deck.select(5).map((card) => ({ ...card, flipped: false })), + lastUpdated: Date.now(), + }; - this.games.set(id, newGame); + this.games.set(id, newGame); - return newGame; - } + return newGame; + } - joinGame(gameID: string, playerID: string): GameUpdate { - const game = this.games.get(gameID) || this.createGame(gameID); + joinGame(gameID: string, playerID: string): GameUpdate { + const game = this.games.get(gameID) || this.createGame(gameID); - game.players.add(playerID); - game.lastUpdated = Date.now(); + game.players.add(playerID); + game.lastUpdated = Date.now(); - return this.gameUpdate(game); - } + return this.gameUpdate(game); + } - leaveGame(gameID: string, playerID: string): GameState { - const game = this.getGame(gameID); + leaveGame(gameID: string, playerID: string): GameState { + const game = this.getGame(gameID); - game.players.delete(playerID); - game.lastUpdated = Date.now(); + game.players.delete(playerID); + game.lastUpdated = Date.now(); - return game; - } + return game; + } - flipCard(gameID: string, cardIndex: number): GameUpdate { - const game = this.getGame(gameID); - const card = game.cards[cardIndex]; + flipCard(gameID: string, cardIndex: number): GameUpdate { + const game = this.getGame(gameID); + const card = game.cards[cardIndex]; - if (!card) throw new Error(`Card ${cardIndex} not found`); + if (!card) throw new Error(`Card ${cardIndex} not found`); - card.flipped = !card.flipped; - game.lastUpdated = Date.now(); + card.flipped = !card.flipped; + game.lastUpdated = Date.now(); - return this.gameUpdate(game); - } + return this.gameUpdate(game); + } - getGame(gameID: string): GameState { - const game = this.games.get(gameID); + getGame(gameID: string): GameState { + const game = this.games.get(gameID); - if (!game) throw new Error(`Game ${gameID} not found`); + if (!game) throw new Error(`Game ${gameID} not found`); - return game; - } + return game; + } - gameUpdate(game: GameState): GameUpdate { - const { id, cards } = game; - return { id, cards: (cards as TarokkaGameCard[]).map((card: TarokkaGameCard) => card.flipped ? card : { ...deck.getBack(), flipped: false }) }; - } + gameUpdate(game: GameState): GameUpdate { + const { id, cards } = game; + return { + id, + cards: (cards as TarokkaGameCard[]).map((card: TarokkaGameCard) => + card.flipped ? card : { ...deck.getBack(), flipped: false }, + ), + }; + } - deleteGame(gameID: string): void { - this.games.delete(gameID); - } + deleteGame(gameID: string): void { + this.games.delete(gameID); + } } diff --git a/lib/StandardDeck.ts b/lib/StandardDeck.ts index 197b3de..41def2f 100644 --- a/lib/StandardDeck.ts +++ b/lib/StandardDeck.ts @@ -3,45 +3,45 @@ import cards from '../constants/standardCards'; import type { StandardCard } from '../types'; export interface Options { - back: number; - jokers: boolean; + back: number; + jokers: boolean; } export interface OptionProps { - back?: number; - jokers?: boolean; + back?: number; + jokers?: boolean; } const DEFAULT_OPTIONS = { - jokers: false, - back: 1, + jokers: false, + back: 1, }; export default class Cards { - private options: Options; + private options: Options; - private deck: StandardCard[] = []; - private backs: StandardCard[] = []; - private jokers: StandardCard[] = []; + private deck: StandardCard[] = []; + private backs: StandardCard[] = []; + private jokers: StandardCard[] = []; - constructor(options: OptionProps = {}) { - this.options = { ...DEFAULT_OPTIONS, ...options }; + constructor(options: OptionProps = {}) { + this.options = { ...DEFAULT_OPTIONS, ...options }; - this.deck = cards.filter(card => !card.back && (this.options.jokers || !card.joker)); - this.backs = cards.filter(card => card.back); - this.jokers = cards.filter(card => card.joker); - } + this.deck = cards.filter((card) => !card.back && (this.options.jokers || !card.joker)); + this.backs = cards.filter((card) => card.back); + this.jokers = cards.filter((card) => card.joker); + } - select(count: number): StandardCard[] { - return getRandomItems(this.deck, count); - } + select(count: number): StandardCard[] { + return getRandomItems(this.deck, count); + } - getBack(style: number): StandardCard { - style = style || this.options.back; - return this.backs.find(card => card.id.startsWith(String(style))) || this.backs[0]; - } + getBack(style: number): StandardCard { + style = style || this.options.back; + return this.backs.find((card) => card.id.startsWith(String(style))) || this.backs[0]; + } - getJokers(): StandardCard[] { - return this.jokers; - } + getJokers(): StandardCard[] { + return this.jokers; + } } diff --git a/lib/TarokkaDeck.ts b/lib/TarokkaDeck.ts index eab6b24..f67cfec 100644 --- a/lib/TarokkaDeck.ts +++ b/lib/TarokkaDeck.ts @@ -3,19 +3,19 @@ import cards from '../constants/tarokkaCards'; import type { TarokkaCard } from '../types'; export default class TarokkaDeck { - private deck: TarokkaCard[] = []; - private backs: TarokkaCard[] = []; + private deck: TarokkaCard[] = []; + private backs: TarokkaCard[] = []; - constructor() { - this.deck = cards.filter(card => !card.back); - this.backs = cards.filter(card => card.back); - } + constructor() { + this.deck = cards.filter((card) => !card.back); + this.backs = cards.filter((card) => card.back); + } - select(count: number): TarokkaCard[] { - return getRandomItems(this.deck, count); - } + select(count: number): TarokkaCard[] { + return getRandomItems(this.deck, count); + } - getBack(): TarokkaCard { - return this.backs[0]; - } + getBack(): TarokkaCard { + return this.backs[0]; + } } diff --git a/package-lock.json b/package-lock.json index 8835750..892b45f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "custom-server-app", + "name": "tarokka", "lockfileVersion": 3, "requires": true, "packages": { @@ -14,10 +14,11 @@ }, "devDependencies": { "@tailwindcss/postcss": "^4", - "@types/node": "^22.14.0", - "@types/react": "^19.1.0", + "@types/node": "^22.14.1", + "@types/react": "^19.1.1", "@types/react-dom": "^19.1.2", "nodemon": "^3.1.9", + "prettier": "^3.5.3", "tailwindcss": "^4", "ts-node": "^10.9.2", "typescript": "^5.8.3" @@ -822,18 +823,20 @@ } }, "node_modules/@types/node": { - "version": "22.14.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.0.tgz", - "integrity": "sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==", + "version": "22.14.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", + "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", + "license": "MIT", "dependencies": { "undici-types": "~6.21.0" } }, "node_modules/@types/react": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.0.tgz", - "integrity": "sha512-UaicktuQI+9UKyA4njtDOGBD/67t8YEBt2xdfqu8+gP9hqPUPsiXlNPcpS2gVdjmis5GKPG3fCxbQLVgxsQZ8w==", + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.1.tgz", + "integrity": "sha512-ePapxDL7qrgqSF67s0h9m412d9DbXyC1n59O2st+9rjuuamWsZuD2w55rqY12CbzsZ7uVXb5Nw0gEp9Z8MMutQ==", "dev": true, + "license": "MIT", "dependencies": { "csstype": "^3.0.2" } @@ -843,6 +846,7 @@ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.2.tgz", "integrity": "sha512-XGJkWF41Qq305SKWEILa1O8vzhb3aOo3ogBlSmiqNko/WmRb6QIaweuZCXjKygVDXpzXb5wyxKTSOsmkuqj+Qw==", "dev": true, + "license": "MIT", "peerDependencies": { "@types/react": "^19.0.0" } @@ -1123,6 +1127,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -1738,6 +1743,7 @@ "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz", "integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==", "dev": true, + "license": "MIT", "dependencies": { "chokidar": "^3.5.2", "debug": "^4", @@ -1831,6 +1837,22 @@ "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": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -1841,6 +1863,7 @@ "version": "19.1.0", "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -1849,6 +1872,7 @@ "version": "19.1.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "license": "MIT", "dependencies": { "scheduler": "^0.26.0" }, @@ -1871,7 +1895,8 @@ "node_modules/scheduler": { "version": "0.26.0", "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": { "version": "7.7.1", @@ -1957,6 +1982,7 @@ "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", "dev": true, + "license": "MIT", "dependencies": { "semver": "^7.5.3" }, @@ -2219,6 +2245,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -2236,7 +2263,8 @@ "node_modules/undici-types": { "version": "6.21.0", "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": { "version": "3.0.1", diff --git a/package.json b/package.json index 185493f..d1d18a5 100644 --- a/package.json +++ b/package.json @@ -8,19 +8,20 @@ "dependencies": { "cross-env": "^7.0.3", "next": "latest", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "^19.1.0", + "react-dom": "^19.1.0", "socket.io": "^4.8.1", "socket.io-client": "^4.8.1" }, "devDependencies": { "@tailwindcss/postcss": "^4", - "@types/node": "^18.11.5", - "@types/react": "^18.0.23", - "@types/react-dom": "^18.0.7", - "nodemon": "^2.0.20", + "@types/node": "^22.14.1", + "@types/react": "^19.1.1", + "@types/react-dom": "^19.1.2", + "nodemon": "^3.1.9", + "prettier": "^3.5.3", "tailwindcss": "^4", - "ts-node": "^10.9.1", - "typescript": "^4.8.4" + "ts-node": "^10.9.2", + "typescript": "^5.8.3" } } diff --git a/server.ts b/server.ts index 3cb4d0e..dc54bf1 100644 --- a/server.ts +++ b/server.ts @@ -6,51 +6,50 @@ import GameStore from './lib/GameStore'; import type { ClientUpdate } from './types'; const dev = process.env.NODE_ENV !== 'production'; -const hostname = 'localhost'; +const hostname = '0.0.0.0'; const port = 3000; -// when using middleware `hostname` and `port` must be provided const app = next({ dev, hostname, port }); const handler = app.getRequestHandler(); const gameStore = new GameStore(); app.prepare().then(() => { - const httpServer = createServer(handler); + const httpServer = createServer(handler); - const io = new SocketIOServer(httpServer); + const io = new SocketIOServer(httpServer); - io.on('connection', (socket: Socket) => { - console.log(`Client connected: ${socket.id}`); + io.on('connection', (socket: Socket) => { + console.log(`Client connected: ${socket.id}`); - socket.on('join', (gameID) => { - socket.join(gameID); - const gameUpdate = gameStore.joinGame(gameID, socket.id); + socket.on('join', (gameID) => { + socket.join(gameID); + 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) => { - console.log('Card flipped:', { gameID, cardIndex }); + socket.on('flip-card', ({ gameID, cardIndex }: ClientUpdate) => { + console.log('Card flipped:', { gameID, cardIndex }); - const gameUpdate = gameStore.flipCard(gameID, cardIndex); + const gameUpdate = gameStore.flipCard(gameID, cardIndex); - io.to(gameID).emit('card-flipped', gameUpdate); - }); + io.to(gameID).emit('card-flipped', gameUpdate); + }); - socket.on('disconnect', () => { - console.log(`Client disconnected: ${socket.id}`); - }); - }); + socket.on('disconnect', () => { + console.log(`Client disconnected: ${socket.id}`); + }); + }); - httpServer - .once('error', (err) => { - console.error('Server error:', err); - process.exit(1); - }) - .listen(port, () => { - console.log(`> Ready on http://${hostname}:${port}`); - }); + httpServer + .once('error', (err) => { + console.error('Server error:', err); + process.exit(1); + }) + .listen(port, () => { + console.log(`> Ready on http://${hostname}:${port}`); + }); }); diff --git a/tools/getRandomItems.ts b/tools/getRandomItems.ts index ac5ed52..df1cd7a 100644 --- a/tools/getRandomItems.ts +++ b/tools/getRandomItems.ts @@ -1,11 +1,11 @@ export default function getRandomItems(items: T[], count: number): T[] { - const shuffled = [...items]; + const shuffled = [...items]; - // Fisher-Yates shuffle - for (let i = shuffled.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; - } + // Fisher-Yates shuffle + for (let i = shuffled.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; + } - return count > shuffled.length ? shuffled : shuffled.slice(0, count); -} + return count > shuffled.length ? shuffled : shuffled.slice(0, count); +} diff --git a/tools/simpleID.ts b/tools/simpleID.ts index 7255844..61700d1 100644 --- a/tools/simpleID.ts +++ b/tools/simpleID.ts @@ -1,10 +1,9 @@ import getRandomItems from '@/tools/getRandomItems'; -const alphabet = - '0123456789abcdefghijklmnopqrstuvwxyz'; +const alphabet = '0123456789abcdefghijklmnopqrstuvwxyz'; const generateID = (length: number = 6) => { - return getRandomItems(alphabet.split(''), length).join(''); + return getRandomItems(alphabet.split(''), length).join(''); }; export default generateID; diff --git a/types/index.ts b/types/index.ts index 19e3a1a..38dd744 100644 --- a/types/index.ts +++ b/types/index.ts @@ -1,45 +1,45 @@ export interface StandardCard { - id: string; - aria: string; - back: boolean; - face: boolean; - joker: boolean; - suit: 'Clubs' | 'Diamonds' | 'Hearts' | 'Spades' | null; - url: string; + id: string; + aria: string; + back: boolean; + face: boolean; + joker: boolean; + suit: 'Clubs' | 'Diamonds' | 'Hearts' | 'Spades' | null; + url: string; } export interface StandardGameCard extends StandardCard { - flipped: boolean; + flipped: boolean; } export interface TarokkaCard { - id: string; - name: string; - card: string; - description: string; - aria: string; - back: boolean; - suit: 'Coins' | 'Glyphs' | 'High Deck' | 'Stars' | 'Swords' | null; - url: string; + id: string; + name: string; + card: string; + description: string; + aria: string; + back: boolean; + suit: 'Coins' | 'Glyphs' | 'High Deck' | 'Stars' | 'Swords' | null; + url: string; } export interface TarokkaGameCard extends TarokkaCard { - flipped: boolean; + flipped: boolean; } export interface GameState { - id: string; - players: Set; - cards: StandardGameCard[] | TarokkaGameCard[]; - lastUpdated: number; + id: string; + players: Set; + cards: StandardGameCard[] | TarokkaGameCard[]; + lastUpdated: number; } export interface GameUpdate { - id: string; - cards: StandardGameCard[] | TarokkaGameCard[]; + id: string; + cards: StandardGameCard[] | TarokkaGameCard[]; } export interface ClientUpdate { - gameID: string; - cardIndex: number; + gameID: string; + cardIndex: number; }