Skip to content

Commit

Permalink
throw card DRAFT
Browse files Browse the repository at this point in the history
  • Loading branch information
criticic committed Jun 2, 2024
1 parent a4574ab commit 96835ef
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 9 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
4 changes: 2 additions & 2 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
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
38 changes: 38 additions & 0 deletions backend/uno-game-engine/events/throwCard.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// TODO: Currently the test fails, because
// we should have a way to generate the set of cards,
// and add them to the game.

// import { createGame, retrieveGame } from '../../gameStore';
// import { handleEvent } from "../gameEvents";

// function CreateUNOTestGame() {
// const gameId = createGame();
// const game = retrieveGame(gameId);
// if (!game) {
// throw new Error('Game not found');
// }

// game.addPlayer({ id: '1', cards: [] });
// game.addPlayer({ id: '2', cards: [] });
// game.startGame();
// game.allotCards();
// return game;
// }

// test('Throw a card', () => {
// const game = CreateUNOTestGame();
// const player = game.players[0];
// const card = player.cards[0];
// const event: GameEvent = {
// type: 'THROW_CARD',
// playerId: player.id,
// data: {
// card,
// },
// };
// const result = handleEvent(game, event);
// expect(result.type).toBe('SUCCESS');
// expect(player.cards.length).toBe(6);
// expect(game.thrownCards.length).toBe(1);
// expect(game.lastThrownCard).toBe(card);
// });
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 96835ef

Please sign in to comment.