diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts index bb6d0357f..77f420896 100644 --- a/src/client/ClientGameRunner.ts +++ b/src/client/ClientGameRunner.ts @@ -211,7 +211,7 @@ export class ClientGameRunner { } consolex.log(`clicked cell ${cell}`) const tile = this.gs.tile(cell) - if (tile.isLand() && !tile.hasOwner() && this.gs.inSpawnPhase()) { + if (tile.terrain().isLand() && !tile.hasOwner() && this.gs.inSpawnPhase()) { this.eventBus.emit(new SendSpawnIntentEvent(cell)) return } @@ -233,13 +233,13 @@ export class ClientGameRunner { return } - if (tile.isLand()) { + if (tile.terrain().isLand()) { if (tile.hasOwner()) { if (this.myPlayer.sharesBorderWith(tile.owner())) { this.eventBus.emit(new SendAttackIntentEvent(targetID, this.myPlayer.troops() * this.renderer.uiState.attackRatio)) } } else { - outer_loop: for (const t of bfs(tile, and(t => !t.hasOwner() && t.isLand(), dist(tile, 200)))) { + outer_loop: for (const t of bfs(tile, and(t => !t.hasOwner() && t.terrain().isLand(), dist(tile, 200)))) { for (const n of t.neighbors()) { if (n.owner() == this.myPlayer) { this.eventBus.emit(new SendAttackIntentEvent(targetID, this.myPlayer.troops() * this.renderer.uiState.attackRatio)) diff --git a/src/client/graphics/NameBoxCalculator.ts b/src/client/graphics/NameBoxCalculator.ts index 7e1a2c80b..5aaed12e3 100644 --- a/src/client/graphics/NameBoxCalculator.ts +++ b/src/client/graphics/NameBoxCalculator.ts @@ -61,7 +61,7 @@ export function createGrid(game: Game, player: Player, boundingBox: { min: Point const cell = new Cell(x * scalingFactor, y * scalingFactor); if (game.isOnMap(cell)) { const tile = game.tile(cell); - grid[x - scaledBoundingBox.min.x][y - scaledBoundingBox.min.y] = tile.isLake() || tile.owner() === player; // TODO: okay if lake + grid[x - scaledBoundingBox.min.x][y - scaledBoundingBox.min.y] = tile.terrain().isLake() || tile.owner() === player; // TODO: okay if lake } } } diff --git a/src/client/graphics/layers/PlayerInfoOverlay.ts b/src/client/graphics/layers/PlayerInfoOverlay.ts index 4a709fc24..a8c79edc6 100644 --- a/src/client/graphics/layers/PlayerInfoOverlay.ts +++ b/src/client/graphics/layers/PlayerInfoOverlay.ts @@ -64,7 +64,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer { if (owner.isPlayer()) { this.player = owner; this.setVisible(true); - } else if (!tile.isLand()) { + } else if (!tile.terrain().isLand()) { const units = this.game.units(UnitType.Destroyer, UnitType.Battleship, UnitType.TradeShip) .filter(u => euclideanDist(worldCoord, u.tile().cell()) < 50) .sort(distSortUnit(tile)); diff --git a/src/client/graphics/layers/radial/RadialMenu.ts b/src/client/graphics/layers/radial/RadialMenu.ts index 1e8683418..853fc5772 100644 --- a/src/client/graphics/layers/radial/RadialMenu.ts +++ b/src/client/graphics/layers/radial/RadialMenu.ts @@ -239,7 +239,7 @@ export class RadialMenu implements Layer { const other = tile.owner() if (this.game.inSpawnPhase()) { - if (tile.isLand() && !tile.hasOwner()) { + if (tile.terrain().isLand() && !tile.hasOwner()) { this.enableCenterButton(true) } return @@ -268,13 +268,13 @@ export class RadialMenu implements Layer { } } - if (tile.owner() != myPlayer && tile.isLand() && myPlayer.sharesBorderWith(other)) { + if (tile.owner() != myPlayer && tile.terrain().isLand() && myPlayer.sharesBorderWith(other)) { if (other.isPlayer()) { if (!myPlayer.isAlliedWith(other)) { this.enableCenterButton(true) } } else { - outer_loop: for (const t of bfs(tile, and(t => !t.hasOwner() && t.isLand(), dist(tile, 200)))) { + outer_loop: for (const t of bfs(tile, and(t => !t.hasOwner() && t.terrain().isLand(), dist(tile, 200)))) { for (const n of t.neighbors()) { if (n.owner() == myPlayer) { this.enableCenterButton(true) @@ -321,7 +321,7 @@ export class RadialMenu implements Layer { } } - if (!tile.isLand()) { + if (!tile.terrain().isLand()) { return } if (myPlayer.units(UnitType.TransportShip).length >= this.game.config().boatMaxNumber()) { @@ -330,7 +330,7 @@ export class RadialMenu implements Layer { let myPlayerBordersOcean = false for (const bt of myPlayer.borderTiles()) { - if (bt.isOceanShore()) { + if (bt.terrain().isOceanShore()) { myPlayerBordersOcean = true break } @@ -340,7 +340,7 @@ export class RadialMenu implements Layer { otherPlayerBordersOcean = true } else { for (const bt of (other as Player).borderTiles()) { - if (bt.isOceanShore()) { + if (bt.terrain().isOceanShore()) { otherPlayerBordersOcean = true break } @@ -352,8 +352,8 @@ export class RadialMenu implements Layer { } let nearOcean = false - for (const t of bfs(tile, and(t => t.owner() == tile.owner() && t.isLand(), dist(tile, 25)))) { - if (t.isOceanShore()) { + for (const t of bfs(tile, and(t => t.owner() == tile.owner() && t.terrain().isLand(), dist(tile, 25)))) { + if (t.terrain().isOceanShore()) { nearOcean = true break } diff --git a/src/core/GameRunner.ts b/src/core/GameRunner.ts index 850304fe0..b336113eb 100644 --- a/src/core/GameRunner.ts +++ b/src/core/GameRunner.ts @@ -1,32 +1,12 @@ import { getConfig } from "./configuration/Config"; import { EventBus } from "./EventBus"; import { Executor } from "./execution/ExecutionManager"; -import { Game } from "./game/Game"; +import { Game, Tile, TileEvent } from "./game/Game"; import { createGame } from "./game/GameImpl"; import { loadTerrainMap } from "./game/TerrainMapLoader"; +import { GameUpdateViewData } from "./GameViewData"; import { GameConfig, Turn } from "./Schemas"; -export interface GameUpdate { - players: PlayerUpdate[] - units: UnitUpdate[] - -} - -export interface PlayerUpdate { - -} - -export interface UnitUpdate { - -} - -export interface TileUpdate { - x: number - y: number - isBorder: boolean - -} - export async function createGameRunner(gameID: string, gameConfig: GameConfig): Promise { const config = getConfig(gameConfig) const terrainMap = await loadTerrainMap(gameConfig.gameMap); @@ -36,13 +16,14 @@ export async function createGameRunner(gameID: string, gameConfig: GameConfig): } export class GameRunner { + private updatedTiles: Tile[] constructor(private game: Game, private eventBus: EventBus, private execManager: Executor) { - + eventBus.on(TileEvent, (e) => { this.updatedTiles.push(e.tile) }) } - public executeNextTick(turn: Turn): GameUpdate { - + public executeNextTick(turn: Turn): GameUpdateViewData { + this.updatedTiles = [] this.game.executeNextTick() return null } diff --git a/src/core/GameView.ts b/src/core/GameView.ts index 1822723f3..024c1f1ca 100644 --- a/src/core/GameView.ts +++ b/src/core/GameView.ts @@ -1,26 +1,319 @@ -import { Cell, PlayerID } from "./game/Game"; -import { GameUpdate } from "./GameRunner"; - -export class TileView { +import { MessageType } from "../client/graphics/layers/EventsDisplay"; +import { Config } from "./configuration/Config"; +import { Alliance, AllianceRequest, AllPlayers, Cell, DefenseBonus, EmojiMessage, Execution, ExecutionView, Game, Gold, Nation, Player, PlayerID, PlayerInfo, PlayerType, Relation, TerrainMap, TerrainTile, TerrainType, TerraNullius, Tick, Tile, Unit, UnitInfo, UnitType } from "./game/Game"; +import { GameUpdateViewData, PlayerViewData, TileViewData, UnitViewData } from "./GameViewData"; +import { ClientID } from "./Schemas"; +export class TileView implements Tile { + constructor(private data: TileViewData, terrain: TerrainTile) { } + isLand(): boolean { + throw new Error("Method not implemented."); + } + isShore(): boolean { + throw new Error("Method not implemented."); + } + isOceanShore(): boolean { + throw new Error("Method not implemented."); + } + isWater(): boolean { + throw new Error("Method not implemented."); + } + isShorelineWater(): boolean { + throw new Error("Method not implemented."); + } + isOcean(): boolean { + throw new Error("Method not implemented."); + } + isLake(): boolean { + throw new Error("Method not implemented."); + } + terrain(): TerrainTile { + throw new Error("Method not implemented."); + } + magnitude(): number { + throw new Error("Method not implemented."); + } + owner(): Player | TerraNullius { + throw new Error("Method not implemented."); + } + hasOwner(): boolean { + throw new Error("Method not implemented."); + } + isBorder(): boolean { + throw new Error("Method not implemented."); + } + borders(other: Player | TerraNullius): boolean { + throw new Error("Method not implemented."); + } + isInterior(): boolean { + throw new Error("Method not implemented."); + } + cell(): Cell { + throw new Error("Method not implemented."); + } + neighbors(): Tile[] { + throw new Error("Method not implemented."); + } + neighborsWrapped(): Tile[] { + throw new Error("Method not implemented."); + } + onShore(): boolean { + throw new Error("Method not implemented."); + } + defenseBonuses(): DefenseBonus[] { + throw new Error("Method not implemented."); + } + defenseBonus(player: Player): number { + throw new Error("Method not implemented."); + } + hasFallout(): boolean { + throw new Error("Method not implemented."); + } + cost(): number { + throw new Error("Method not implemented."); + } + type(): TerrainType { + throw new Error("Method not implemented."); + } } -export class PlayerView { +export class UnitView implements Unit { + constructor(private data: UnitViewData) { } + type(): UnitType { + throw new Error("Method not implemented."); + } + troops(): number { + throw new Error("Method not implemented."); + } + tile(): Tile { + throw new Error("Method not implemented."); + } + owner(): Player { + throw new Error("Method not implemented."); + } + isActive(): boolean { + throw new Error("Method not implemented."); + } + info(): UnitInfo { + throw new Error("Method not implemented."); + } + hasHealth(): boolean { + throw new Error("Method not implemented."); + } + health(): number { + throw new Error("Method not implemented."); + } } -export class GameView { +export class PlayerView implements Player { + constructor(private data: PlayerViewData) { } + info(): PlayerInfo { + throw new Error("Method not implemented."); + } + name(): string { + throw new Error("Method not implemented."); + } + displayName(): string { + throw new Error("Method not implemented."); + } + clientID(): ClientID { + throw new Error("Method not implemented."); + } + id(): PlayerID { + throw new Error("Method not implemented."); + } + type(): PlayerType { + throw new Error("Method not implemented."); + } + units(...types: UnitType[]): Unit[] { + throw new Error("Method not implemented."); + } + ownsTile(cell: Cell): boolean { + throw new Error("Method not implemented."); + } + isAlive(): boolean { + throw new Error("Method not implemented."); + } + borderTiles(): ReadonlySet { + throw new Error("Method not implemented."); + } + isPlayer(): this is Player { + throw new Error("Method not implemented."); + } + neighbors(): (Player | TerraNullius)[] { + throw new Error("Method not implemented."); + } + numTilesOwned(): number { + throw new Error("Method not implemented."); + } + tiles(): ReadonlySet { + throw new Error("Method not implemented."); + } + sharesBorderWith(other: Player | TerraNullius): boolean { + throw new Error("Method not implemented."); + } + incomingAllianceRequests(): AllianceRequest[] { + throw new Error("Method not implemented."); + } + outgoingAllianceRequests(): AllianceRequest[] { + throw new Error("Method not implemented."); + } + alliances(): Alliance[] { + throw new Error("Method not implemented."); + } + allies(): Player[] { + throw new Error("Method not implemented."); + } + isAlliedWith(other: Player): boolean { + throw new Error("Method not implemented."); + } + allianceWith(other: Player): Alliance | null { + throw new Error("Method not implemented."); + } + recentOrPendingAllianceRequestWith(other: Player): boolean { + throw new Error("Method not implemented."); + } + relation(other: Player): Relation { + throw new Error("Method not implemented."); + } + allRelationsSorted(): { player: Player; relation: Relation; }[] { + throw new Error("Method not implemented."); + } + isTraitor(): boolean { + throw new Error("Method not implemented."); + } + canTarget(other: Player): boolean { + throw new Error("Method not implemented."); + } + targets(): Player[] { + throw new Error("Method not implemented."); + } + transitiveTargets(): Player[] { + throw new Error("Method not implemented."); + } + toString(): string { + throw new Error("Method not implemented."); + } + canSendEmoji(recipient: Player | typeof AllPlayers): boolean { + throw new Error("Method not implemented."); + } + outgoingEmojis(): EmojiMessage[] { + throw new Error("Method not implemented."); + } + canDonate(recipient: Player): boolean { + throw new Error("Method not implemented."); + } + gold(): Gold { + throw new Error("Method not implemented."); + } + population(): number { + throw new Error("Method not implemented."); + } + workers(): number { + throw new Error("Method not implemented."); + } + targetTroopRatio(): number { + throw new Error("Method not implemented."); + } + troops(): number { + throw new Error("Method not implemented."); + } + canBuild(type: UnitType, targetTile: Tile): Tile | false { + throw new Error("Method not implemented."); + } + lastTileChange(): Tick { + throw new Error("Method not implemented."); + } +} - public update(gu: GameUpdate) { +export class GameView implements Game { + private lastGameUpdate: GameUpdateViewData + private tiles: TileViewData[][] = [] + constructor(private _terrainMap: TerrainMap) { } + executions(): ExecutionView[] { + throw new Error("Method not implemented."); + } + executeNextTick(): void { + throw new Error("Method not implemented."); } - tile(cell: Cell): TileView { - return null + public update(gu: GameUpdateViewData) { + this.lastGameUpdate = gu + gu.tileUpdates.forEach(tu => { + this.tiles[tu.x][tu.y] = tu + }) } - player(id: PlayerID): PlayerView { - return null + recentlyUpdatedTiles(): TileView[] { + return this.lastGameUpdate.tileUpdates.map(tu => new TileView(tu, this._terrainMap.terrain(new Cell(tu.x, tu.y)))) } + player(id: PlayerID): Player { + throw new Error("Method not implemented."); + } + playerByClientID(id: ClientID): Player | null { + throw new Error("Method not implemented."); + } + hasPlayer(id: PlayerID): boolean { + throw new Error("Method not implemented."); + } + players(): Player[] { + throw new Error("Method not implemented."); + } + tile(cell: Cell): Tile { + throw new Error("Method not implemented."); + } + isOnMap(cell: Cell): boolean { + throw new Error("Method not implemented."); + } + neighbors(cell: Cell | Tile): Tile[] { + throw new Error("Method not implemented."); + } + width(): number { + throw new Error("Method not implemented."); + } + height(): number { + throw new Error("Method not implemented."); + } + numLandTiles(): number { + throw new Error("Method not implemented."); + } + forEachTile(fn: (tile: Tile) => void): void { + throw new Error("Method not implemented."); + } + terraNullius(): TerraNullius { + throw new Error("Method not implemented."); + } + ticks(): Tick { + throw new Error("Method not implemented."); + } + inSpawnPhase(): boolean { + throw new Error("Method not implemented."); + } + addExecution(...exec: Execution[]): void { + throw new Error("Method not implemented."); + } + nations(): Nation[] { + throw new Error("Method not implemented."); + } + config(): Config { + throw new Error("Method not implemented."); + } + displayMessage(message: string, type: MessageType, playerID: PlayerID | null): void { + throw new Error("Method not implemented."); + } + units(...types: UnitType[]): Unit[] { + throw new Error("Method not implemented."); + } + unitInfo(type: UnitType): UnitInfo { + throw new Error("Method not implemented."); + } + terrainMap(): TerrainMap { + throw new Error("Method not implemented."); + } + terrainMiniMap(): TerrainMap { + throw new Error("Method not implemented."); + } } \ No newline at end of file diff --git a/src/core/GameViewData.ts b/src/core/GameViewData.ts new file mode 100644 index 000000000..db592fa26 --- /dev/null +++ b/src/core/GameViewData.ts @@ -0,0 +1,26 @@ +export interface ViewSerialiable { + toViewData(): ViewData +} + +export interface ViewData { + +} + +export interface TileViewData extends ViewData { + x: number + y: number +} + +export interface UnitViewData extends ViewData { + +} + +export interface PlayerViewData extends ViewData { + +} + +export interface GameUpdateViewData extends ViewData { + units: UnitViewData[] + players: PlayerViewData[] + tileUpdates: TileViewData[] +} \ No newline at end of file diff --git a/src/core/Util.ts b/src/core/Util.ts index 4e57422ea..9e5f29774 100644 --- a/src/core/Util.ts +++ b/src/core/Util.ts @@ -86,7 +86,7 @@ export function targetTransportTile(game: Game, tile: Tile): Tile | null { } export function closestOceanShoreFromPlayer(player: Player, target: Tile, width: number): Tile | null { - const shoreTiles = Array.from(player.borderTiles()).filter(t => t.isOceanShore()) + const shoreTiles = Array.from(player.borderTiles()).filter(t => t.terrain().isOceanShore()) if (shoreTiles.length == 0) { return null } @@ -100,7 +100,7 @@ export function closestOceanShoreFromPlayer(player: Player, target: Tile, width: function closestOceanShoreTN(tile: Tile, searchDist: number): Tile { const tn = Array.from(bfs(tile, and(t => !t.hasOwner(), dist(tile, searchDist)))) - .filter(t => t.isOceanShore()) + .filter(t => t.terrain().isOceanShore()) .sort((a, b) => manhattanDist(tile.cell(), a.cell()) - manhattanDist(tile.cell(), b.cell())) if (tn.length == 0) { return null diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts index 20d54664e..98e3e74bc 100644 --- a/src/core/configuration/DefaultConfig.ts +++ b/src/core/configuration/DefaultConfig.ts @@ -189,7 +189,7 @@ export class DefaultConfig implements Config { attackLogic(attackTroops: number, attacker: Player, defender: Player | TerraNullius, tileToConquer: Tile): { attackerTroopLoss: number; defenderTroopLoss: number; tilesPerTickUsed: number } { let mag = 0 let speed = 0 - switch (tileToConquer.terrain()) { + switch (tileToConquer.terrain().type()) { case TerrainType.Plains: mag = 80 speed = 15 diff --git a/src/core/configuration/PastelTheme.ts b/src/core/configuration/PastelTheme.ts index 06b80613b..8854c8ea0 100644 --- a/src/core/configuration/PastelTheme.ts +++ b/src/core/configuration/PastelTheme.ts @@ -155,18 +155,18 @@ export const pastelTheme = new class implements Theme { } terrainColor(tile: Tile): Colord { - let mag = tile.magnitude() - if (tile.isShore()) { + let mag = tile.terrain().magnitude() + if (tile.terrain().isShore()) { return this.shore } - switch (tile.terrain()) { + switch (tile.terrain().type()) { case TerrainType.Ocean: case TerrainType.Lake: const w = this.water.rgba - if (tile.isShorelineWater()) { + if (tile.terrain().isShorelineWater()) { return this.shorelineWater } - if (tile.magnitude() < 7) { + if (tile.terrain().magnitude() < 7) { return colord({ r: Math.max(w.r - 7 + mag, 0), g: Math.max(w.g - 7 + mag, 0), diff --git a/src/core/execution/AttackExecution.ts b/src/core/execution/AttackExecution.ts index fbb1b7b75..e509d4c64 100644 --- a/src/core/execution/AttackExecution.ts +++ b/src/core/execution/AttackExecution.ts @@ -173,12 +173,12 @@ export class AttackExecution implements Execution { private addNeighbors(tile: Tile) { for (const neighbor of tile.neighbors()) { - if (neighbor.isWater() || neighbor.owner() != this.target) { + if (neighbor.terrain().isWater() || neighbor.owner() != this.target) { continue } this.border.add(neighbor) let numOwnedByMe = neighbor.neighbors() - .filter(t => t.isLand()) + .filter(t => t.terrain().isLand()) .filter(t => t.owner() == this._owner) .length let dist = 0 @@ -189,7 +189,7 @@ export class AttackExecution implements Execution { numOwnedByMe = 10 } let mag = 0 - switch (tile.terrain()) { + switch (tile.terrain().type()) { case TerrainType.Plains: mag = 1 break diff --git a/src/core/execution/BattleshipExecution.ts b/src/core/execution/BattleshipExecution.ts index 8417f8ba3..64b9b90c4 100644 --- a/src/core/execution/BattleshipExecution.ts +++ b/src/core/execution/BattleshipExecution.ts @@ -34,7 +34,7 @@ export class BattleshipExecution implements Execution { init(mg: MutableGame, ticks: number): void { - this.pathfinder = PathFinder.Mini(mg, 5000, t => t.terrainType() == TerrainType.Ocean) + this.pathfinder = PathFinder.Mini(mg, 5000, t => t.type() == TerrainType.Ocean) this._owner = mg.player(this.playerID) this.mg = mg this.patrolCenterTile = mg.tile(this.cell) @@ -133,7 +133,7 @@ export class BattleshipExecution implements Execution { continue } const tile = this.mg.tile(cell) - if (!tile.isOcean()) { + if (!tile.terrain().isOcean()) { continue } return tile diff --git a/src/core/execution/BotExecution.ts b/src/core/execution/BotExecution.ts index 7ffc3f297..26bd6b082 100644 --- a/src/core/execution/BotExecution.ts +++ b/src/core/execution/BotExecution.ts @@ -56,7 +56,7 @@ export class BotExecution implements Execution { if (this.neighborsTerraNullius) { for (const b of this.bot.borderTiles()) { for (const n of b.neighbors()) { - if (n.owner() == this.mg.terraNullius() && n.isLand()) { + if (n.owner() == this.mg.terraNullius() && n.terrain().isLand()) { this.sendAttack(this.mg.terraNullius()) return } diff --git a/src/core/execution/BotSpawner.ts b/src/core/execution/BotSpawner.ts index 2ea6706ce..7580a01d7 100644 --- a/src/core/execution/BotSpawner.ts +++ b/src/core/execution/BotSpawner.ts @@ -34,7 +34,7 @@ export class BotSpawner { spawnBot(botName: string): SpawnIntent | null { const tile = this.randTile() - if (!tile.isLand()) { + if (!tile.terrain().isLand()) { return null } for (const spawn of this.bots) { diff --git a/src/core/execution/DefensePostExecution.ts b/src/core/execution/DefensePostExecution.ts index 34e4cb2dc..1f443828b 100644 --- a/src/core/execution/DefensePostExecution.ts +++ b/src/core/execution/DefensePostExecution.ts @@ -30,7 +30,7 @@ export class DefensePostExecution implements Execution { } this.post = this.player.buildUnit(UnitType.DefensePost, 0, spawnTile) bfs(spawnTile, dist(spawnTile, this.mg.config().defensePostRange())).forEach(t => { - if (t.isLand()) { + if (t.terrain().isLand()) { this.defenseBonuses.push(this.mg.addTileDefenseBonus(t, this.post, this.mg.config().defensePostDefenseBonus())) } }) diff --git a/src/core/execution/DestroyerExecution.ts b/src/core/execution/DestroyerExecution.ts index 73bc96453..08bb93929 100644 --- a/src/core/execution/DestroyerExecution.ts +++ b/src/core/execution/DestroyerExecution.ts @@ -30,7 +30,7 @@ export class DestroyerExecution implements Execution { init(mg: MutableGame, ticks: number): void { - this.pathfinder = PathFinder.Mini(mg, 5000, t => t.terrainType() == TerrainType.Ocean) + this.pathfinder = PathFinder.Mini(mg, 5000, t => t.type() == TerrainType.Ocean) this._owner = mg.player(this.playerID) this.mg = mg this.patrolCenterTile = mg.tile(this.cell) @@ -141,7 +141,7 @@ export class DestroyerExecution implements Execution { continue } const tile = this.mg.tile(cell) - if (!tile.isOcean()) { + if (!tile.terrain().isOcean()) { continue } return tile diff --git a/src/core/execution/FakeHumanExecution.ts b/src/core/execution/FakeHumanExecution.ts index b3c6b8ea3..61028db7f 100644 --- a/src/core/execution/FakeHumanExecution.ts +++ b/src/core/execution/FakeHumanExecution.ts @@ -91,7 +91,7 @@ export class FakeHumanExecution implements Execution { this.handleEnemies() this.handleUnits() - const enemyborder = Array.from(this.player.borderTiles()).flatMap(t => t.neighbors()).filter(t => t.isLand() && t.owner() != this.player) + const enemyborder = Array.from(this.player.borderTiles()).flatMap(t => t.neighbors()).filter(t => t.terrain().isLand() && t.owner() != this.player) if (enemyborder.length == 0) { if (this.random.chance(5)) { @@ -243,8 +243,8 @@ export class FakeHumanExecution implements Execution { private maybeSendBoatAttack(other: Player) { const closest = closestTwoTiles( - Array.from(this.player.borderTiles()).filter(t => t.isOceanShore()), - Array.from(other.borderTiles()).filter(t => t.isOceanShore()) + Array.from(this.player.borderTiles()).filter(t => t.terrain().isOceanShore()), + Array.from(other.borderTiles()).filter(t => t.terrain().isOceanShore()) ) if (closest == null) { return @@ -262,7 +262,7 @@ export class FakeHumanExecution implements Execution { private handleUnits() { const ports = this.player.units(UnitType.Port) if (ports.length == 0 && this.player.gold() > this.cost(UnitType.Port)) { - const oceanTiles = Array.from(this.player.borderTiles()).filter(t => t.isOceanShore()) + const oceanTiles = Array.from(this.player.borderTiles()).filter(t => t.terrain().isOceanShore()) if (oceanTiles.length > 0) { const buildTile = this.random.randElement(oceanTiles) this.mg.addExecution(new PortExecution(this.player.id(), buildTile.cell())) @@ -359,7 +359,7 @@ export class FakeHumanExecution implements Execution { continue } const tile = this.mg.tile(cell) - if (!tile.isOcean()) { + if (!tile.terrain().isOcean()) { continue } return tile @@ -402,7 +402,7 @@ export class FakeHumanExecution implements Execution { } if (oceanShore == null) { - oceanShore = Array.from(this.player.borderTiles()).filter(t => t.isOceanShore()) + oceanShore = Array.from(this.player.borderTiles()).filter(t => t.terrain().isOceanShore()) } if (oceanShore.length == 0) { return @@ -412,9 +412,9 @@ export class FakeHumanExecution implements Execution { const otherShore = Array.from( bfs( src, - and((t) => t.isOcean() || t.isOceanShore(), dist(src, 200)) + and((t) => t.terrain().isOcean() || t.terrain().isOceanShore(), dist(src, 200)) ) - ).filter(t => t.isOceanShore() && t.owner() != this.player) + ).filter(t => t.terrain().isOceanShore() && t.owner() != this.player) if (otherShore.length == 0) { return @@ -453,8 +453,8 @@ export class FakeHumanExecution implements Execution { continue } const tile = this.mg.tile(cell) - if (tile.isLand() && !tile.hasOwner()) { - if (tile.terrain() == TerrainType.Mountain && this.random.chance(2)) { + if (tile.terrain().isLand() && !tile.hasOwner()) { + if (tile.terrain().type() == TerrainType.Mountain && this.random.chance(2)) { continue } return tile @@ -474,7 +474,7 @@ export class FakeHumanExecution implements Execution { } isSmallIsland(tile: Tile): boolean { - return bfs(tile, and((t) => t.isLand(), dist(tile, 10))).size < 50 + return bfs(tile, and((t) => t.terrain().isLand(), dist(tile, 10))).size < 50 } owner(): MutablePlayer { diff --git a/src/core/execution/NukeExecution.ts b/src/core/execution/NukeExecution.ts index d007c0c97..701c4ef1a 100644 --- a/src/core/execution/NukeExecution.ts +++ b/src/core/execution/NukeExecution.ts @@ -92,7 +92,7 @@ export class NukeExecution implements Execution { const prev = attacked.get(mp) attacked.set(mp, prev + 1) } - if (tile.isLand()) { + if (tile.terrain().isLand()) { this.mg.addFallout(tile) } } diff --git a/src/core/execution/PlayerExecution.ts b/src/core/execution/PlayerExecution.ts index 11a60a89c..4ca2c8018 100644 --- a/src/core/execution/PlayerExecution.ts +++ b/src/core/execution/PlayerExecution.ts @@ -112,7 +112,7 @@ export class PlayerExecution implements Execution { private surroundedBySamePlayer(cluster: Set): false | Player { const enemies = new Set() for (const tile of cluster) { - if (tile.isOceanShore() || tile.neighbors().find(n => !n.hasOwner())) { + if (tile.terrain().isOceanShore() || tile.neighbors().find(n => !n.hasOwner())) { return false } tile.neighbors() @@ -131,7 +131,7 @@ export class PlayerExecution implements Execution { private isSurrounded(cluster: Set): boolean { let enemyTiles = new Set() for (const tile of cluster) { - if (tile.isOceanShore()) { + if (tile.terrain().isOceanShore()) { return false } tile.neighbors() diff --git a/src/core/execution/PortExecution.ts b/src/core/execution/PortExecution.ts index 848bbd788..7bfa6e716 100644 --- a/src/core/execution/PortExecution.ts +++ b/src/core/execution/PortExecution.ts @@ -41,7 +41,7 @@ export class PortExecution implements Execution { return } const spawns = Array.from(bfs(tile, dist(tile, 20))) - .filter(t => t.isOceanShore() && t.owner() == player) + .filter(t => t.terrain().isOceanShore() && t.owner() == player) .sort((a, b) => manhattanDist(a.cell(), tile.cell()) - manhattanDist(b.cell(), tile.cell())) if (spawns.length == 0) { @@ -89,7 +89,7 @@ export class PortExecution implements Execution { this.mg.terrainMap(), this.mg.terrainMiniMap(), this.port.tile(), port.tile(), - sn => sn.terrainType() == TerrainType.Ocean, + sn => sn.type() == TerrainType.Ocean, 10_000, 25 ) @@ -109,7 +109,7 @@ export class PortExecution implements Execution { const port = this.random.randElement(portConnections) const path = this.portPaths.get(port) if (path != null) { - const pf = PathFinder.Mini(this.mg, 10, (sn) => sn.terrainType() == TerrainType.Ocean) + const pf = PathFinder.Mini(this.mg, 10, (sn) => sn.type() == TerrainType.Ocean) this.mg.addExecution(new TradeShipExecution(this.player().id(), this.port, port, pf, path)) } } diff --git a/src/core/execution/TransportShipExecution.ts b/src/core/execution/TransportShipExecution.ts index 0a3231b87..823bb0a0a 100644 --- a/src/core/execution/TransportShipExecution.ts +++ b/src/core/execution/TransportShipExecution.ts @@ -44,7 +44,7 @@ export class TransportShipExecution implements Execution { init(mg: MutableGame, ticks: number) { this.lastMove = ticks this.mg = mg - this.pathFinder = PathFinder.Mini(mg, 10_000, t => t.terrainType() == TerrainType.Ocean, 2) + this.pathFinder = PathFinder.Mini(mg, 10_000, t => t.type() == TerrainType.Ocean, 2) this.attacker = mg.player(this.attackerID) diff --git a/src/core/execution/Util.ts b/src/core/execution/Util.ts index a1a13af86..fe4a4c946 100644 --- a/src/core/execution/Util.ts +++ b/src/core/execution/Util.ts @@ -12,7 +12,7 @@ export function getSpawnCells(gs: Game, cell: Cell): Cell[] { if (Math.abs(dx) === 2 && Math.abs(dy) === 2) { continue; } - if (gs.tile(c).isWater()) { + if (gs.tile(c).terrain().isWater()) { continue; } if (gs.tile(c).hasOwner()) { diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index b51b9dde7..00b341ec3 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -162,7 +162,15 @@ export interface TerrainMap { } export interface TerrainTile extends SearchNode { - terrainType(): TerrainType + isLand(): boolean + isShore(): boolean + isOceanShore(): boolean + isWater(): boolean + isShorelineWater(): boolean + isOcean(): boolean + isLake(): boolean + type(): TerrainType + magnitude(): number } export interface DefenseBonus { @@ -173,15 +181,6 @@ export interface DefenseBonus { } export interface Tile extends SearchNode { - isLand(): boolean - isShore(): boolean - isOceanShore(): boolean - isWater(): boolean - isShorelineWater(): boolean - isOcean(): boolean - isLake(): boolean - terrain(): TerrainType - magnitude(): number owner(): Player | TerraNullius hasOwner(): boolean isBorder(): boolean @@ -190,12 +189,11 @@ export interface Tile extends SearchNode { cell(): Cell neighbors(): Tile[] neighborsWrapped(): Tile[] - onShore(): boolean - defenseBonuses(): DefenseBonus[] // defense bonus against this player defenseBonus(player: Player): number hasFallout(): boolean + terrain(): TerrainTile } export interface Unit { diff --git a/src/core/game/GameImpl.ts b/src/core/game/GameImpl.ts index 8b6f3b626..8b22c9fad 100644 --- a/src/core/game/GameImpl.ts +++ b/src/core/game/GameImpl.ts @@ -342,7 +342,7 @@ export class GameImpl implements MutableGame { if (!tile.hasOwner()) { throw new Error(`Cannot relinquish tile because it is unowned: cell ${tile.cell().toString()}`) } - if (tile.isWater()) { + if (tile.terrain().isWater()) { throw new Error("Cannot relinquish water") } diff --git a/src/core/game/PlayerImpl.ts b/src/core/game/PlayerImpl.ts index f344aa9a9..4b053b1d2 100644 --- a/src/core/game/PlayerImpl.ts +++ b/src/core/game/PlayerImpl.ts @@ -110,7 +110,7 @@ export class PlayerImpl implements MutablePlayer { 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) { + if (neighbor.terrain().isLand() && neighbor.owner() != this) { ns.add((neighbor as TileImpl)._owner); } } @@ -450,7 +450,7 @@ export class PlayerImpl implements MutablePlayer { portSpawn(tile: Tile): Tile | false { const spawns = Array.from(bfs(tile, dist(tile, 20))) - .filter(t => t.owner() == this && t.isOceanShore()) + .filter(t => t.owner() == this && t.terrain().isOceanShore()) .sort((a, b) => manhattanDist(a.cell(), tile.cell()) - manhattanDist(b.cell(), tile.cell())) if (spawns.length == 0) { return false @@ -459,7 +459,7 @@ export class PlayerImpl implements MutablePlayer { } warshipSpawn(tile: Tile): Tile | false { - if (!tile.isOcean()) { + if (!tile.terrain().isOcean()) { return false } const spawns = this.units(UnitType.Port) @@ -479,7 +479,7 @@ export class PlayerImpl implements MutablePlayer { } transportShipSpawn(targetTile: Tile): Tile | false { - if (!targetTile.isOceanShore()) { + if (!targetTile.terrain().isOceanShore()) { return false } const spawn = closestOceanShoreFromPlayer(this, targetTile, this.gs.width()) diff --git a/src/core/game/TerrainMapLoader.ts b/src/core/game/TerrainMapLoader.ts index f556a2d39..2ed34664c 100644 --- a/src/core/game/TerrainMapLoader.ts +++ b/src/core/game/TerrainMapLoader.ts @@ -20,19 +20,41 @@ export interface Nation { export class TerrainTileImpl implements TerrainTile { public shoreline: boolean = false - public magnitude: number = 0 + public _magnitude: number = 0 public ocean = false public land = false private _neighbors: TerrainTile[] | null = null - constructor(private map: TerrainMap, public type: TerrainType, private _cell: Cell) { } - - terrainType(): TerrainType { - return this.type + constructor(private map: TerrainMap, public _type: TerrainType, private _cell: Cell) { } + type(): TerrainType { + return this._type + } + isLake(): boolean { + return !this.isLand() && !this.isOcean(); + } + isOcean(): boolean { + return this.ocean; + } + magnitude(): number { + return this._magnitude; + } + isShore(): boolean { + return this.isLand() && this.shoreline; + } + isOceanShore(): boolean { + return this.isShore() && this.neighbors().filter(n => n.isOcean()).length > -1; + } + isShorelineWater(): boolean { + return this.isWater() && this.shoreline; + } + isLand(): boolean { + return this.land; + } + isWater(): boolean { + return !this.land; } - cost(): number { - return this.magnitude < 10 ? 2 : 1 + return this._magnitude < 10 ? 2 : 1 } cell(): Cell { @@ -146,7 +168,7 @@ export async function loadTerrainFromFile(fileData: string): Promise 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; + terrain(): TerrainTile { + return this._terrain } borders(other: Player | TerraNullius): boolean { @@ -114,12 +90,6 @@ export class TileImpl implements Tile { 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; } @@ -140,6 +110,6 @@ export class TileImpl implements Tile { } cost(): number { - return this.magnitude() < 10 ? 2 : 1 + return this.terrain().magnitude() < 10 ? 2 : 1 }; } diff --git a/src/core/pathfinding/AStar.ts b/src/core/pathfinding/AStar.ts index e7a1188e4..d9931d80f 100644 --- a/src/core/pathfinding/AStar.ts +++ b/src/core/pathfinding/AStar.ts @@ -26,7 +26,7 @@ export interface SearchNode { cost(): number cell(): Cell neighbors(): SearchNode[] - terrainType(): TerrainType + type(): TerrainType } export interface Point { x: number; diff --git a/src/core/worker/Worker.worker.ts b/src/core/worker/Worker.worker.ts index be783e4fe..ecd4a41e4 100644 --- a/src/core/worker/Worker.worker.ts +++ b/src/core/worker/Worker.worker.ts @@ -53,7 +53,7 @@ function findPath(terrainMap: TerrainMap, miniTerrainMap: TerrainMap, req: Searc miniTerrainMap, terrainMap.terrain(req.start), terrainMap.terrain(req.end), - (sn: SearchNode) => (sn as TerrainTile).terrainType() == TerrainType.Ocean, + (sn: SearchNode) => (sn as TerrainTile).type() == TerrainType.Ocean, 10_000, req.duration, ); diff --git a/src/core/worker/WorkerClient.ts b/src/core/worker/WorkerClient.ts index 4d202d8ad..eb673b133 100644 --- a/src/core/worker/WorkerClient.ts +++ b/src/core/worker/WorkerClient.ts @@ -117,7 +117,7 @@ export class ParallelAStar implements AStar { this.game.terrainMap(), this.game.terrainMiniMap(), this.src, this.dst, - (t: TerrainTile) => t.terrainType() == TerrainType.Ocean, + (t: TerrainTile) => t.type() == TerrainType.Ocean, 100_000_000, 20 )