diff --git a/TODO.txt b/TODO.txt index dda24999a..bbcbb01d7 100644 --- a/TODO.txt +++ b/TODO.txt @@ -57,12 +57,12 @@ * make bot territory less funky (more likely attack neighbors with larger border) DONE 8/29/2024 * REFACTOR: remove player config DONE 8/29/2024 * give boats limit for how far they can go DONE 8/29/2024 -* PERF: precompute spawns +* boats can go around the world DONE 8/29/2024 +* PERF: more efficient spawns * PERF: load terrain map async * PERF: enable CDN * enable load balancing metrics * end game when no players left (or after 1 hour or so?) -* boats can go around the world * Add terrain elevation to map * use better favicon * REFACTOR: give terranullius an ID, game.player() returns terranullius diff --git a/src/core/Game.ts b/src/core/Game.ts index 93d0024fe..f9ff0ea16 100644 --- a/src/core/Game.ts +++ b/src/core/Game.ts @@ -57,6 +57,7 @@ export interface Tile { cell(): Cell game(): Game neighbors(): Tile[] + neighborsWrapped(): Tile[] onShore(): boolean } diff --git a/src/core/GameImpl.ts b/src/core/GameImpl.ts index c18d35ea8..e502ab5d2 100644 --- a/src/core/GameImpl.ts +++ b/src/core/GameImpl.ts @@ -23,6 +23,40 @@ class TileImpl implements Tile { 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() } diff --git a/src/core/Util.ts b/src/core/Util.ts index 6ba9d4b6c..d85fd8dcc 100644 --- a/src/core/Util.ts +++ b/src/core/Util.ts @@ -5,6 +5,19 @@ export function manhattanDist(c1: Cell, c2: Cell): number { return Math.abs(c1.x - c2.x) + Math.abs(c1.y - c2.y); } +export function manhattenDistWrapped(c1: Cell, c2: Cell, width: number): number { + // Calculate x distance + let dx = Math.abs(c1.x - c2.x); + // Check if wrapping around the x-axis is shorter + dx = Math.min(dx, width - dx); + + // Calculate y distance (no wrapping for y-axis) + let dy = Math.abs(c1.y - c2.y); + + // Return the sum of x and y distances + return dx + dy; +} + export function within(value: number, min: number, max: number): number { return Math.min(Math.max(value, min), max); } diff --git a/src/core/execution/BoatAttackExecution.ts b/src/core/execution/BoatAttackExecution.ts index 37b10b9de..75cfe50f6 100644 --- a/src/core/execution/BoatAttackExecution.ts +++ b/src/core/execution/BoatAttackExecution.ts @@ -1,6 +1,6 @@ import {PriorityQueue} from "@datastructures-js/priority-queue"; import {Boat, Cell, Execution, MutableBoat, MutableGame, MutablePlayer, Player, PlayerID, TerraNullius, Tile, TileEvent} from "../Game"; -import {manhattanDist} from "../Util"; +import {manhattanDist, manhattenDistWrapped} from "../Util"; import {AttackExecution} from "./AttackExecution"; import {Config} from "../configuration/Config"; @@ -67,7 +67,7 @@ export class BoatAttackExecution implements Execution { this.active = false return } - if (manhattanDist(this.src.cell(), this.dst.cell()) > mg.config().boatMaxDistance()) { + if (manhattenDistWrapped(this.src.cell(), this.dst.cell(), mg.width()) > mg.config().boatMaxDistance()) { console.log(`boat attack distance too large, dist ${manhattanDist(this.src.cell(), this.dst.cell())} max: ${mg.config().boatMaxDistance()}`) this.active = false return @@ -179,7 +179,7 @@ export class AStar { return true; } - for (const neighbor of this.current.neighbors()) { + for (const neighbor of this.current.neighborsWrapped()) { if (neighbor != this.dst && neighbor.isLand()) continue; // Skip non-water tiles const tentativeGScore = this.gScore.get(this.current)! + 100 - neighbor.magnitude();