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(); playersMap = new Map(); maxPlayersPerGame = 2; constructor(public board: Board, options: Partial = {}) { 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); } } }