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; +}