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.
117 lines
3.3 KiB
117 lines
3.3 KiB
2 years ago
|
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);
|
||
|
}
|
||
|
}
|
||
|
}
|