From 09b7759c8e5197c6b1fdc45cdcc7ac1a2ac987f3 Mon Sep 17 00:00:00 2001 From: Kislay Date: Sat, 1 Jun 2024 13:39:04 +0530 Subject: [PATCH 1/6] api: Implement server side of long polling. This commit adds a handler for GET /events which stores the response object for the client for emitting events in the future. --- backend/eventRecipients.ts | 18 ++++++++++++++++++ backend/routes/gameRoutes.js | 10 ++++++++++ 2 files changed, 28 insertions(+) create mode 100644 backend/eventRecipients.ts diff --git a/backend/eventRecipients.ts b/backend/eventRecipients.ts new file mode 100644 index 0000000..3861c85 --- /dev/null +++ b/backend/eventRecipients.ts @@ -0,0 +1,18 @@ +// this module is responsible for handling the clients currently connected to the server. +// It stores the clients in a Map object, where the key is the client's user_id and the value is the client's http response object. + +import { Response } from 'express'; + +const clients = new Map(); + +export function addClient(userId: string, res: Response) { + clients.set(userId, res); +} + +export function removeClient(userId: string) { + clients.delete(userId); +} + +export function getClient(userId: string) { + return clients.get(userId); +} diff --git a/backend/routes/gameRoutes.js b/backend/routes/gameRoutes.js index e69de29..fa43827 100644 --- a/backend/routes/gameRoutes.js +++ b/backend/routes/gameRoutes.js @@ -0,0 +1,10 @@ +import express from 'express'; +import { addClient } from '../eventRecipients'; +const router = express.Router(); + +router.get('/events', (req, res) => { + addClient('user_id', res); +}); + +// the post handler should retrieve the game the user is currently in, and update the game state. +// The request body contains the event data, as described in ARCHITECTURE.md From 193329e15203c1da55ca0b13828c8c42c3587f8f Mon Sep 17 00:00:00 2001 From: Kislay Date: Sat, 1 Jun 2024 13:40:44 +0530 Subject: [PATCH 2/6] engine: Implement structure for game event handling. Created a module to deal with executing events on the game object. --- backend/uno-game-engine/engine.ts | 5 +++++ backend/uno-game-engine/gameEvents.ts | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 backend/uno-game-engine/gameEvents.ts diff --git a/backend/uno-game-engine/engine.ts b/backend/uno-game-engine/engine.ts index 2af035d..17bf2fe 100644 --- a/backend/uno-game-engine/engine.ts +++ b/backend/uno-game-engine/engine.ts @@ -1,4 +1,5 @@ import { getShuffledCardDeck } from './deck'; +import { handleEvent } from './gameEvents'; const NUM_CARDS_PER_PLAYER = 7; @@ -52,4 +53,8 @@ export class GameEngine { .find((p: Player) => p.id === player.id) .cards.push(this.cardDeck.pop()); } + dispatchEvent(event: GameEvent) { + // handle different types of events based on event.type + handleEvent(this, event); + } } diff --git a/backend/uno-game-engine/gameEvents.ts b/backend/uno-game-engine/gameEvents.ts new file mode 100644 index 0000000..e8126b8 --- /dev/null +++ b/backend/uno-game-engine/gameEvents.ts @@ -0,0 +1,22 @@ +// this module houses the handlers for various game events. + +import { type GameEngine } from './engine'; + +type GameEventHandler = (game: GameEngine, event: GameEvent) => EventResult; + +const map = new Map(); + +export function registerEventHandler( + eventType: GameEventType, + handler: GameEventHandler +) { + map.set(eventType, handler); +} + +export function handleEvent(game: GameEngine, event: GameEvent): EventResult { + const handler = map.get(event.type); + if (!handler) { + return { type: 'ERROR', message: 'Invalid event type' }; + } + return handler(game, event); +} From 44927c83cd459365a6999107f391147222bf8672 Mon Sep 17 00:00:00 2001 From: Kislay Date: Sat, 1 Jun 2024 13:44:12 +0530 Subject: [PATCH 3/6] prettier: Use LF for line endings. --- backend/.prettierrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/.prettierrc b/backend/.prettierrc index a94e076..fe1af8e 100644 --- a/backend/.prettierrc +++ b/backend/.prettierrc @@ -3,5 +3,5 @@ "trailingComma": "es5", "tabWidth": 4, "semi": true, - "endOfLine": "crlf" + "endOfLine": "lf" } From f05595ca4143420a34ad4ad01bea35a460bfd5ac Mon Sep 17 00:00:00 2001 From: Divyansh Seth Date: Tue, 28 May 2024 17:11:16 +0530 Subject: [PATCH 4/6] deck: Assigned unique id to each card. - Created an ID in the format `card-type-color-value`. - Added a `sameCardCount` array to uniquely identify cards of the same type and color. - Generated a unique ID using the format `card-type-color-value-sameCardCount[id]`. Fixes #4 --- backend/src/types.d.ts | 2 +- backend/uno-game-engine/deck.ts | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/backend/src/types.d.ts b/backend/src/types.d.ts index a606980..0ac9c89 100644 --- a/backend/src/types.d.ts +++ b/backend/src/types.d.ts @@ -14,7 +14,7 @@ type UNOCard = { type: CardType; color: CardColor; value: CardValue; - id: undefined; + id: string; }; type Player = { diff --git a/backend/uno-game-engine/deck.ts b/backend/uno-game-engine/deck.ts index c25ebe0..a4aacce 100644 --- a/backend/uno-game-engine/deck.ts +++ b/backend/uno-game-engine/deck.ts @@ -16,6 +16,7 @@ const values = [ ]; const specialCards = ['wild', 'draw4']; const deck = []; +const sameCardCount = []; // to keep track of same cards in assigning unique id to each card /** * In a standard UNO deck, there are 108 cards. Here's the breakdown: @@ -60,8 +61,13 @@ export function makeCard( color: CardColor, value: CardValue ): UNOCard { - //todo: Implement unique identification of cards by assigning an id to each card - return { type, color, value, id: undefined }; + const id = `card-${type}-${color}-${value}`; + + if (!sameCardCount[id]) sameCardCount[id] = 0; + sameCardCount[id]++; // increment the count of same cards to assign unique id + + const uid = `${id}-${sameCardCount[id]}`; + return { type, color, value, id: uid }; } /** From ef2dc47626c6f8457c6c1629748da42c4af09d0b Mon Sep 17 00:00:00 2001 From: Divyansh Seth Date: Sat, 1 Jun 2024 00:27:48 +0530 Subject: [PATCH 5/6] deck: Implemented Fisher-Yates shuffling algorithm to shuffle the deck Fixes: #2 --- backend/tests/deck.test.ts | 23 ++++++++++++++++++++++- backend/uno-game-engine/deck.ts | 9 ++++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/backend/tests/deck.test.ts b/backend/tests/deck.test.ts index 98e629a..d54e7b3 100644 --- a/backend/tests/deck.test.ts +++ b/backend/tests/deck.test.ts @@ -1,7 +1,28 @@ -import { makeCard } from '../uno-game-engine/deck'; +import { shuffle, makeCard } from '../uno-game-engine/deck'; describe('testing deck.ts', () => { test('makeCard', () => { const card = makeCard('number', 'blue', '3'); expect(card.color).toBe('blue'); }); }); +describe('shuffle function', () => { + test('should change order of elements', () => { + // Create a mock deck + const deck = [ + makeCard('number', 'red', '1'), + makeCard('number', 'blue', '2'), + makeCard('number', 'red', '1'), + makeCard('number', 'yellow', '1'), + ]; + + // Create a copy of the deck for comparison + const originalDeck = [...deck]; + shuffle(deck); + + // Check that the order of elements has changed + const orderChanged = deck.some( + (card, index) => card !== originalDeck[index] + ); + expect(orderChanged).toBe(true); + }); +}); diff --git a/backend/uno-game-engine/deck.ts b/backend/uno-game-engine/deck.ts index a4aacce..8a6c19e 100644 --- a/backend/uno-game-engine/deck.ts +++ b/backend/uno-game-engine/deck.ts @@ -74,7 +74,10 @@ export function makeCard( * This function shuffles the elements of the given array *in place* . The function behaves in a type-agnostic way. * Time complexity: O(n) */ -export function shuffle(deck: Array) { - //todo: Implement a generic shuffling algorithm - [deck[0], deck[1]] = [deck[1], deck[0]]; +export function shuffle(deck: Array) { + // Fisher-Yates shuffle algorithm to shuffle card deck + for (let i = deck.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * i); + [deck[i], deck[j]] = [deck[j], deck[i]]; + } } From a4574ab3966c3e761bd6a08fdcce623042897da4 Mon Sep 17 00:00:00 2001 From: Kislay Date: Sun, 2 Jun 2024 11:32:05 +0530 Subject: [PATCH 6/6] lint: Fix prettier and eslint conflict. The two systems conflicted over indentations in switch case. --- backend/.eslintrc.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/backend/.eslintrc.json b/backend/.eslintrc.json index f3b3648..70fb569 100644 --- a/backend/.eslintrc.json +++ b/backend/.eslintrc.json @@ -11,7 +11,13 @@ }, "plugins": ["@typescript-eslint"], "rules": { - "indent": ["error", 4], + "indent": [ + "error", + 4, + { + "SwitchCase": 1 + } + ], "quotes": ["error", "single"], "semi": ["error", "always"] }