From 5efd9f4779a23175a18b54aa1b710c9ec9615938 Mon Sep 17 00:00:00 2001 From: evanpelle Date: Sat, 10 Aug 2024 12:29:35 -0700 Subject: [PATCH] removed sharesborderwith cache --- TODO.txt | 6 +- src/client/ClientGame.ts | 2 +- src/core/Game.ts | 12 ++- src/core/GameImpl.ts | 129 ++++++-------------------- src/core/Settings.ts | 2 +- src/core/execution/AttackExecution.ts | 54 +++++------ src/core/execution/BotExecution.ts | 2 +- 7 files changed, 69 insertions(+), 138 deletions(-) diff --git a/TODO.txt b/TODO.txt index 4c58e5803..3185e01b6 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,9 +1,11 @@ * fix conquer expansion DONE * perf improvements on graphics (only draw images to canvas on ticks) DONE * double join lobby bug +* better troop addition logic +* use draw rect instead of image data * improve front page * make boats larger -* better troop addition logic * maybe cache neigbors? * have boats not get close to shore -* better algorithm for name render placement \ No newline at end of file +* better algorithm for name render placement +* re-enable directed expansion \ No newline at end of file diff --git a/src/client/ClientGame.ts b/src/client/ClientGame.ts index 535e906ce..272ea8349 100644 --- a/src/client/ClientGame.ts +++ b/src/client/ClientGame.ts @@ -148,7 +148,7 @@ export class ClientGame { const owner = tile.owner() const targetID = owner.isPlayer() ? owner.id() : null if (tile.owner() != this.myPlayer) { - if (this.myPlayer.sharesBorderWith(tile.owner())) { + if (this.myPlayer.sharesBorderWith(tile.owner())) { this.sendAttackIntent(targetID, cell) } else { // TODO verify on ocean diff --git a/src/core/Game.ts b/src/core/Game.ts index e6b8c5888..904eb0f06 100644 --- a/src/core/Game.ts +++ b/src/core/Game.ts @@ -9,12 +9,17 @@ export type GameID = string export type LobbyID = string export class Cell { + + private strRepr: string + constructor( public readonly x, public readonly y - ) { } + ) { + this.strRepr = `Cell[${this.x},${this.y}]` + } - toString(): string {return `Cell[${this.x},${this.y}]`} + toString(): string {return this.strRepr} } export interface ExecutionView { @@ -85,8 +90,6 @@ export interface MutableBoat extends Boat { export interface TerraNullius { ownsTile(cell: Cell): boolean isPlayer(): false - borderTilesWith(other: Player): ReadonlySet - sharesBorderWith(other: Player): boolean } export interface Player { @@ -98,7 +101,6 @@ export interface Player { isAlive(): boolean executions(): ExecutionView[] borderTiles(): ReadonlySet - borderTilesWith(other: Player | TerraNullius): ReadonlySet isPlayer(): this is Player neighbors(): (Player | TerraNullius)[] numTilesOwned(): number diff --git a/src/core/GameImpl.ts b/src/core/GameImpl.ts index f55dab4a2..a07766db7 100644 --- a/src/core/GameImpl.ts +++ b/src/core/GameImpl.ts @@ -67,77 +67,14 @@ export class BoatImpl implements MutableBoat { } } -class Border { - borderWith: Map> = new Map() - public _borderTiles: Map = new Map() - - constructor(private gs: GameImpl, private player: Player | TerraNullius) { } - - sharesBorderWith(other: Player | TerraNullius): boolean { - if (!this.borderWith.has(other)) { - return false - } - return this.borderWith.get(other).size > 0 - } - - addCalcBorderWithTile(tile: Tile) { - this.gs.neighbors(tile.cell()).map(c => this.gs.tile(c)).forEach(t => { - this.insertBorderWithTile(tile, t.owner()) - }) - } - - removeCalcBorderWithTile(tile: Tile, oldNeighbor: Player | TerraNullius) { - const length = this.gs.neighbors(tile.cell()).map(c => this.gs.tile(c)).filter(t => t.owner() == oldNeighbor).length - if (length == 0) { - this.deleteBorderWithTile(tile, oldNeighbor) - } - } - - insertBorderWithTile(tile: Tile, player: Player | TerraNullius) { - if (!this.borderWith.has(player)) { - this.borderWith.set(player, new Set()) - } - if (player != this.player && tile.terrain() == TerrainTypes.Land) { - this.borderWith.get(player).add(tile) - } - } - - deleteBorderWithTile(tile: Tile, player: Player | TerraNullius) { - if (!this.borderWith.has(player)) { - this.borderWith.set(player, new Set()) - } - this.borderWith.get(player).delete(tile) - } - updateBorderWithTile(tile: Tile, oldOwner: Player | TerraNullius, newOwner: Player | TerraNullius) { - if (!this.borderWith.has(oldOwner)) { - this.borderWith.set(oldOwner, new Set()) - } - if (!this.borderWith.has(newOwner)) { - this.borderWith.set(newOwner, new Set()) - } - - // Delete old neighbors - if (this.gs.tileNeighbors(tile).filter(t => t.owner() == newOwner).length == 0) { - this.borderWith.get(oldOwner).delete(tile) - } - } - - borderTiles(): ReadonlySet { - return new Set(this._borderTiles.values()) - } - borderTilesWith(other: Player | TerraNullius): ReadonlySet { - return this.borderWith.get(other) || new Set(); - } -} - export class PlayerImpl implements MutablePlayer { + public _borderTiles: Map = new Map() + public _borderTileSet: Set = new Set() public _boats: BoatImpl[] = [] public tiles: Map = new Map() - public border: Border constructor(private gs: GameImpl, public readonly _id: PlayerID, public readonly playerInfo: PlayerInfo, private _troops) { - this.border = new Border(gs, this) } addBoat(troops: number, cell: Cell, target: Player | TerraNullius): BoatImpl { @@ -150,24 +87,33 @@ export class PlayerImpl implements MutablePlayer { return this._boats } sharesBorderWith(other: Player | TerraNullius): boolean { - return this.border.sharesBorderWith(other) + for (const border of this._borderTileSet) { + for (const neighbor of border.neighbors()) { + if (neighbor.owner() == other) { + return true + } + } + } + return false } numTilesOwned(): number { return this.tiles.size } borderTiles(): ReadonlySet { - return this.border.borderTiles() + return this._borderTileSet } neighbors(): (MutablePlayer | TerraNullius)[] { - const ns: (MutablePlayer | TerraNullius)[] = [] - for (const [player, tiles] of this.border.borderWith) { - if (tiles.size > 0) { - ns.push(player as MutablePlayer) + const ns: Set<(MutablePlayer | TerraNullius)> = new Set() + for (const border of this.borderTiles()) { + for (const neighbor of border.neighbors()) { + if (neighbor.owner() != this) { + ns.add((neighbor as TileImpl)._owner) + } } } - return ns + return Array.from(ns) } addTroops(troops: number): void { @@ -189,18 +135,13 @@ export class PlayerImpl implements MutablePlayer { executions(): Execution[] { return this.gs.executions().filter(exec => exec.owner().id() == this.id()) } - borderTilesWith(other: Player | TerraNullius): ReadonlySet { - return this.border.borderTilesWith(other) - } } class TerraNulliusImpl implements TerraNullius { public tiles: Map = new Map() - public border: Border constructor(private gs: GameImpl) { - this.border = new Border(gs, this) } id(): PlayerID { @@ -211,13 +152,6 @@ class TerraNulliusImpl implements TerraNullius { } isPlayer(): false {return false as const} - borderTilesWith(other: Player): ReadonlySet { - return this.border.borderTilesWith(other) - } - - sharesBorderWith(other: Player): boolean { - return this.border.sharesBorderWith(other) - } } export class TerrainMapImpl implements TerrainMap { @@ -376,15 +310,18 @@ export class GameImpl implements MutableGame { throw new Error("Must be a player") } let tile = this.tile(cell) as TileImpl + if (tile.terrain() == TerrainTypes.Water) { + throw new Error("Cannot conquer water") + } let previousOwner = tile._owner if (previousOwner.isPlayer()) { previousOwner.tiles.delete(cell.toString()) - previousOwner.border._borderTiles.delete(cell.toString()) + previousOwner._borderTiles.delete(cell.toString()) + previousOwner._borderTileSet.delete(tile) } tile._owner = owner owner.tiles.set(cell.toString(), tile) this.updateBorders(cell) - this.updateBordersWith(tile, previousOwner) this.eventBus.emit(new TileEvent(tile)) } @@ -394,27 +331,15 @@ export class GameImpl implements MutableGame { this.neighbors(cell).forEach(c => cells.push(c)) cells.map(c => this.tile(c)).filter(c => c.hasOwner()).forEach(t => { if (this.isBorder(t)) { - (t.owner() as PlayerImpl).border._borderTiles.set(t.cell().toString(), t) + (t.owner() as PlayerImpl)._borderTiles.set(t.cell().toString(), t); + (t.owner() as PlayerImpl)._borderTileSet.add(t) } else { - (t.owner() as PlayerImpl).border._borderTiles.delete(t.cell().toString()) + (t.owner() as PlayerImpl)._borderTiles.delete(t.cell().toString()); + (t.owner() as PlayerImpl)._borderTileSet.delete(t) } }) } - private updateBordersWith(tile: TileImpl, previousOwner: PlayerImpl | TerraNulliusImpl) { - const newOwner = tile._owner - const neighbors = this.neighbors(tile.cell()).map(c => this.tile(c)) - - newOwner.border.addCalcBorderWithTile(tile) - - neighbors.map(t => (t as TileImpl)).forEach(t => { - const p = t._owner - p.border.addCalcBorderWithTile(t) - p.border.removeCalcBorderWithTile(t, previousOwner) - previousOwner.border.deleteBorderWithTile(tile, p) - }) - } - isBorder(tile: Tile): boolean { this.assertIsOnMap(tile.cell()) if (!tile.hasOwner()) { diff --git a/src/core/Settings.ts b/src/core/Settings.ts index fadb50782..88359276a 100644 --- a/src/core/Settings.ts +++ b/src/core/Settings.ts @@ -25,7 +25,7 @@ export const defaultSettings = new class implements Settings { return 1 } turnIntervalMs(): number { - return 1000 / 10 + return 1000 / 5 } lobbyCreationRate(): number { return 5 * 1000 diff --git a/src/core/execution/AttackExecution.ts b/src/core/execution/AttackExecution.ts index 062d2149e..e1dbefa60 100644 --- a/src/core/execution/AttackExecution.ts +++ b/src/core/execution/AttackExecution.ts @@ -43,14 +43,14 @@ export class AttackExecution implements Execution { // } - let numTilesPerTick = this._owner.borderTilesWith(this.target).size + let numTilesPerTick = this._owner.borderTiles().size / 2 while (numTilesPerTick > 0) { if (this.troops < 1) { this.active = false return } - if (this.toConquer.size() < this._owner.borderTilesWith(this.target).size / 1.5) { + if (this.toConquer.size() < 10) { this.calculateToConquer() } if (this.toConquer.size() == 0) { @@ -74,8 +74,6 @@ export class AttackExecution implements Execution { private calculateToConquer() { // console.profile('calc_to_conquer') - const enemyBorder = this.target.borderTilesWith(this._owner) - // let closestTile: Tile; @@ -106,32 +104,36 @@ export class AttackExecution implements Execution { this.toConquer.clear() - if (this.targetCell != null) { - let tiles = Array.from(enemyBorder) - tiles = tiles.slice().sort((a, b) => manhattanDist(a.cell(), this.targetCell) - manhattanDist(b.cell(), this.targetCell)) - for (let i = 0; i < tiles.length; i++) { - const numOwnedByMe = tiles[i].neighbors() - .filter(t => t.terrain() == TerrainTypes.Land) - .filter(t => t.owner() == this._owner) - .length + // if (this.targetCell != null) { + // let tiles = Array.from(enemyBorder) + // tiles = tiles.slice().sort((a, b) => manhattanDist(a.cell(), this.targetCell) - manhattanDist(b.cell(), this.targetCell)) + // for (let i = 0; i < tiles.length; i++) { + // const numOwnedByMe = tiles[i].neighbors() + // .filter(t => t.terrain() == TerrainTypes.Land) + // .filter(t => t.owner() == this._owner) + // .length - let distModifer = 0 - if (this.targetCell != null) { - distModifer = i / tiles.length * 2 + // let distModifer = 0 + // if (this.targetCell != null) { + // distModifer = i / tiles.length * 2 + // } + // this.toConquer.add(new TileContainer(tiles[i], distModifer - numOwnedByMe + this.random.nextInt(0, 2))) + // // this.toConquer.add(new TileContainer(tiles[i], i)) + // } + // } else { + for (const tile of this._owner.borderTiles()) { + for (const neighbor of tile.neighbors()) { + if (neighbor.terrain() == TerrainTypes.Water || neighbor.owner() != this.target) { + continue } - this.toConquer.add(new TileContainer(tiles[i], distModifer - numOwnedByMe + this.random.nextInt(0, 2))) - // this.toConquer.add(new TileContainer(tiles[i], i)) - } - } else { - for (const tile of enemyBorder) { - const numOwnedByMe = tile.neighbors() - .filter(t => t.terrain() == TerrainTypes.Land) - .filter(t => t.owner() == this._owner) - .length - - this.toConquer.add(new TileContainer(tile, - numOwnedByMe + this.random.nextInt(0, 2))) + // const numOwnedByMe = tile.neighbors() + // .filter(t => t.terrain() == TerrainTypes.Land) + // .filter(t => t.owner() == this._owner) + // .length + this.toConquer.add(new TileContainer(neighbor, + this.random.nextInt(0, 4))) } } + // } // console.profileEnd('calc_to_conquer') diff --git a/src/core/execution/BotExecution.ts b/src/core/execution/BotExecution.ts index f451ad949..d3e091526 100644 --- a/src/core/execution/BotExecution.ts +++ b/src/core/execution/BotExecution.ts @@ -35,7 +35,7 @@ export class BotExecution implements Execution { const toAttack = ns[this.random.nextInt(0, ns.length)] this.gs.addExecution(new AttackExecution( - this.bot.troops() / 15, + this.bot.troops() / 5, this.bot.id(), toAttack.isPlayer() ? toAttack.id() : null, null