Skip to content

Commit

Permalink
gameEvent: Implement Throw Card Event
Browse files Browse the repository at this point in the history
Adds the throw card event handler.

Fixes #44

Signed-off-by: Sagnik Mandal <[email protected]>
  • Loading branch information
criticic committed Jun 3, 2024
1 parent dfd80ee commit 273d3d5
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 11 deletions.
12 changes: 9 additions & 3 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,17 @@ format of the message is:

```json
{
"type": "DRAW_CARD",
"player": "player1",
"type": "THROW_CARD",
"playerId": "1",
"data": {
"cardID": "red-5"
"card": {
"color": "red",
"value": "5",
"type": "number",
"id": "card-number-red-5"
}
}

}
```
Other possible values for `type` are `THROW_CARD`, `ANNOUNCE_UNO`, etc.
Expand Down
2 changes: 1 addition & 1 deletion backend/eventRecipients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ export function scheduleSend(clientId: ClientId, event: AppEvent) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function doSendEvent(clientId: ClientId, event: AppEvent) {
//todo: Send all the events in the queue to the client, only if the response object is available.
}
}
6 changes: 3 additions & 3 deletions backend/src/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// 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 CardType = 'number' | 'special';

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';
Expand Down Expand Up @@ -46,5 +46,5 @@ type GameEvent =
};

// Represent all the events that can be sent to the client
type AppEvent = GameEvent
type AppEvent = GameEvent;
//todo: Add more events
16 changes: 12 additions & 4 deletions backend/uno-game-engine/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,19 @@ export class GameEngine {
this.currentPlayerIndex =
(this.currentPlayerIndex + this.direction) % this.players.length;
}
drawCardFromDeck(player: Player) {
drawCardFromDeck(player: Player, numCards = 1) {
//todo: Handle the case when the deck is empty and we have to move the thrown cards back to the deck
this.players
.find((p: Player) => p.id === player.id)
.cards.push(this.cardDeck.pop());
// this.players
// .find((p: Player) => p.id === player.id)
// .cards.push(this.cardDeck.pop());
const currentPlayer = this.players.find((p) => p.id === player.id);
if (!currentPlayer) {
throw new Error('Player not found');
}

for (let i = 0; i < numCards; i++) {
currentPlayer.cards.push(this.cardDeck.pop());
}
}
dispatchEvent(event: GameEvent) {
// handle different types of events based on event.type
Expand Down
84 changes: 84 additions & 0 deletions backend/uno-game-engine/events/throwCard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { registerEventHandler } from '../gameEvents';
import { GameEngine } from '../engine';

function findPlayer(game: GameEngine, playerId: string) {
return game.players.find((p) => p.id === playerId);
}

function findCard(player, cardId: string) {
return player.cards.find((c) => c.id === cardId);
}

export function throwCard(game: GameEngine, event: GameEvent): EventResult {
const { currentPlayerIndex, players } = game;
const currentPlayer = players[currentPlayerIndex];

// check if the player is the current player
if (currentPlayer.id !== event.playerId) {
return { type: 'ERROR', message: 'It is not your turn' };
}

// check if the card is present in the player's hand
const player = findPlayer(game, event.playerId);
if (!player) {
return { type: 'ERROR', message: 'Player not found' };
}
const card = findCard(player, event.data.card.id);
if (!card) {
return { type: 'ERROR', message: 'Card not found' };
}

// check if the card can be thrown
if (game.lastThrownCard && !canThrowCard(game.lastThrownCard, card)) {
return { type: 'ERROR', message: 'Cannot throw this card' };
}

player.cards = player.cards.filter((c) => c.id !== card.id);

game.thrownCards.push(card);
game.lastThrownCard = card;

game.nextPlayer();

// handle special cards
if (card.type === 'special') {
handleSpecialCard(game, card);
}

return { type: 'SUCCESS', message: 'Card thrown successfully' };
}

function canThrowCard(lastThrownCard: UNOCard, card: UNOCard): boolean {
return (
lastThrownCard.color === card.color ||
lastThrownCard.value === card.value ||
card.color === 'wild'
);
}

function handleSpecialCard(game: GameEngine, card: UNOCard) {
switch (card.value) {
case 'skip':
game.nextPlayer();
break;
case 'reverse':
game.direction *= -1;
break;
case 'draw2':
{
const nextPlayer = game.players[game.currentPlayerIndex];
game.drawCardFromDeck(nextPlayer, 2);
}
break;
case 'draw4':
{
const currentPlayer = game.players[game.currentPlayerIndex];
game.drawCardFromDeck(currentPlayer, 4);
}
break;
default:
break;
}
}

registerEventHandler('THROW_CARD', throwCard);

0 comments on commit 273d3d5

Please sign in to comment.