diff --git a/app/[gameID]/page.tsx b/app/[gameID]/page.tsx
index d2508d3..1591c04 100644
--- a/app/[gameID]/page.tsx
+++ b/app/[gameID]/page.tsx
@@ -2,40 +2,12 @@
import { useEffect, useState } from "react";
import { useParams } from 'next/navigation';
-import { socket } from "../../socket";
+import { socket } from "@/socket";
-//import Card from '@/components/Card';
+import Card from '@/components/Card';
-//import type { GameCard } from '@/types';
+import type { GameCard } from '@/types';
-export default function GamePage() {
- const { gameID } = useParams();
-
- const [cards, setCards] = useState();
-
- useEffect(() => {
- socket.emit('join', gameID);
-
- socket.on('init', data => {
- console.log('init', data);
- setCards(data.id);
- });
-
- socket.on('card-flipped', (data) => {
- console.log('>>>', data);
- setCards(data.id);
- });
-
- return () => {
- socket.off('init');
- socket.off('card-flipped');
- };
- }, []);
-
- return
A Page! {cards}
-}
-
-/*
export default function GamePage() {
const { gameID } = useParams();
@@ -91,4 +63,3 @@ export default function GamePage() {
) : null;
}
-*/
diff --git a/components/Card.tsx b/components/Card.tsx
new file mode 100644
index 0000000..6486c79
--- /dev/null
+++ b/components/Card.tsx
@@ -0,0 +1,28 @@
+'use client';
+
+type CardProps = {
+ id: string;
+ flipped: boolean;
+ onFlip: (id: string) => void;
+};
+
+export default function Card({ id, flipped, onFlip }: CardProps) {
+ return (
+ onFlip(id)}
+ >
+ {flipped ? (
+

+ ) : (
+

+ )
+ }
+
+ );
+}
+
diff --git a/const/cards.js b/const/cards.js
new file mode 100644
index 0000000..20e31e5
--- /dev/null
+++ b/const/cards.js
@@ -0,0 +1,453 @@
+const cards = [
+ {
+ "id": "1B",
+ "back": true,
+ "face": false,
+ "joker": false,
+ "suit": null,
+ "url": "/cards/1B.svg"
+ },
+ {
+ "id": "2B",
+ "back": true,
+ "face": false,
+ "joker": false,
+ "suit": null,
+ "url": "/cards/2B.svg"
+ },
+ {
+ "id": "1J",
+ "back": false,
+ "face": false,
+ "joker": true,
+ "suit": null,
+ "url": "/cards/1J.svg"
+ },
+ {
+ "id": "2J",
+ "back": false,
+ "face": false,
+ "joker": true,
+ "suit": null,
+ "url": "/cards/2J.svg"
+ },
+ {
+ "id": "AC",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/AC.svg"
+ },
+ {
+ "id": "AD",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/AD.svg"
+ },
+ {
+ "id": "AH",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/AH.svg"
+ },
+ {
+ "id": "AS",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/AS.svg"
+ },
+ {
+ "id": "2C",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/2C.svg"
+ },
+ {
+ "id": "2D",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/2D.svg"
+ },
+ {
+ "id": "2H",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/2H.svg"
+ },
+ {
+ "id": "2S",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/2S.svg"
+ },
+ {
+ "id": "3C",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/3C.svg"
+ },
+ {
+ "id": "3D",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/3D.svg"
+ },
+ {
+ "id": "3H",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/3H.svg"
+ },
+ {
+ "id": "3S",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/3S.svg"
+ },
+ {
+ "id": "4C",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/4C.svg"
+ },
+ {
+ "id": "4D",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/4D.svg"
+ },
+ {
+ "id": "4H",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/4H.svg"
+ },
+ {
+ "id": "4S",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/4S.svg"
+ },
+ {
+ "id": "5C",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/5C.svg"
+ },
+ {
+ "id": "5D",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/5D.svg"
+ },
+ {
+ "id": "5H",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/5H.svg"
+ },
+ {
+ "id": "5S",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/5S.svg"
+ },
+ {
+ "id": "6C",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/6C.svg"
+ },
+ {
+ "id": "6D",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/6D.svg"
+ },
+ {
+ "id": "6H",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/6H.svg"
+ },
+ {
+ "id": "6S",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/6S.svg"
+ },
+ {
+ "id": "7C",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/7C.svg"
+ },
+ {
+ "id": "7D",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/7D.svg"
+ },
+ {
+ "id": "7H",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/7H.svg"
+ },
+ {
+ "id": "7S",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/7S.svg"
+ },
+ {
+ "id": "8C",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/8C.svg"
+ },
+ {
+ "id": "8D",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/8D.svg"
+ },
+ {
+ "id": "8H",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/8H.svg"
+ },
+ {
+ "id": "8S",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/8S.svg"
+ },
+ {
+ "id": "9C",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/9C.svg"
+ },
+ {
+ "id": "9D",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/9D.svg"
+ },
+ {
+ "id": "9H",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/9H.svg"
+ },
+ {
+ "id": "9S",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/9S.svg"
+ },
+ {
+ "id": "10C",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/10C.svg"
+ },
+ {
+ "id": "10D",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/10D.svg"
+ },
+ {
+ "id": "10H",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/10H.svg"
+ },
+ {
+ "id": "10S",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/10S.svg"
+ },
+ {
+ "id": "JC",
+ "back": false,
+ "face": true,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/JC.svg"
+ },
+ {
+ "id": "JD",
+ "back": false,
+ "face": true,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/JD.svg"
+ },
+ {
+ "id": "JH",
+ "back": false,
+ "face": true,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/JH.svg"
+ },
+ {
+ "id": "JS",
+ "back": false,
+ "face": true,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/JS.svg"
+ },
+ {
+ "id": "KC",
+ "back": false,
+ "face": true,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/KC.svg"
+ },
+ {
+ "id": "KD",
+ "back": false,
+ "face": true,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/KD.svg"
+ },
+ {
+ "id": "KH",
+ "back": false,
+ "face": true,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/KH.svg"
+ },
+ {
+ "id": "KS",
+ "back": false,
+ "face": true,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/KS.svg"
+ },
+ {
+ "id": "QC",
+ "back": false,
+ "face": true,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/QC.svg"
+ },
+ {
+ "id": "QD",
+ "back": false,
+ "face": true,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/QD.svg"
+ },
+ {
+ "id": "QH",
+ "back": false,
+ "face": true,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/QH.svg"
+ },
+ {
+ "id": "QS",
+ "back": false,
+ "face": true,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/QS.svg"
+ }
+];
+
+export default cards;
+
diff --git a/const/cards.ts b/const/cards.ts
new file mode 100644
index 0000000..9ad1cb5
--- /dev/null
+++ b/const/cards.ts
@@ -0,0 +1,454 @@
+import { CardImage } from '@/types';
+
+const cards: CardImage[] = [
+ {
+ "id": "1B",
+ "back": true,
+ "face": false,
+ "joker": false,
+ "suit": null,
+ "url": "/cards/1B.svg"
+ },
+ {
+ "id": "2B",
+ "back": true,
+ "face": false,
+ "joker": false,
+ "suit": null,
+ "url": "/cards/2B.svg"
+ },
+ {
+ "id": "1J",
+ "back": false,
+ "face": false,
+ "joker": true,
+ "suit": null,
+ "url": "/cards/1J.svg"
+ },
+ {
+ "id": "2J",
+ "back": false,
+ "face": false,
+ "joker": true,
+ "suit": null,
+ "url": "/cards/2J.svg"
+ },
+ {
+ "id": "AC",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/AC.svg"
+ },
+ {
+ "id": "AD",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/AD.svg"
+ },
+ {
+ "id": "AH",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/AH.svg"
+ },
+ {
+ "id": "AS",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/AS.svg"
+ },
+ {
+ "id": "2C",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/2C.svg"
+ },
+ {
+ "id": "2D",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/2D.svg"
+ },
+ {
+ "id": "2H",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/2H.svg"
+ },
+ {
+ "id": "2S",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/2S.svg"
+ },
+ {
+ "id": "3C",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/3C.svg"
+ },
+ {
+ "id": "3D",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/3D.svg"
+ },
+ {
+ "id": "3H",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/3H.svg"
+ },
+ {
+ "id": "3S",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/3S.svg"
+ },
+ {
+ "id": "4C",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/4C.svg"
+ },
+ {
+ "id": "4D",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/4D.svg"
+ },
+ {
+ "id": "4H",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/4H.svg"
+ },
+ {
+ "id": "4S",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/4S.svg"
+ },
+ {
+ "id": "5C",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/5C.svg"
+ },
+ {
+ "id": "5D",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/5D.svg"
+ },
+ {
+ "id": "5H",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/5H.svg"
+ },
+ {
+ "id": "5S",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/5S.svg"
+ },
+ {
+ "id": "6C",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/6C.svg"
+ },
+ {
+ "id": "6D",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/6D.svg"
+ },
+ {
+ "id": "6H",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/6H.svg"
+ },
+ {
+ "id": "6S",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/6S.svg"
+ },
+ {
+ "id": "7C",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/7C.svg"
+ },
+ {
+ "id": "7D",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/7D.svg"
+ },
+ {
+ "id": "7H",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/7H.svg"
+ },
+ {
+ "id": "7S",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/7S.svg"
+ },
+ {
+ "id": "8C",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/8C.svg"
+ },
+ {
+ "id": "8D",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/8D.svg"
+ },
+ {
+ "id": "8H",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/8H.svg"
+ },
+ {
+ "id": "8S",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/8S.svg"
+ },
+ {
+ "id": "9C",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/9C.svg"
+ },
+ {
+ "id": "9D",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/9D.svg"
+ },
+ {
+ "id": "9H",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/9H.svg"
+ },
+ {
+ "id": "9S",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/9S.svg"
+ },
+ {
+ "id": "10C",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/10C.svg"
+ },
+ {
+ "id": "10D",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/10D.svg"
+ },
+ {
+ "id": "10H",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/10H.svg"
+ },
+ {
+ "id": "10S",
+ "back": false,
+ "face": false,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/10S.svg"
+ },
+ {
+ "id": "JC",
+ "back": false,
+ "face": true,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/JC.svg"
+ },
+ {
+ "id": "JD",
+ "back": false,
+ "face": true,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/JD.svg"
+ },
+ {
+ "id": "JH",
+ "back": false,
+ "face": true,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/JH.svg"
+ },
+ {
+ "id": "JS",
+ "back": false,
+ "face": true,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/JS.svg"
+ },
+ {
+ "id": "KC",
+ "back": false,
+ "face": true,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/KC.svg"
+ },
+ {
+ "id": "KD",
+ "back": false,
+ "face": true,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/KD.svg"
+ },
+ {
+ "id": "KH",
+ "back": false,
+ "face": true,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/KH.svg"
+ },
+ {
+ "id": "KS",
+ "back": false,
+ "face": true,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/KS.svg"
+ },
+ {
+ "id": "QC",
+ "back": false,
+ "face": true,
+ "joker": false,
+ "suit": "Clubs",
+ "url": "/cards/QC.svg"
+ },
+ {
+ "id": "QD",
+ "back": false,
+ "face": true,
+ "joker": false,
+ "suit": "Diamonds",
+ "url": "/cards/QD.svg"
+ },
+ {
+ "id": "QH",
+ "back": false,
+ "face": true,
+ "joker": false,
+ "suit": "Hearts",
+ "url": "/cards/QH.svg"
+ },
+ {
+ "id": "QS",
+ "back": false,
+ "face": true,
+ "joker": false,
+ "suit": "Spades",
+ "url": "/cards/QS.svg"
+ }
+];
+
+export default cards;
diff --git a/lib/Cards.ts b/lib/Cards.ts
new file mode 100644
index 0000000..34bca59
--- /dev/null
+++ b/lib/Cards.ts
@@ -0,0 +1,48 @@
+import getRandomItems from '../tools/getRandomItems';
+import cards from '../const/cards';
+import type { CardImage } from '@/types';
+
+export interface Options {
+ back: number;
+ jokers: boolean;
+}
+
+export interface OptionProps {
+ back?: number;
+ jokers?: boolean;
+}
+
+const DEFAULT_OPTIONS = {
+ jokers: false,
+ back: 1,
+};
+
+export default class Cards {
+ private options: Options;
+
+ private deck: CardImage[] = [];
+ private backs: CardImage[] = [];
+ private jokers: CardImage[] = [];
+
+
+ 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);
+ }
+
+ select(count: number): CardImage[] {
+ return getRandomItems(this.deck, count);
+ }
+
+ getBack(style: number): CardImage {
+ style = style || this.options.back;
+ return this.backs.find(card => card.id.startsWith(String(style))) || this.backs[0];
+ }
+
+ getJokers(): CardImage[] {
+ return this.jokers;
+ }
+}
diff --git a/lib/GameStore.ts b/lib/GameStore.ts
new file mode 100644
index 0000000..ca94dbd
--- /dev/null
+++ b/lib/GameStore.ts
@@ -0,0 +1,70 @@
+import Cards from './Cards'
+
+import { GameState } from '../types'
+
+const deck = new Cards();
+
+export default class GameStore {
+ private games: Map;
+
+ constructor() {
+ this.games = new Map();
+ }
+
+ 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(),
+ };
+
+ this.games.set(id, newGame);
+
+ return newGame;
+ }
+
+ joinGame(gameID: string, playerID: string): GameState {
+ const game = this.games.get(gameID) || this.createGame(gameID);
+
+ game.players.add(playerID);
+ game.lastUpdated = Date.now();
+
+ return game;
+ }
+
+ leaveGame(gameID: string, playerID: string): GameState {
+ const game = this.getGame(gameID);
+
+ game.players.delete(playerID);
+ game.lastUpdated = Date.now();
+
+ return game;
+ }
+
+ flipCard(gameID: string, cardID: string): GameState {
+ const game = this.getGame(gameID);
+ const card = game.cards.find(c => c.id === cardID);
+
+ if (!card) throw new Error(`Card ${cardID} not found`);
+
+ card.flipped = !card.flipped;
+ game.lastUpdated = Date.now();
+
+ return game;
+ }
+
+ getGame(gameID: string): GameState {
+ const game = this.games.get(gameID);
+
+ if (!game) throw new Error(`Game ${gameID} not found`);
+
+ return game;
+ }
+
+ deleteGame(gameID: string): void {
+ this.games.delete(gameID);
+ }
+}
diff --git a/public/cards/10C.svg b/public/cards/10C.svg
new file mode 100644
index 0000000..869f15d
--- /dev/null
+++ b/public/cards/10C.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/10D.svg b/public/cards/10D.svg
new file mode 100644
index 0000000..465d829
--- /dev/null
+++ b/public/cards/10D.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/10H.svg b/public/cards/10H.svg
new file mode 100644
index 0000000..5296a72
--- /dev/null
+++ b/public/cards/10H.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/10S.svg b/public/cards/10S.svg
new file mode 100644
index 0000000..a501258
--- /dev/null
+++ b/public/cards/10S.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/1B.svg b/public/cards/1B.svg
new file mode 100644
index 0000000..6e62286
--- /dev/null
+++ b/public/cards/1B.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/1J.svg b/public/cards/1J.svg
new file mode 100644
index 0000000..57034ab
--- /dev/null
+++ b/public/cards/1J.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/2B.svg b/public/cards/2B.svg
new file mode 100644
index 0000000..95483d9
--- /dev/null
+++ b/public/cards/2B.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/2C.svg b/public/cards/2C.svg
new file mode 100644
index 0000000..ef10c4d
--- /dev/null
+++ b/public/cards/2C.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/2D.svg b/public/cards/2D.svg
new file mode 100644
index 0000000..02bd29e
--- /dev/null
+++ b/public/cards/2D.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/2H.svg b/public/cards/2H.svg
new file mode 100644
index 0000000..faac2df
--- /dev/null
+++ b/public/cards/2H.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/2J.svg b/public/cards/2J.svg
new file mode 100644
index 0000000..381cb58
--- /dev/null
+++ b/public/cards/2J.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/2S.svg b/public/cards/2S.svg
new file mode 100644
index 0000000..c897b2b
--- /dev/null
+++ b/public/cards/2S.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/3C.svg b/public/cards/3C.svg
new file mode 100644
index 0000000..c7a5903
--- /dev/null
+++ b/public/cards/3C.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/3D.svg b/public/cards/3D.svg
new file mode 100644
index 0000000..2681f7c
--- /dev/null
+++ b/public/cards/3D.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/3H.svg b/public/cards/3H.svg
new file mode 100644
index 0000000..86f0bab
--- /dev/null
+++ b/public/cards/3H.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/3S.svg b/public/cards/3S.svg
new file mode 100644
index 0000000..b18e2ff
--- /dev/null
+++ b/public/cards/3S.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/4C.svg b/public/cards/4C.svg
new file mode 100644
index 0000000..ebd9cec
--- /dev/null
+++ b/public/cards/4C.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/4D.svg b/public/cards/4D.svg
new file mode 100644
index 0000000..ddcafa4
--- /dev/null
+++ b/public/cards/4D.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/4H.svg b/public/cards/4H.svg
new file mode 100644
index 0000000..17cd88b
--- /dev/null
+++ b/public/cards/4H.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/4S.svg b/public/cards/4S.svg
new file mode 100644
index 0000000..7223961
--- /dev/null
+++ b/public/cards/4S.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/5C.svg b/public/cards/5C.svg
new file mode 100644
index 0000000..03e5749
--- /dev/null
+++ b/public/cards/5C.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/5D.svg b/public/cards/5D.svg
new file mode 100644
index 0000000..51579d8
--- /dev/null
+++ b/public/cards/5D.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/5H.svg b/public/cards/5H.svg
new file mode 100644
index 0000000..a94b79b
--- /dev/null
+++ b/public/cards/5H.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/5S.svg b/public/cards/5S.svg
new file mode 100644
index 0000000..f483b9b
--- /dev/null
+++ b/public/cards/5S.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/6C.svg b/public/cards/6C.svg
new file mode 100644
index 0000000..d4ec280
--- /dev/null
+++ b/public/cards/6C.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/6D.svg b/public/cards/6D.svg
new file mode 100644
index 0000000..5ebe293
--- /dev/null
+++ b/public/cards/6D.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/6H.svg b/public/cards/6H.svg
new file mode 100644
index 0000000..120274e
--- /dev/null
+++ b/public/cards/6H.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/6S.svg b/public/cards/6S.svg
new file mode 100644
index 0000000..6a753e8
--- /dev/null
+++ b/public/cards/6S.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/7C.svg b/public/cards/7C.svg
new file mode 100644
index 0000000..801cef0
--- /dev/null
+++ b/public/cards/7C.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/7D.svg b/public/cards/7D.svg
new file mode 100644
index 0000000..1c3e2af
--- /dev/null
+++ b/public/cards/7D.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/7H.svg b/public/cards/7H.svg
new file mode 100644
index 0000000..90d4896
--- /dev/null
+++ b/public/cards/7H.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/7S.svg b/public/cards/7S.svg
new file mode 100644
index 0000000..74d127d
--- /dev/null
+++ b/public/cards/7S.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/8C.svg b/public/cards/8C.svg
new file mode 100644
index 0000000..69653e2
--- /dev/null
+++ b/public/cards/8C.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/8D.svg b/public/cards/8D.svg
new file mode 100644
index 0000000..78e308a
--- /dev/null
+++ b/public/cards/8D.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/8H.svg b/public/cards/8H.svg
new file mode 100644
index 0000000..68de1b2
--- /dev/null
+++ b/public/cards/8H.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/8S.svg b/public/cards/8S.svg
new file mode 100644
index 0000000..9e5fa8c
--- /dev/null
+++ b/public/cards/8S.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/9C.svg b/public/cards/9C.svg
new file mode 100644
index 0000000..09793eb
--- /dev/null
+++ b/public/cards/9C.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/9D.svg b/public/cards/9D.svg
new file mode 100644
index 0000000..b7bafc1
--- /dev/null
+++ b/public/cards/9D.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/9H.svg b/public/cards/9H.svg
new file mode 100644
index 0000000..981f3f6
--- /dev/null
+++ b/public/cards/9H.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/9S.svg b/public/cards/9S.svg
new file mode 100644
index 0000000..6d2edcf
--- /dev/null
+++ b/public/cards/9S.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/AC.svg b/public/cards/AC.svg
new file mode 100644
index 0000000..8fcb90a
--- /dev/null
+++ b/public/cards/AC.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/AD.svg b/public/cards/AD.svg
new file mode 100644
index 0000000..6092846
--- /dev/null
+++ b/public/cards/AD.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/AH.svg b/public/cards/AH.svg
new file mode 100644
index 0000000..5d43449
--- /dev/null
+++ b/public/cards/AH.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/AS.svg b/public/cards/AS.svg
new file mode 100644
index 0000000..f7b68f0
--- /dev/null
+++ b/public/cards/AS.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/JC.svg b/public/cards/JC.svg
new file mode 100644
index 0000000..611c487
--- /dev/null
+++ b/public/cards/JC.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/JD.svg b/public/cards/JD.svg
new file mode 100644
index 0000000..4b73a85
--- /dev/null
+++ b/public/cards/JD.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/JH.svg b/public/cards/JH.svg
new file mode 100644
index 0000000..a316d9c
--- /dev/null
+++ b/public/cards/JH.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/JS.svg b/public/cards/JS.svg
new file mode 100644
index 0000000..e0b8ff0
--- /dev/null
+++ b/public/cards/JS.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/KC.svg b/public/cards/KC.svg
new file mode 100644
index 0000000..3e659f3
--- /dev/null
+++ b/public/cards/KC.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/KD.svg b/public/cards/KD.svg
new file mode 100644
index 0000000..312c33d
--- /dev/null
+++ b/public/cards/KD.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/KH.svg b/public/cards/KH.svg
new file mode 100644
index 0000000..163b91b
--- /dev/null
+++ b/public/cards/KH.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/KS.svg b/public/cards/KS.svg
new file mode 100644
index 0000000..1582a52
--- /dev/null
+++ b/public/cards/KS.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/QC.svg b/public/cards/QC.svg
new file mode 100644
index 0000000..1ace1e8
--- /dev/null
+++ b/public/cards/QC.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/QD.svg b/public/cards/QD.svg
new file mode 100644
index 0000000..c1b8eab
--- /dev/null
+++ b/public/cards/QD.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/QH.svg b/public/cards/QH.svg
new file mode 100644
index 0000000..0097932
--- /dev/null
+++ b/public/cards/QH.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/cards/QS.svg b/public/cards/QS.svg
new file mode 100644
index 0000000..fb09b8b
--- /dev/null
+++ b/public/cards/QS.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/public/file.svg b/public/file.svg
new file mode 100644
index 0000000..004145c
--- /dev/null
+++ b/public/file.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/globe.svg b/public/globe.svg
new file mode 100644
index 0000000..567f17b
--- /dev/null
+++ b/public/globe.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/next.svg b/public/next.svg
new file mode 100644
index 0000000..5174b28
--- /dev/null
+++ b/public/next.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/vercel.svg b/public/vercel.svg
new file mode 100644
index 0000000..7705396
--- /dev/null
+++ b/public/vercel.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/window.svg b/public/window.svg
new file mode 100644
index 0000000..b2b2a44
--- /dev/null
+++ b/public/window.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/server.ts b/server.ts
index 77ca7ac..98c64ed 100644
--- a/server.ts
+++ b/server.ts
@@ -2,7 +2,7 @@ import next from 'next';
import { createServer } from 'http';
import { Server as SocketIOServer, type Socket } from 'socket.io';
-//import GameStore from '@/lib/GameStore';
+import GameStore from './lib/GameStore';
const dev = process.env.NODE_ENV !== 'production';
const hostname = 'localhost';
@@ -12,7 +12,7 @@ const port = 3000;
const app = next({ dev, hostname, port });
const handler = app.getRequestHandler();
-//const gameStore = new GameStore();
+const gameStore = new GameStore();
app.prepare().then(() => {
const httpServer = createServer(handler);
@@ -24,22 +24,20 @@ app.prepare().then(() => {
socket.on('join', (gameID) => {
socket.join(gameID);
- //const game = gameStore.joinGame(gameID, socket.id);
+ const game = gameStore.joinGame(gameID, socket.id);
console.log(`Socket ${socket.id} joined game ${gameID}`)
- //socket.emit('init', { cards: game.cards });
- socket.emit('init', { id: socket.id });
+ socket.emit('init', { cards: game.cards });
})
socket.on('flip-card', ({ gameID, cardID }) => {
console.log('Card flipped:', { gameID, cardID });
- //const game = gameStore.flipCard(gameID, cardID);
- //io.to(gameID).emit('card-flipped', { gameID, cards: game.cards });
- io.to(gameID).emit('card-flipped', { id: socket.id });
+ const game = gameStore.flipCard(gameID, cardID);
+ io.to(gameID).emit('card-flipped', { gameID, cards: game.cards });
});
- socket.on('disconnect', (obj) => {
+ socket.on('disconnect', () => {
console.log(`Client disconnected: ${socket.id}`);
});
});
diff --git a/tools/getRandomItems.js b/tools/getRandomItems.js
new file mode 100644
index 0000000..d67c0db
--- /dev/null
+++ b/tools/getRandomItems.js
@@ -0,0 +1,12 @@
+export default function getRandomItems(items, count) {
+ 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]];
+ }
+
+ return count > shuffled.length ? shuffled : shuffled.slice(0, count);
+}
+
diff --git a/tools/getRandomItems.ts b/tools/getRandomItems.ts
new file mode 100644
index 0000000..ac5ed52
--- /dev/null
+++ b/tools/getRandomItems.ts
@@ -0,0 +1,11 @@
+export default function getRandomItems(items: T[], count: number): T[] {
+ 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]];
+ }
+
+ return count > shuffled.length ? shuffled : shuffled.slice(0, count);
+}
diff --git a/tools/simpleID.ts b/tools/simpleID.ts
new file mode 100644
index 0000000..581c0f8
--- /dev/null
+++ b/tools/simpleID.ts
@@ -0,0 +1,8 @@
+import { customAlphabet } from 'nanoid';
+
+const alphabet =
+ '0123456789abcdefghijklmnopqrstuvwxyz';
+
+const generateID = (length: number = 6) => customAlphabet(alphabet, length);
+
+export default generateID;
diff --git a/tsconfig.json b/tsconfig.json
index 0eae8b9..2c24990 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -19,6 +19,9 @@
"name": "next"
}
],
+ "paths": {
+ "@/*": ["./*"]
+ },
"strictNullChecks": true
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
diff --git a/types/index.ts b/types/index.ts
new file mode 100644
index 0000000..e035936
--- /dev/null
+++ b/types/index.ts
@@ -0,0 +1,19 @@
+export interface CardImage {
+ id: string;
+ back: boolean;
+ face: boolean;
+ joker: boolean;
+ suit: 'Clubs' | 'Diamonds' | 'Hearts' | 'Spades' | null;
+ url: string;
+}
+
+export interface GameCard extends CardImage {
+ flipped: boolean;
+}
+
+export interface GameState {
+ id: string;
+ players: Set;
+ cards: GameCard[];
+ lastUpdated: number;
+}