From f3307300ef100b828c29eabb7da0a21a79e22013 Mon Sep 17 00:00:00 2001 From: evanpelle Date: Tue, 17 Sep 2024 19:49:16 -0700 Subject: [PATCH] refactored GameImpl into multiple files --- src/client/Client.ts | 2 +- src/client/ClientGame.ts | 6 +- src/client/InputHandler.ts | 2 +- src/client/graphics/GameRenderer.ts | 2 +- src/client/graphics/NameBoxCalculator.ts | 2 +- src/client/graphics/TransformHandler.ts | 2 +- src/client/graphics/layers/NameLayer.ts | 2 +- src/client/graphics/layers/TerrainLayer.ts | 2 +- src/client/graphics/layers/TerritoryLayer.ts | 2 +- src/client/graphics/layers/UILayer.ts | 2 +- src/core/Schemas.ts | 2 +- src/core/Util.ts | 2 +- src/core/configuration/Config.ts | 2 +- src/core/configuration/DefaultConfig.ts | 2 +- src/core/configuration/PastelTheme.ts | 2 +- .../execution/AllianceRequestExecution.ts | 4 +- src/core/execution/AttackExecution.ts | 4 +- src/core/execution/BoatAttackExecution.ts | 2 +- src/core/execution/BotExecution.ts | 2 +- src/core/execution/BotSpawner.ts | 2 +- src/core/execution/ExecutionManager.ts | 2 +- src/core/execution/FakeHumanExecution.ts | 2 +- src/core/execution/PlayerExecution.ts | 4 +- src/core/execution/SpawnExecution.ts | 2 +- src/core/execution/UpdateNameExecution.ts | 2 +- src/core/execution/Util.ts | 2 +- src/core/execution/WinCheckExecution.ts | 2 +- src/core/game/AllianceRequestImpl.ts | 24 ++ src/core/game/BoatImpl.ts | 46 +++ src/core/{ => game}/Game.ts | 6 +- src/core/{ => game}/GameImpl.ts | 317 +----------------- src/core/game/PlayerImpl.ts | 117 +++++++ src/core/game/TerraNulliusImpl.ts | 19 ++ src/core/{ => game}/TerrainMapLoader.ts | 2 +- src/core/game/TileImpl.ts | 109 ++++++ 35 files changed, 360 insertions(+), 344 deletions(-) create mode 100644 src/core/game/AllianceRequestImpl.ts create mode 100644 src/core/game/BoatImpl.ts rename src/core/{ => game}/Game.ts (97%) rename src/core/{ => game}/GameImpl.ts (51%) create mode 100644 src/core/game/PlayerImpl.ts create mode 100644 src/core/game/TerraNulliusImpl.ts rename src/core/{ => game}/TerrainMapLoader.ts (97%) create mode 100644 src/core/game/TileImpl.ts diff --git a/src/client/Client.ts b/src/client/Client.ts index b8c9c031d..e47e35668 100644 --- a/src/client/Client.ts +++ b/src/client/Client.ts @@ -1,6 +1,6 @@ import {Config, getConfig} from "../core/configuration/Config"; import {GameID, Lobby, ServerMessage, ServerMessageSchema} from "../core/Schemas"; -import {loadTerrainMap, TerrainMap} from "../core/TerrainMapLoader"; +import {loadTerrainMap, TerrainMap} from "../core/game/TerrainMapLoader"; import {ClientGame, createClientGame} from "./ClientGame"; import backgroundImage from '../../resources/images/TerrainMapFrontPage.png'; import favicon from '../../resources/images/Favicon.png'; diff --git a/src/client/ClientGame.ts b/src/client/ClientGame.ts index 42b39ed7a..260b49110 100644 --- a/src/client/ClientGame.ts +++ b/src/client/ClientGame.ts @@ -1,12 +1,12 @@ import {Executor} from "../core/execution/ExecutionManager"; -import {Cell, MutableGame, PlayerEvent, PlayerID, MutablePlayer, TileEvent, Player, Game, BoatEvent, Tile, PlayerType} from "../core/Game"; -import {createGame} from "../core/GameImpl"; +import {Cell, MutableGame, PlayerEvent, PlayerID, MutablePlayer, TileEvent, Player, Game, BoatEvent, Tile, PlayerType} from "../core/game/Game"; +import {createGame} from "../core/game/GameImpl"; import {EventBus} from "../core/EventBus"; import {Config} from "../core/configuration/Config"; import {createRenderer, GameRenderer} from "./graphics/GameRenderer"; import {InputHandler, MouseUpEvent, ZoomEvent, DragEvent, MouseDownEvent} from "./InputHandler" import {ClientID, ClientIntentMessageSchema, ClientJoinMessageSchema, ClientLeaveMessageSchema, ClientMessageSchema, GameID, Intent, ServerMessage, ServerMessageSchema, ServerSyncMessage, Turn} from "../core/Schemas"; -import {TerrainMap} from "../core/TerrainMapLoader"; +import {TerrainMap} from "../core/game/TerrainMapLoader"; import {and, bfs, dist, manhattanDist} from "../core/Util"; import {TerrainLayer} from "./graphics/layers/TerrainLayer"; import {WinCheckExecution} from "../core/execution/WinCheckExecution"; diff --git a/src/client/InputHandler.ts b/src/client/InputHandler.ts index 1d6c781d3..ccc0abc0f 100644 --- a/src/client/InputHandler.ts +++ b/src/client/InputHandler.ts @@ -1,5 +1,5 @@ import {EventBus, GameEvent} from "../core/EventBus"; -import {Cell} from "../core/Game"; +import {Cell} from "../core/game/Game"; export class MouseUpEvent implements GameEvent { constructor( diff --git a/src/client/graphics/GameRenderer.ts b/src/client/graphics/GameRenderer.ts index 00028063f..4edf943a6 100644 --- a/src/client/graphics/GameRenderer.ts +++ b/src/client/graphics/GameRenderer.ts @@ -1,4 +1,4 @@ -import {Game} from "../../core/Game"; +import {Game} from "../../core/game/Game"; import {NameLayer} from "./layers/NameLayer"; import {TerrainLayer} from "./layers/TerrainLayer"; import {TerritoryLayer} from "./layers/TerritoryLayer"; diff --git a/src/client/graphics/NameBoxCalculator.ts b/src/client/graphics/NameBoxCalculator.ts index c8e5cf1d3..7f667e14a 100644 --- a/src/client/graphics/NameBoxCalculator.ts +++ b/src/client/graphics/NameBoxCalculator.ts @@ -1,4 +1,4 @@ -import {Game, Player, Tile, Cell} from '../../core/Game'; +import {Game, Player, Tile, Cell} from '../../core/game/Game'; import {calculateBoundingBox, within} from '../../core/Util'; export interface Point { diff --git a/src/client/graphics/TransformHandler.ts b/src/client/graphics/TransformHandler.ts index 22618cd3c..299423d5e 100644 --- a/src/client/graphics/TransformHandler.ts +++ b/src/client/graphics/TransformHandler.ts @@ -1,5 +1,5 @@ import {EventBus} from "../../core/EventBus" -import {Cell, Game} from "../../core/Game"; +import {Cell, Game} from "../../core/game/Game"; import {ZoomEvent, DragEvent} from "../InputHandler"; export class TransformHandler { diff --git a/src/client/graphics/layers/NameLayer.ts b/src/client/graphics/layers/NameLayer.ts index a097ba5cb..d9946e18b 100644 --- a/src/client/graphics/layers/NameLayer.ts +++ b/src/client/graphics/layers/NameLayer.ts @@ -1,4 +1,4 @@ -import {Cell, Game, Player, PlayerType} from "../../../core/Game" +import {Cell, Game, Player, PlayerType} from "../../../core/game/Game" import {PseudoRandom} from "../../../core/PseudoRandom" import {calculateBoundingBox} from "../../../core/Util" import {Theme} from "../../../core/configuration/Config" diff --git a/src/client/graphics/layers/TerrainLayer.ts b/src/client/graphics/layers/TerrainLayer.ts index 36853dd19..5505cce62 100644 --- a/src/client/graphics/layers/TerrainLayer.ts +++ b/src/client/graphics/layers/TerrainLayer.ts @@ -1,5 +1,5 @@ import {inherits} from "util" -import {Game} from "../../../core/Game"; +import {Game} from "../../../core/game/Game"; import {throws} from "assert"; import {Layer} from "./Layer"; import {TransformHandler} from "../TransformHandler"; diff --git a/src/client/graphics/layers/TerritoryLayer.ts b/src/client/graphics/layers/TerritoryLayer.ts index fbce14ad9..b2c797761 100644 --- a/src/client/graphics/layers/TerritoryLayer.ts +++ b/src/client/graphics/layers/TerritoryLayer.ts @@ -1,5 +1,5 @@ import {PriorityQueue} from "@datastructures-js/priority-queue"; -import {Boat, BoatEvent, Cell, Game, Player, Tile, TileEvent} from "../../../core/Game"; +import {Boat, BoatEvent, Cell, Game, Player, Tile, TileEvent} from "../../../core/game/Game"; import {PseudoRandom} from "../../../core/PseudoRandom"; import {Colord} from "colord"; import {bfs, dist} from "../../../core/Util"; diff --git a/src/client/graphics/layers/UILayer.ts b/src/client/graphics/layers/UILayer.ts index 7cb7accd9..a791a7eef 100644 --- a/src/client/graphics/layers/UILayer.ts +++ b/src/client/graphics/layers/UILayer.ts @@ -1,7 +1,7 @@ import {GameEnv, Theme} from "../../../core/configuration/Config"; import {EventBus, GameEvent} from "../../../core/EventBus"; import {WinEvent} from "../../../core/execution/WinCheckExecution"; -import {AllianceRequest, Game, Player} from "../../../core/Game"; +import {AllianceRequest, Game, Player} from "../../../core/game/Game"; import {ClientID} from "../../../core/Schemas"; import {renderTroops} from "../Utils"; import winModalHtml from '../WinModal.html'; diff --git a/src/core/Schemas.ts b/src/core/Schemas.ts index f64afe3f8..eda0705f4 100644 --- a/src/core/Schemas.ts +++ b/src/core/Schemas.ts @@ -1,5 +1,5 @@ import {z} from 'zod'; -import {PlayerType} from './Game'; +import {PlayerType} from './game/Game'; export type GameID = string export type ClientID = string diff --git a/src/core/Util.ts b/src/core/Util.ts index dc8f9936a..ced73311a 100644 --- a/src/core/Util.ts +++ b/src/core/Util.ts @@ -1,7 +1,7 @@ import {v4 as uuidv4} from 'uuid'; -import {Cell, Player, Tile} from "./Game"; +import {Cell, Player, Tile} from "./game/Game"; export function manhattanDist(c1: Cell, c2: Cell): number { return Math.abs(c1.x - c2.x) + Math.abs(c1.y - c2.y); diff --git a/src/core/configuration/Config.ts b/src/core/configuration/Config.ts index 0f6be37b1..6071b659e 100644 --- a/src/core/configuration/Config.ts +++ b/src/core/configuration/Config.ts @@ -1,4 +1,4 @@ -import {Player, PlayerID, PlayerInfo, TerraNullius, Tile} from "../Game"; +import {Player, PlayerID, PlayerInfo, TerraNullius, Tile} from "../game/Game"; import {Colord, colord} from "colord"; import {devConfig} from "./DevConfig"; import {defaultConfig} from "./DefaultConfig"; diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts index e712da7f0..8e8e58834 100644 --- a/src/core/configuration/DefaultConfig.ts +++ b/src/core/configuration/DefaultConfig.ts @@ -1,4 +1,4 @@ -import {Player, PlayerInfo, PlayerType, TerrainType, TerraNullius, Tile} from "../Game"; +import {Player, PlayerInfo, PlayerType, TerrainType, TerraNullius, Tile} from "../game/Game"; import {GameID} from "../Schemas"; import {simpleHash, within} from "../Util"; import {Config, Theme} from "./Config"; diff --git a/src/core/configuration/PastelTheme.ts b/src/core/configuration/PastelTheme.ts index 7ed4da2e1..17382567b 100644 --- a/src/core/configuration/PastelTheme.ts +++ b/src/core/configuration/PastelTheme.ts @@ -1,5 +1,5 @@ import {Colord, colord, random} from "colord"; -import {PlayerID, PlayerInfo, TerrainType, Tile} from "../Game"; +import {PlayerID, PlayerInfo, TerrainType, Tile} from "../game/Game"; import {Theme} from "./Config"; import {time} from "console"; import {PseudoRandom} from "../PseudoRandom"; diff --git a/src/core/execution/AllianceRequestExecution.ts b/src/core/execution/AllianceRequestExecution.ts index c60fc4018..6d2574c79 100644 --- a/src/core/execution/AllianceRequestExecution.ts +++ b/src/core/execution/AllianceRequestExecution.ts @@ -1,4 +1,4 @@ -import {AllianceRequest, Execution, MutableGame, MutablePlayer, Player, PlayerID} from "../Game"; +import {AllianceRequest, Execution, MutableGame, MutablePlayer, Player, PlayerID} from "../game/Game"; export class AllianceRequestExecution implements Execution { private active = true @@ -17,7 +17,7 @@ export class AllianceRequestExecution implements Execution { tick(ticks: number): void { alert('recied request') - + this.active = false } diff --git a/src/core/execution/AttackExecution.ts b/src/core/execution/AttackExecution.ts index b2ad77ba5..9c304827c 100644 --- a/src/core/execution/AttackExecution.ts +++ b/src/core/execution/AttackExecution.ts @@ -1,8 +1,8 @@ import {PriorityQueue} from "@datastructures-js/priority-queue"; -import {Cell, Execution, MutableGame, MutablePlayer, PlayerID, TerrainType, TerraNullius, Tile} from "../Game"; +import {Cell, Execution, MutableGame, MutablePlayer, PlayerID, TerrainType, TerraNullius, Tile} from "../game/Game"; import {PseudoRandom} from "../PseudoRandom"; import {manhattanDist} from "../Util"; -import {Terrain} from "../TerrainMapLoader"; +import {Terrain} from "../game/TerrainMapLoader"; export class AttackExecution implements Execution { private active: boolean = true; diff --git a/src/core/execution/BoatAttackExecution.ts b/src/core/execution/BoatAttackExecution.ts index 16acd084c..4f17ca175 100644 --- a/src/core/execution/BoatAttackExecution.ts +++ b/src/core/execution/BoatAttackExecution.ts @@ -1,5 +1,5 @@ import {PriorityQueue} from "@datastructures-js/priority-queue"; -import {Boat, Cell, Execution, MutableBoat, MutableGame, MutablePlayer, Player, PlayerID, TerraNullius, Tile, TileEvent} from "../Game"; +import {Boat, Cell, Execution, MutableBoat, MutableGame, MutablePlayer, Player, PlayerID, TerraNullius, Tile, TileEvent} from "../game/Game"; import {manhattanDist, manhattanDistWrapped} from "../Util"; import {AttackExecution} from "./AttackExecution"; import {Config} from "../configuration/Config"; diff --git a/src/core/execution/BotExecution.ts b/src/core/execution/BotExecution.ts index c56eea2a3..858c6d214 100644 --- a/src/core/execution/BotExecution.ts +++ b/src/core/execution/BotExecution.ts @@ -1,4 +1,4 @@ -import {Cell, Execution, MutableGame, MutablePlayer, Player, PlayerID, PlayerInfo, PlayerType, TerraNullius} from "../Game" +import {Cell, Execution, MutableGame, MutablePlayer, Player, PlayerID, PlayerInfo, PlayerType, TerraNullius} from "../game/Game" import {PseudoRandom} from "../PseudoRandom" import {simpleHash} from "../Util"; import {AttackExecution} from "./AttackExecution"; diff --git a/src/core/execution/BotSpawner.ts b/src/core/execution/BotSpawner.ts index b22ed3365..89bc6b8a7 100644 --- a/src/core/execution/BotSpawner.ts +++ b/src/core/execution/BotSpawner.ts @@ -1,4 +1,4 @@ -import {Cell, Game, PlayerType, Tile, TileEvent} from "../Game"; +import {Cell, Game, PlayerType, Tile, TileEvent} from "../game/Game"; import {PseudoRandom} from "../PseudoRandom"; import {SpawnIntent} from "../Schemas"; import {bfs, dist as dist, manhattanDist} from "../Util"; diff --git a/src/core/execution/ExecutionManager.ts b/src/core/execution/ExecutionManager.ts index 38222f323..59486cde9 100644 --- a/src/core/execution/ExecutionManager.ts +++ b/src/core/execution/ExecutionManager.ts @@ -1,4 +1,4 @@ -import {Cell, Execution, MutableGame, Game, MutablePlayer, PlayerInfo, TerraNullius, Tile, PlayerType, Alliance} from "../Game"; +import {Cell, Execution, MutableGame, Game, MutablePlayer, PlayerInfo, TerraNullius, Tile, PlayerType, Alliance} from "../game/Game"; import {AttackIntent, BoatAttackIntentSchema, GameID, Intent, Turn} from "../Schemas"; import {AttackExecution} from "./AttackExecution"; import {SpawnExecution} from "./SpawnExecution"; diff --git a/src/core/execution/FakeHumanExecution.ts b/src/core/execution/FakeHumanExecution.ts index 61ab4d352..c6fd866e5 100644 --- a/src/core/execution/FakeHumanExecution.ts +++ b/src/core/execution/FakeHumanExecution.ts @@ -1,4 +1,4 @@ -import {Cell, Execution, MutableGame, MutablePlayer, Player, PlayerID, PlayerInfo, PlayerType, TerrainType, TerraNullius, Tile} from "../Game" +import {Cell, Execution, MutableGame, MutablePlayer, Player, PlayerID, PlayerInfo, PlayerType, TerrainType, TerraNullius, Tile} from "../game/Game" import {PseudoRandom} from "../PseudoRandom" import {and, bfs, dist, simpleHash} from "../Util"; import {AttackExecution} from "./AttackExecution"; diff --git a/src/core/execution/PlayerExecution.ts b/src/core/execution/PlayerExecution.ts index f74f3cfde..c902b6071 100644 --- a/src/core/execution/PlayerExecution.ts +++ b/src/core/execution/PlayerExecution.ts @@ -1,7 +1,7 @@ import {Config} from "../configuration/Config" -import {Execution, MutableGame, MutablePlayer, Player, PlayerID, TerraNullius, Tile} from "../Game" +import {Execution, MutableGame, MutablePlayer, Player, PlayerID, TerraNullius, Tile} from "../game/Game" import {bfs, calculateBoundingBox, getMode, inscribed, simpleHash} from "../Util" -import {GameImpl} from "../GameImpl" +import {GameImpl} from "../game/GameImpl" export class PlayerExecution implements Execution { diff --git a/src/core/execution/SpawnExecution.ts b/src/core/execution/SpawnExecution.ts index a72459638..014205b8c 100644 --- a/src/core/execution/SpawnExecution.ts +++ b/src/core/execution/SpawnExecution.ts @@ -1,4 +1,4 @@ -import {Cell, Execution, MutableGame, MutablePlayer, PlayerInfo, PlayerType} from "../Game" +import {Cell, Execution, MutableGame, MutablePlayer, PlayerInfo, PlayerType} from "../game/Game" import {BotExecution} from "./BotExecution" import {PlayerExecution} from "./PlayerExecution" import {getSpawnCells} from "./Util" diff --git a/src/core/execution/UpdateNameExecution.ts b/src/core/execution/UpdateNameExecution.ts index 364ed7b29..3702a1ab6 100644 --- a/src/core/execution/UpdateNameExecution.ts +++ b/src/core/execution/UpdateNameExecution.ts @@ -1,4 +1,4 @@ -import {Execution, MutableGame, MutablePlayer, PlayerID} from "../Game" +import {Execution, MutableGame, MutablePlayer, PlayerID} from "../game/Game" import {ClientID} from "../Schemas" export class UpdateNameExecution implements Execution { diff --git a/src/core/execution/Util.ts b/src/core/execution/Util.ts index c6ec07867..c7c7db742 100644 --- a/src/core/execution/Util.ts +++ b/src/core/execution/Util.ts @@ -1,4 +1,4 @@ -import {Game, Cell} from "../Game"; +import {Game, Cell} from "../game/Game"; export function getSpawnCells(gs: Game, cell: Cell): Cell[] { diff --git a/src/core/execution/WinCheckExecution.ts b/src/core/execution/WinCheckExecution.ts index 19aa850eb..f640bb962 100644 --- a/src/core/execution/WinCheckExecution.ts +++ b/src/core/execution/WinCheckExecution.ts @@ -1,5 +1,5 @@ import {EventBus, GameEvent} from "../EventBus" -import {Execution, MutableGame, MutablePlayer, Player, PlayerID} from "../Game" +import {Execution, MutableGame, MutablePlayer, Player, PlayerID} from "../game/Game" export class WinEvent implements GameEvent { constructor(public readonly winner: Player) { } diff --git a/src/core/game/AllianceRequestImpl.ts b/src/core/game/AllianceRequestImpl.ts new file mode 100644 index 000000000..5ea2e96ac --- /dev/null +++ b/src/core/game/AllianceRequestImpl.ts @@ -0,0 +1,24 @@ +import {MutableAllianceRequest, Player} from "./Game"; +import {GameImpl} from "./GameImpl"; + + +export class AllianceRequestImpl implements MutableAllianceRequest { + + constructor(private requestor_, private recipient_, private game: GameImpl) { } + + requestor(): Player { + return this.requestor_; + } + + recipient(): Player { + return this.recipient_; + } + + accept(): void { + throw new Error("Method not implemented."); + } + reject(): void { + throw new Error("Method not implemented."); + } + +} diff --git a/src/core/game/BoatImpl.ts b/src/core/game/BoatImpl.ts new file mode 100644 index 000000000..fe13cc3c6 --- /dev/null +++ b/src/core/game/BoatImpl.ts @@ -0,0 +1,46 @@ +import {MutableBoat, Tile, TerraNullius} from "./Game"; +import {GameImpl} from "./GameImpl"; +import {PlayerImpl} from "./PlayerImpl"; +import {TerraNulliusImpl} from "./TerraNulliusImpl"; + + +export class BoatImpl implements MutableBoat { + private _active = true; + + constructor( + private g: GameImpl, + private _tile: Tile, + private _troops: number, + private _owner: PlayerImpl, + private _target: PlayerImpl | TerraNulliusImpl + ) { } + + move(tile: Tile): void { + const oldTile = this._tile; + this._tile = tile; + this.g.fireBoatUpdateEvent(this, oldTile); + } + setTroops(troops: number): void { + this._troops = troops; + } + troops(): number { + return this._troops; + } + tile(): Tile { + return this._tile; + } + owner(): PlayerImpl { + return this._owner; + } + target(): PlayerImpl | TerraNullius { + return this._target; + } + delete(): void { + this._owner._boats = this._owner._boats.filter(b => b != this); + this._active = false; + this.g.fireBoatUpdateEvent(this, this._tile); + } + isActive(): boolean { + return this._active; + } +} diff --git a/src/core/Game.ts b/src/core/game/Game.ts similarity index 97% rename from src/core/Game.ts rename to src/core/game/Game.ts index a62a8bd29..bbb8bc84d 100644 --- a/src/core/Game.ts +++ b/src/core/game/Game.ts @@ -1,7 +1,7 @@ import {info} from "console" -import {Config} from "./configuration/Config" -import {GameEvent} from "./EventBus" -import {ClientID, GameID} from "./Schemas" +import {Config} from "../configuration/Config" +import {GameEvent} from "../EventBus" +import {ClientID, GameID} from "../Schemas" export type PlayerID = string diff --git a/src/core/GameImpl.ts b/src/core/game/GameImpl.ts similarity index 51% rename from src/core/GameImpl.ts rename to src/core/game/GameImpl.ts index 192f258fb..690dd1ba0 100644 --- a/src/core/GameImpl.ts +++ b/src/core/game/GameImpl.ts @@ -1,308 +1,18 @@ import {info} from "console"; -import {Config} from "./configuration/Config"; -import {EventBus} from "./EventBus"; -import {Cell, Execution, MutableGame, Game, MutablePlayer, PlayerEvent, PlayerID, PlayerInfo, Player, TerraNullius, Tile, TileEvent, Boat, MutableBoat, BoatEvent, TerrainType, PlayerType, MutableAllianceRequest} from "./Game"; -import {ClientID} from "./Schemas"; -import {Terrain, TerrainMap} from "./TerrainMapLoader"; -import {simpleHash} from "./Util"; +import {Config} from "../configuration/Config"; +import {EventBus} from "../EventBus"; +import {Cell, Execution, MutableGame, Game, MutablePlayer, PlayerEvent, PlayerID, PlayerInfo, Player, TerraNullius, Tile, TileEvent, Boat, BoatEvent, PlayerType, MutableAllianceRequest} from "./Game"; +import {TerrainMap} from "./TerrainMapLoader"; +import {PlayerImpl} from "./PlayerImpl"; +import {TerraNulliusImpl} from "./TerraNulliusImpl"; +import {TileImpl} from "./TileImpl"; +import {AllianceRequestImpl} from "./AllianceRequestImpl"; export function createGame(terrainMap: TerrainMap, eventBus: EventBus, config: Config): Game { return new GameImpl(terrainMap, eventBus, config) } -type CellString = string - -class TileImpl implements Tile { - - public _isBorder = false - private _neighbors: Tile[] = null - - constructor( - private readonly gs: GameImpl, - public _owner: PlayerImpl | TerraNulliusImpl, - private readonly _cell: Cell, - private readonly _terrain: Terrain - ) { } - - neighborsWrapped(): Tile[] { - const x = this._cell.x; - const y = this._cell.y; - const ns: Tile[] = []; - - // Check top neighbor - if (y > 0) { - ns.push(this.gs.map[x][y - 1]); - } else { - ns.push(this.gs.map[x][this.gs.height() - 1]); - } - - // Check bottom neighbor - if (y < this.gs.height() - 1) { - ns.push(this.gs.map[x][y + 1]); - } else { - ns.push(this.gs.map[x][0]); - } - - // Check left neighbor (wrap around) - if (x > 0) { - ns.push(this.gs.map[x - 1][y]); - } else { - ns.push(this.gs.map[this.gs.width() - 1][y]); - } - - // Check right neighbor (wrap around) - if (x < this.gs.width() - 1) { - ns.push(this.gs.map[x + 1][y]); - } else { - ns.push(this.gs.map[0][y]); - } - return ns; - } - isLake(): boolean { - return !this.isLand() && !this.isOcean() - } - isOcean(): boolean { - return this._terrain.ocean - } - magnitude(): number { - return this._terrain.magnitude - } - isShore(): boolean { - return this.isLand() && this._terrain.shoreline - } - isOceanShore(): boolean { - return this.isShore() && this.neighbors().filter(n => n.isOcean()).length > 0 - } - isShorelineWater(): boolean { - return this.isWater() && this._terrain.shoreline - } - isLand(): boolean { - return this._terrain.land - } - isWater(): boolean { - return !this._terrain.land - } - terrain(): TerrainType { - return this._terrain.type - } - - borders(other: Player | TerraNullius): boolean { - for (const n of this.neighbors()) { - if (n.owner() == other) { - return true - } - } - return false - } - - onShore(): boolean { - return this.neighbors() - .filter(t => t.isWater()) - .length > 0 - } - - hasOwner(): boolean {return this._owner != this.gs._terraNullius} - owner(): MutablePlayer | TerraNullius {return this._owner} - isBorder(): boolean {return this._isBorder} - isInterior(): boolean {return this.hasOwner() && !this.isBorder()} - cell(): Cell {return this._cell} - - neighbors(): Tile[] { - if (this._neighbors == null) { - this._neighbors = this.gs.neighbors(this) - } - return this._neighbors - } -} - -export class AllianceRequestImpl implements MutableAllianceRequest { - - constructor(private requestor_, private recipient_, private game: GameImpl) { } - - requestor(): Player { - return this.requestor_ - } - - recipient(): Player { - return this.recipient_ - } - - accept(): void { - throw new Error("Method not implemented."); - } - reject(): void { - throw new Error("Method not implemented."); - } - -} - -export class BoatImpl implements MutableBoat { - private _active = true - - constructor( - private g: GameImpl, - private _tile: Tile, - private _troops: number, - private _owner: PlayerImpl, - private _target: PlayerImpl | TerraNulliusImpl - ) { } - - move(tile: Tile): void { - const oldTile = this._tile - this._tile = tile - this.g.fireBoatUpdateEvent(this, oldTile) - } - setTroops(troops: number): void { - this._troops = troops - } - troops(): number { - return this._troops - } - tile(): Tile { - return this._tile - } - owner(): PlayerImpl { - return this._owner - } - target(): PlayerImpl | TerraNullius { - return this._target - } - delete(): void { - this._owner._boats = this._owner._boats.filter(b => b != this) - this._active = false - this.g.fireBoatUpdateEvent(this, this._tile) - } - isActive(): boolean { - return this._active - } -} - -export class PlayerImpl implements MutablePlayer { - public _borderTiles: Set = new Set() - - public _boats: BoatImpl[] = [] - public _tiles: Map = new Map() - - private _name: string - - constructor(private gs: GameImpl, private readonly playerInfo: PlayerInfo, private _troops) { - this._name = playerInfo.name - } - - name(): string { - return this._name - } - - clientID(): ClientID { - return this.playerInfo.clientID - } - - id(): PlayerID { - return this.playerInfo.id - } - - type(): PlayerType { - return this.playerInfo.playerType - } - - setName(name: string) { - - } - - addBoat(troops: number, tile: Tile, target: Player | TerraNullius): BoatImpl { - const b = new BoatImpl(this.gs, tile, troops, this, target as PlayerImpl | TerraNulliusImpl) - this._boats.push(b) - this.gs.fireBoatUpdateEvent(b, b.tile()) - return b - } - - boats(): BoatImpl[] { - return this._boats - } - - sharesBorderWith(other: Player | TerraNullius): boolean { - for (const border of this._borderTiles) { - for (const neighbor of border.neighbors()) { - if (neighbor.owner() == other) { - return true - } - } - } - return false - } - numTilesOwned(): number { - return this._tiles.size - } - - tiles(): ReadonlySet { - return new Set(this._tiles.values()) - } - - borderTiles(): ReadonlySet { - return this._borderTiles - } - - neighbors(): (MutablePlayer | TerraNullius)[] { - const ns: Set<(MutablePlayer | TerraNullius)> = new Set() - for (const border of this.borderTiles()) { - for (const neighbor of border.neighbors()) { - if (neighbor.isLand() && neighbor.owner() != this) { - ns.add((neighbor as TileImpl)._owner) - } - } - } - return Array.from(ns) - } - - addTroops(troops: number): void { - this._troops += Math.floor(troops) - } - removeTroops(troops: number): void { - this._troops -= Math.floor(troops) - this._troops = Math.max(this._troops, 0) - } - - isPlayer(): this is MutablePlayer {return true as const} - ownsTile(cell: Cell): boolean {return this._tiles.has(cell.toString())} - setTroops(troops: number) {this._troops = Math.floor(troops)} - conquer(tile: Tile) {this.gs.conquer(this, tile)} - relinquish(tile: Tile) { - if (tile.owner() != this) { - throw new Error(`Cannot relinquish tile not owned by this player`) - } - this.gs.relinquish(tile) - } - info(): PlayerInfo {return this.playerInfo} - troops(): number {return this._troops} - isAlive(): boolean {return this._tiles.size > 0} - gameState(): MutableGame {return this.gs} - executions(): Execution[] { - return this.gs.executions().filter(exec => exec.owner().id() == this.id()) - } - hash(): number { - return simpleHash(this.id()) * (this.troops() + this.numTilesOwned()) - } - toString(): string { - return `Player:{name:${this.info().name},clientID:${this.info().clientID},isAlive:${this.isAlive()},troops:${this._troops},numTileOwned:${this.numTilesOwned()}}]` - } -} - -class TerraNulliusImpl implements TerraNullius { - public tiles: Map = new Map() - - - constructor(private gs: GameImpl) { - } - - id(): PlayerID { - return 'TerraNulliusID' - } - ownsTile(cell: Cell): boolean { - return this.tiles.has(cell) - } - isPlayer(): false {return false as const} -} - +export type CellString = string export class GameImpl implements MutableGame { private _ticks = 0 @@ -531,15 +241,6 @@ export class GameImpl implements MutableGame { } conquer(owner: PlayerImpl, tile: Tile): void { - // if (tile.owner() == owner) { - // throw new Error(`Player ${owner} already owns cell ${tile.cell().toString()}`) - // } - // if (!owner.isPlayer()) { - // throw new Error("Must be a player") - // } - // if (tile.isWater()) { - // throw new Error("Cannot conquer water") - // } const tileImpl = tile as TileImpl let previousOwner = tileImpl._owner if (previousOwner.isPlayer()) { diff --git a/src/core/game/PlayerImpl.ts b/src/core/game/PlayerImpl.ts new file mode 100644 index 000000000..5e14f9043 --- /dev/null +++ b/src/core/game/PlayerImpl.ts @@ -0,0 +1,117 @@ +import {MutablePlayer, Tile, PlayerInfo, PlayerID, PlayerType, Player, TerraNullius, Cell, MutableGame, Execution} from "./Game"; +import {ClientID} from "../Schemas"; +import {simpleHash} from "../Util"; +import {CellString, GameImpl} from "./GameImpl"; +import {BoatImpl} from "./BoatImpl"; +import {TileImpl} from "./TileImpl"; +import {TerraNulliusImpl} from "./TerraNulliusImpl"; + + +export class PlayerImpl implements MutablePlayer { + public _borderTiles: Set = new Set(); + + public _boats: BoatImpl[] = []; + public _tiles: Map = new Map(); + + private _name: string; + + constructor(private gs: GameImpl, private readonly playerInfo: PlayerInfo, private _troops) { + this._name = playerInfo.name; + } + + name(): string { + return this._name; + } + + clientID(): ClientID { + return this.playerInfo.clientID; + } + + id(): PlayerID { + return this.playerInfo.id; + } + + type(): PlayerType { + return this.playerInfo.playerType; + } + + setName(name: string) { + } + + addBoat(troops: number, tile: Tile, target: Player | TerraNullius): BoatImpl { + const b = new BoatImpl(this.gs, tile, troops, this, target as PlayerImpl | TerraNulliusImpl); + this._boats.push(b); + this.gs.fireBoatUpdateEvent(b, b.tile()); + return b; + } + + boats(): BoatImpl[] { + return this._boats; + } + + sharesBorderWith(other: Player | TerraNullius): boolean { + for (const border of this._borderTiles) { + for (const neighbor of border.neighbors()) { + if (neighbor.owner() == other) { + return true; + } + } + } + return false; + } + numTilesOwned(): number { + return this._tiles.size; + } + + tiles(): ReadonlySet { + return new Set(this._tiles.values()); + } + + borderTiles(): ReadonlySet { + return this._borderTiles; + } + + neighbors(): (MutablePlayer | TerraNullius)[] { + const ns: Set<(MutablePlayer | TerraNullius)> = new Set(); + for (const border of this.borderTiles()) { + for (const neighbor of border.neighbors()) { + if (neighbor.isLand() && neighbor.owner() != this) { + ns.add((neighbor as TileImpl)._owner); + } + } + } + return Array.from(ns); + } + + addTroops(troops: number): void { + this._troops += Math.floor(troops); + } + removeTroops(troops: number): void { + this._troops -= Math.floor(troops); + this._troops = Math.max(this._troops, 0); + } + + isPlayer(): this is MutablePlayer {return true as const;} + ownsTile(cell: Cell): boolean {return this._tiles.has(cell.toString());} + setTroops(troops: number) {this._troops = Math.floor(troops);} + conquer(tile: Tile) {this.gs.conquer(this, tile);} + relinquish(tile: Tile) { + if (tile.owner() != this) { + throw new Error(`Cannot relinquish tile not owned by this player`); + } + this.gs.relinquish(tile); + } + info(): PlayerInfo {return this.playerInfo;} + troops(): number {return this._troops;} + isAlive(): boolean {return this._tiles.size > 0;} + gameState(): MutableGame {return this.gs;} + executions(): Execution[] { + return this.gs.executions().filter(exec => exec.owner().id() == this.id()); + } + hash(): number { + return simpleHash(this.id()) * (this.troops() + this.numTilesOwned()); + } + toString(): string { + return `Player:{name:${this.info().name},clientID:${this.info().clientID},isAlive:${this.isAlive()},troops:${this._troops},numTileOwned:${this.numTilesOwned()}}]`; + } +} diff --git a/src/core/game/TerraNulliusImpl.ts b/src/core/game/TerraNulliusImpl.ts new file mode 100644 index 000000000..d7c910a3a --- /dev/null +++ b/src/core/game/TerraNulliusImpl.ts @@ -0,0 +1,19 @@ +import {TerraNullius, Cell, Tile, PlayerID} from "./Game"; +import {GameImpl} from "./GameImpl"; + + +export class TerraNulliusImpl implements TerraNullius { + public tiles: Map = new Map(); + + + constructor(private gs: GameImpl) { + } + + id(): PlayerID { + return 'TerraNulliusID'; + } + ownsTile(cell: Cell): boolean { + return this.tiles.has(cell); + } + isPlayer(): false {return false as const;} +} diff --git a/src/core/TerrainMapLoader.ts b/src/core/game/TerrainMapLoader.ts similarity index 97% rename from src/core/TerrainMapLoader.ts rename to src/core/game/TerrainMapLoader.ts index 62054e72f..959fcc13b 100644 --- a/src/core/TerrainMapLoader.ts +++ b/src/core/game/TerrainMapLoader.ts @@ -1,5 +1,5 @@ import {Cell, TerrainType} from './Game'; -import binAsString from "!!binary-loader!../../resources/TopoWorldMap.bin"; +import binAsString from "!!binary-loader!../../../resources/TopoWorldMap.bin"; export class TerrainMap { constructor(public readonly tiles: Terrain[][], public readonly numLandTiles: number) { } diff --git a/src/core/game/TileImpl.ts b/src/core/game/TileImpl.ts new file mode 100644 index 000000000..fffae8f6d --- /dev/null +++ b/src/core/game/TileImpl.ts @@ -0,0 +1,109 @@ +import {Tile, Cell, TerrainType, Player, TerraNullius, MutablePlayer} from "./Game"; +import {Terrain} from "./TerrainMapLoader"; +import {GameImpl} from "./GameImpl"; +import {PlayerImpl} from "./PlayerImpl"; +import {TerraNulliusImpl} from "./TerraNulliusImpl"; + + +export class TileImpl implements Tile { + + public _isBorder = false; + private _neighbors: Tile[] = null; + + constructor( + private readonly gs: GameImpl, + public _owner: PlayerImpl | TerraNulliusImpl, + private readonly _cell: Cell, + private readonly _terrain: Terrain + ) { } + + neighborsWrapped(): Tile[] { + const x = this._cell.x; + const y = this._cell.y; + const ns: Tile[] = []; + + // Check top neighbor + if (y > 0) { + ns.push(this.gs.map[x][y - 1]); + } else { + ns.push(this.gs.map[x][this.gs.height() - 1]); + } + + // Check bottom neighbor + if (y < this.gs.height() - 1) { + ns.push(this.gs.map[x][y + 1]); + } else { + ns.push(this.gs.map[x][0]); + } + + // Check left neighbor (wrap around) + if (x > 0) { + ns.push(this.gs.map[x - 1][y]); + } else { + ns.push(this.gs.map[this.gs.width() - 1][y]); + } + + // Check right neighbor (wrap around) + if (x < this.gs.width() - 1) { + ns.push(this.gs.map[x + 1][y]); + } else { + ns.push(this.gs.map[0][y]); + } + return ns; + } + isLake(): boolean { + return !this.isLand() && !this.isOcean(); + } + isOcean(): boolean { + return this._terrain.ocean; + } + magnitude(): number { + return this._terrain.magnitude; + } + isShore(): boolean { + return this.isLand() && this._terrain.shoreline; + } + isOceanShore(): boolean { + return this.isShore() && this.neighbors().filter(n => n.isOcean()).length > 0; + } + isShorelineWater(): boolean { + return this.isWater() && this._terrain.shoreline; + } + isLand(): boolean { + return this._terrain.land; + } + isWater(): boolean { + return !this._terrain.land; + } + terrain(): TerrainType { + return this._terrain.type; + } + + borders(other: Player | TerraNullius): boolean { + for (const n of this.neighbors()) { + if (n.owner() == other) { + return true; + } + } + return false; + } + + onShore(): boolean { + return this.neighbors() + .filter(t => t.isWater()) + .length > 0; + } + + hasOwner(): boolean {return this._owner != this.gs._terraNullius;} + owner(): MutablePlayer | TerraNullius {return this._owner;} + isBorder(): boolean {return this._isBorder;} + isInterior(): boolean {return this.hasOwner() && !this.isBorder();} + cell(): Cell {return this._cell;} + + neighbors(): Tile[] { + if (this._neighbors == null) { + this._neighbors = this.gs.neighbors(this); + } + return this._neighbors; + } +}