From c1383d76f1f76c5e5f9be3a6f30b8f3010258540 Mon Sep 17 00:00:00 2001 From: Evan Date: Mon, 3 Mar 2025 19:19:16 -0800 Subject: [PATCH] allow boating on lakes --- src/core/Util.ts | 18 ++--- src/core/execution/PlayerExecution.ts | 2 +- src/core/game/GameMap.ts | 2 +- src/core/game/PlayerImpl.ts | 99 +++++++++++++++------------ src/core/pathfinding/PathFinding.ts | 2 +- 5 files changed, 68 insertions(+), 55 deletions(-) diff --git a/src/core/Util.ts b/src/core/Util.ts index 7b40d0d2b..f462f8c00 100644 --- a/src/core/Util.ts +++ b/src/core/Util.ts @@ -64,12 +64,12 @@ export function sourceDstOceanShore( tile: TileRef, ): [TileRef | null, TileRef | null] { const dst = gm.owner(tile); - let srcTile = closestOceanShoreFromPlayer(gm, src, tile); + let srcTile = closestShoreFromPlayer(gm, src, tile); let dstTile: TileRef | null = null; if (dst.isPlayer()) { - dstTile = closestOceanShoreFromPlayer(gm, dst as Player, tile); + dstTile = closestShoreFromPlayer(gm, dst as Player, tile); } else { - dstTile = closestOceanShoreTN(gm, tile, 300); + dstTile = closestShoreTN(gm, tile, 50); } return [srcTile, dstTile]; } @@ -78,20 +78,20 @@ export function targetTransportTile(gm: Game, tile: TileRef): TileRef | null { const dst = gm.playerBySmallID(gm.ownerID(tile)); let dstTile: TileRef | null = null; if (dst.isPlayer()) { - dstTile = closestOceanShoreFromPlayer(gm, dst as Player, tile); + dstTile = closestShoreFromPlayer(gm, dst as Player, tile); } else { - dstTile = closestOceanShoreTN(gm, tile, 300); + dstTile = closestShoreTN(gm, tile, 50); } return dstTile; } -export function closestOceanShoreFromPlayer( +export function closestShoreFromPlayer( gm: GameMap, player: Player, target: TileRef, ): TileRef | null { const shoreTiles = Array.from(player.borderTiles()).filter((t) => - gm.isOceanShore(t), + gm.isShore(t), ); if (shoreTiles.length == 0) { return null; @@ -112,7 +112,7 @@ export function closestOceanShoreFromPlayer( }); } -function closestOceanShoreTN( +function closestShoreTN( gm: GameMap, tile: TileRef, searchDist: number, @@ -123,7 +123,7 @@ function closestOceanShoreTN( andFN((_, t) => !gm.hasOwner(t), manhattanDistFN(tile, searchDist)), ), ) - .filter((t) => gm.isOceanShore(t)) + .filter((t) => gm.isShore(t)) .sort((a, b) => gm.manhattanDist(tile, a) - gm.manhattanDist(tile, b)); if (tn.length == 0) { return null; diff --git a/src/core/execution/PlayerExecution.ts b/src/core/execution/PlayerExecution.ts index 275e27d91..91a253039 100644 --- a/src/core/execution/PlayerExecution.ts +++ b/src/core/execution/PlayerExecution.ts @@ -128,7 +128,7 @@ export class PlayerExecution implements Execution { const enemies = new Set(); for (const tile of cluster) { const isOceanShore = this.mg.isOceanShore(tile); - if (this.mg.isShore(tile) && !isOceanShore) { + if (this.mg.isOceanShore(tile) && !isOceanShore) { continue; } if ( diff --git a/src/core/game/GameMap.ts b/src/core/game/GameMap.ts index 8bf1b8ce2..b304f2b92 100644 --- a/src/core/game/GameMap.ts +++ b/src/core/game/GameMap.ts @@ -281,9 +281,9 @@ export class GameMapImpl implements GameMap { q.push(tile); while (q.length > 0) { const curr = q.pop(); - seen.add(curr); for (const n of this.neighbors(curr)) { if (!seen.has(n) && filter(this, n)) { + seen.add(n); q.push(n); } } diff --git a/src/core/game/PlayerImpl.ts b/src/core/game/PlayerImpl.ts index 88fb686b9..1085f6527 100644 --- a/src/core/game/PlayerImpl.ts +++ b/src/core/game/PlayerImpl.ts @@ -24,7 +24,7 @@ import { GameUpdateType } from "./GameUpdates"; import { ClientID } from "../Schemas"; import { assertNever, - closestOceanShoreFromPlayer, + closestShoreFromPlayer, distSortUnit, maxInt, minInt, @@ -731,10 +731,10 @@ export class PlayerImpl implements Player { } transportShipSpawn(targetTile: TileRef): TileRef | false { - if (!this.mg.isOceanShore(targetTile)) { + if (!this.mg.isShore(targetTile)) { return false; } - const spawn = closestOceanShoreFromPlayer(this.mg, this, targetTile); + const spawn = closestShoreFromPlayer(this.mg, this, targetTile); if (spawn == null) { return false; } @@ -782,7 +782,6 @@ export class PlayerImpl implements Player { } public canBoat(tile: TileRef): boolean { - const other = this.mg.owner(tile); if ( this.units(UnitType.TransportShip).length >= this.mg.config().boatMaxNumber() @@ -790,54 +789,68 @@ export class PlayerImpl implements Player { return false; } - let myPlayerBordersOcean = false; - for (const bt of this.borderTiles()) { - if (this.mg.isOceanShore(bt)) { - myPlayerBordersOcean = true; - break; - } - } - let otherPlayerBordersOcean = false; - if (!this.mg.hasOwner(tile)) { - otherPlayerBordersOcean = true; - } else { - for (const bt of (other as Player).borderTiles()) { - if (this.mg.isOceanShore(bt)) { - otherPlayerBordersOcean = true; - break; - } - } + const dst = targetTransportTile(this.mg, tile); + if (dst == null) { + return false; } + const other = this.mg.owner(tile); + if (other == this) { + return false; + } if (other.isPlayer() && this.allianceWith(other)) { return false; } - let nearOcean = false; - for (const t of this.mg.bfs( - tile, - andFN( - (gm, t) => gm.ownerID(t) == gm.ownerID(tile) && gm.isLand(t), - manhattanDistFN(tile, 25), - ), - )) { - if (this.mg.isOceanShore(t)) { - nearOcean = true; - break; - } - } - if (!nearOcean) { - return false; - } - - if (myPlayerBordersOcean && otherPlayerBordersOcean) { - const dst = targetTransportTile(this.mg, tile); - if (dst != null) { - if (this.canBuild(UnitType.TransportShip, dst)) { - return true; + if (this.mg.isOceanShore(dst)) { + let myPlayerBordersOcean = false; + for (const bt of this.borderTiles()) { + if (this.mg.isOceanShore(bt)) { + myPlayerBordersOcean = true; + break; } } + + let otherPlayerBordersOcean = false; + if (!this.mg.hasOwner(tile)) { + otherPlayerBordersOcean = true; + } else { + for (const bt of (other as Player).borderTiles()) { + if (this.mg.isOceanShore(bt)) { + otherPlayerBordersOcean = true; + break; + } + } + } + + if (myPlayerBordersOcean && otherPlayerBordersOcean) { + return this.canBuild(UnitType.TransportShip, dst) != false; + } else { + return false; + } } + + // Now we are boating in a lake, so do a bfs from target until we find + // a border tile owned by the player + + const tiles = this.mg.bfs( + dst, + andFN( + manhattanDistFN(dst, 300), + (_, t: TileRef) => this.mg.isLake(t) || this.mg.isShore(t), + ), + ); + + const sorted = Array.from(tiles).sort( + (a, b) => this.mg.manhattanDist(dst, a) - this.mg.manhattanDist(dst, b), + ); + + for (const t of sorted) { + if (this.mg.owner(t) == this) { + return this.canBuild(UnitType.TransportShip, dst) != false; + } + } + return false; } createAttack( diff --git a/src/core/pathfinding/PathFinding.ts b/src/core/pathfinding/PathFinding.ts index 8b1955639..87a059bde 100644 --- a/src/core/pathfinding/PathFinding.ts +++ b/src/core/pathfinding/PathFinding.ts @@ -33,7 +33,7 @@ export class PathFinder { if (canMoveOnLand) { return true; } - return game.miniMap().isOcean(tr); + return game.miniMap().isWater(tr); }, iterations, maxTries,