From 9cc34c41fef8b9853b01ad4d20f68ca208c85214 Mon Sep 17 00:00:00 2001 From: Kislay Date: Sat, 1 Jun 2024 13:39:04 +0530 Subject: [PATCH 1/2] server : Added wild into the CardColors Added wild color for wild cards like colchange and draw4 cards these changes are made in types.d.ts --- backend/.eslintrc.json | 8 ++- backend/.prettierrc | 2 +- backend/eventRecipients.ts | 18 ++++++ backend/routes/gameRoutes.js | 10 +++ backend/src/types.d.ts | 11 +--- backend/tests/deck.test.ts | 23 ++++++- backend/uno-game-engine/deck.ts | 88 +++++++++++++++++---------- backend/uno-game-engine/engine.ts | 5 ++ backend/uno-game-engine/gameEvents.ts | 22 +++++++ 9 files changed, 144 insertions(+), 43 deletions(-) create mode 100644 backend/eventRecipients.ts create mode 100644 backend/uno-game-engine/gameEvents.ts 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"] } 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" } 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 diff --git a/backend/src/types.d.ts b/backend/src/types.d.ts index a606980..a5b2c1f 100644 --- a/backend/src/types.d.ts +++ b/backend/src/types.d.ts @@ -1,9 +1,6 @@ -// We declare those types which are used throughout the application here. -// For types that are used only in one file, we can declare them in that file itself. - type CardType = 'number' | 'special' | 'wild'; -type CardColor = 'red' | 'blue' | 'green' | 'yellow'; +type CardColor = 'red' | 'blue' | 'green' | 'yellow' | 'wild'; type SpecialCardNames = 'skip' | 'reverse' | 'draw2' | 'draw4' | 'colchange'; type CardNumbers = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'; @@ -14,7 +11,7 @@ type UNOCard = { type: CardType; color: CardColor; value: CardValue; - id: undefined; + id: string; }; type Player = { @@ -27,10 +24,6 @@ type EventResult = { message: string; }; -declare global { - type GameEngine = import('./engine').GameEngine; -} - type GameEventType = 'DRAW_CARD' | 'THROW_CARD'; type GameEvent = 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 c25ebe0..739e26c 100644 --- a/backend/uno-game-engine/deck.ts +++ b/backend/uno-game-engine/deck.ts @@ -1,5 +1,6 @@ -const colors: Array = ['red', 'yellow', 'green', 'blue']; -const values = [ +const colors: CardColor[] = ['red', 'green', 'blue', 'yellow']; + +const values: CardValue[] = [ '0', '1', '2', @@ -14,39 +15,56 @@ const values = [ 'reverse', 'draw2', ]; -const specialCards = ['wild', 'draw4']; -const deck = []; - -/** - * In a standard UNO deck, there are 108 cards. Here's the breakdown: +const specialCards: CardValue[] = ['colchange', 'draw4']; +const sameCardCount = []; +/** +* In a standard UNO deck, there are 108 cards. Here's the breakdown: - colors: These are the four colors of the cards in the deck: red, yellow, green, and blue. -- values: These are the values that can be on the cards. Each color has one '0' card and - two of each of the other number cards ('1' through '9'). - Each color also has two 'skip', 'reverse', and 'draw2' cards. +- values: These are the values that can be on the cards. Each color has one '0' card and +two of each of the other number cards ('1' through '9'). +Each color also has two 'skip', 'reverse', and 'draw2' cards. - specialCards: These are the black cards that can be played on any color. - There are four 'wild' cards that allow the player to choose the color that - continues play, and four 'draw4' cards that act like 'wild' cards but also - force the next player to draw four cards. +There are four 'wild' cards that allow the player to choose the color that +continues play, and four 'draw4' cards that act like 'wild' cards but also +force the next player to draw four cards. - So, in total, for each color there are 19 number cards, 2 'skip' cards, - 2 'reverse' cards, and 2 'draw2' cards, which makes 25 cards per color. - Multiply that by 4 (for the four colors) and add the 8 special cards, - and you get 108 cards. +So, in total, for each color there are 19 number cards, 2 'skip' cards, +2 'reverse' cards, and 2 'draw2' cards, which makes 25 cards per color. +Multiply that by 4 (for the four colors) and add the 8 special cards, +and you get 108 cards. - This function returns a shuffled deck of 108 UNO cards. Each card is an object with a color and a value. - The function makeCard is used to make the card objects. - @returns {Array} deck - An array of 108 UNO cards. - */ +This function returns a shuffled deck of 108 UNO cards. Each card is an object with a color and a value. +The function makeCard is used to make the card objects. +@returns {Array} deck - An array of 108 UNO cards. +*/ export function getShuffledCardDeck(): Array { - const deck = []; - // todo: Implement the card generation logic - // dummy code: - // deck.push(makeCard('special', 'wild', 'wild')) - // deck.push(makeCard('number', 'red', '0')) + const deck: Array = []; + for (let col of colors) { + for (let val of values) { + if (!isNaN(Number(val))) { + for (let i = 0; i < 2; i++) { + deck.push(makeCard('number', col, val)); + if (val === '0') break; + } + } else { + for (let i = 0; i < 2; i++) { + deck.push(makeCard('special', col, val)); + } + } + } + } + + // generating special cards + for (let sc of specialCards) { + for (let i = 0; i < 4; i++) { + deck.push(makeCard('wild', 'wild', sc)); + } + } + // shuffling the deck shuffle(deck); return deck; } @@ -60,15 +78,23 @@ 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 }; } /** * 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]]; + } } 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 b0ca51d07f0790acb48676720d63a28f2825cbbd Mon Sep 17 00:00:00 2001 From: Abhishek Date: Sun, 2 Jun 2024 15:38:32 +0530 Subject: [PATCH 2/2] backend:deck.ts This commit involves one preperatory commit : adding wild color to CardColors Added deck generation function to initialize the deck according to given constraints The function generates cards and push them the deck array generating cards of each color and also wild cards Also have checked the deck composition with jest tests Fixes :#1 --- backend/tests/deck.test.ts | 38 ++++++++++++++++++++++++++++++++- backend/uno-game-engine/deck.ts | 1 + 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/backend/tests/deck.test.ts b/backend/tests/deck.test.ts index d54e7b3..efc6dd4 100644 --- a/backend/tests/deck.test.ts +++ b/backend/tests/deck.test.ts @@ -1,9 +1,45 @@ -import { shuffle, makeCard } from '../uno-game-engine/deck'; +import { + getShuffledCardDeck, + makeCard, + shuffle, +} from '../uno-game-engine/deck'; describe('testing deck.ts', () => { test('makeCard', () => { const card = makeCard('number', 'blue', '3'); expect(card.color).toBe('blue'); }); + test('getShuffledDeck', () => { + const deck = getShuffledCardDeck(); + console.log(deck); + }); +}); +test('validateDeckComposition', () => { + const deck = getShuffledCardDeck(); + + const colorCount = { red: 0, yellow: 0, green: 0, blue: 0 }; + const wildCount = { wild: 0 }; + let totalCards = 0; + + for (const card of deck) { + if (card.color in colorCount) { + colorCount[card.color]++; + } else if (card.color === 'wild') { + wildCount.wild++; + } + totalCards++; + } + + // Verify total number of cards + expect(totalCards).toBe(108); + + // Verify number of cards of each color + expect(colorCount.red).toBe(25); + expect(colorCount.yellow).toBe(25); + expect(colorCount.green).toBe(25); + expect(colorCount.blue).toBe(25); + + // Verify number of wild cards + expect(wildCount.wild).toBe(8); }); describe('shuffle function', () => { test('should change order of elements', () => { diff --git a/backend/uno-game-engine/deck.ts b/backend/uno-game-engine/deck.ts index 739e26c..a83ebcd 100644 --- a/backend/uno-game-engine/deck.ts +++ b/backend/uno-game-engine/deck.ts @@ -15,6 +15,7 @@ const values: CardValue[] = [ 'reverse', 'draw2', ]; + const specialCards: CardValue[] = ['colchange', 'draw4']; const sameCardCount = []; /**