SafetyVantage Coding Challenge
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

116 lines
3.3 KiB

import { EventProcessor, IEventProcessor } from "../utils/event_processor.js";
import { GameStartedEvent } from "../game/events/game_started_event.js";
import { PlayerJoinedEvent } from "../game/events/player_joined_event.js";
import { PlayerRolledEvent } from "../game/events/player_rolled_event.js";
import { Game } from "../game/game.js";
import { Board } from "../game/board.js";
import { Player } from "../game/player.js";
import { MaxPlayersExceededError } from "./errors/max_players_exceeded_error.js";
import { GameNotStartedError } from "./errors/game_not_started_error.js";
import { DuplicateGameError } from "./errors/duplicate_game_error.js";
import { TournamentIncompleteError } from "./errors/tournament_incomplete_error.js";
// Provide Tournament class with additional EventProcessor methods
export interface Tournament extends IEventProcessor {}
interface ITournamentOptions {
maxPlayersPerGame: number;
}
@EventProcessor.watch
export class Tournament implements ITournamentOptions {
gamesMap = new Map<number, Game>();
playersMap = new Map<number, Player>();
maxPlayersPerGame = 2;
constructor(public board: Board, options: Partial<ITournamentOptions> = {}) {
Object.assign(this, options);
}
get players() {
return Array.from(this.playersMap.values());
}
get games() {
return Array.from(this.gamesMap.values());
}
@EventProcessor.handle(GameStartedEvent)
onGameStarted(gameStartedEvent: GameStartedEvent) {
this.newGame(gameStartedEvent.gameId);
}
@EventProcessor.handle(PlayerJoinedEvent)
onPlayerJoined(playerJoinedEvent: PlayerJoinedEvent) {
const game = this.getGame(playerJoinedEvent.gameId);
if (!game) {
throw new GameNotStartedError(
playerJoinedEvent.gameId,
playerJoinedEvent.playerId
);
}
const player = this.getOrCreatePlayer(playerJoinedEvent.playerId);
if (game.playerCount >= this.maxPlayersPerGame) {
throw new MaxPlayersExceededError(
game.gameId,
player.playerId,
this.maxPlayersPerGame
);
}
player.join(game);
game.addPlayer(player);
}
@EventProcessor.handle(PlayerRolledEvent)
onPlayerRolled(playerRolledEvent: PlayerRolledEvent) {
const game = this.getGame(playerRolledEvent.gameId);
if (!game) {
throw new GameNotStartedError(
playerRolledEvent.gameId,
playerRolledEvent.playerId
);
}
const player = this.getOrCreatePlayer(playerRolledEvent.playerId);
player.rollDice(game, playerRolledEvent.roll);
}
getGame(gameId: number) {
return this.gamesMap.get(gameId);
}
newGame(gameId: number) {
if (this.gamesMap.has(gameId)) {
throw new DuplicateGameError(gameId);
}
const game = new Game(gameId, this.board);
this.gamesMap.set(gameId, game);
return game;
}
getOrCreatePlayer(playerId: number) {
let player = this.playersMap.get(playerId);
if (!player) {
player = new Player(playerId);
this.playersMap.set(playerId, player);
}
return player;
}
requireCompletion() {
const incompleteGameIds = Array.from(this.gamesMap.values())
.filter((game: Game) => !game.isComplete)
.map((game: Game) => game.gameId);
if (incompleteGameIds.length) {
throw new TournamentIncompleteError(incompleteGameIds);
}
}
}