mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-17 04:09:24 +00:00
Add coarse-to-fine pathfinding for water navigation
- Introduced a new `CoarseToFineWaterPath` module to enhance pathfinding efficiency for boats by utilizing a coarse map to guide fine path searches. - Implemented a two-stage pathfinding approach: a coarse search on a low-resolution map followed by a refined search on the full-resolution map. - Updated `TransportShipExecution` and `TransportShipUtils` to leverage the new coarse-to-fine pathfinding method, improving routing decisions and handling of retreat paths. - Added tests to validate the new pathfinding functionality and ensure robustness in various scenarios.
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
import { findWaterPathFromSeedsCoarseToFine } from "../../../src/core/pathfinding/CoarseToFineWaterPath";
|
||||
|
||||
type TileRef = number;
|
||||
|
||||
function makeGridWaterMap(w: number, h: number, water: boolean[]) {
|
||||
const num = w * h;
|
||||
if (water.length !== num) throw new Error("bad water array");
|
||||
return {
|
||||
width: () => w,
|
||||
height: () => h,
|
||||
x: (ref: TileRef) => ref % w,
|
||||
y: (ref: TileRef) => Math.floor(ref / w),
|
||||
isWater: (ref: TileRef) => water[ref] === true,
|
||||
} as any;
|
||||
}
|
||||
|
||||
function coarseCellOfFine(ref: TileRef, fineW: number): number {
|
||||
const x = ref % fineW;
|
||||
const y = Math.floor(ref / fineW);
|
||||
const cx = x >= 4 ? 1 : 0;
|
||||
const cy = y >= 4 ? 1 : 0;
|
||||
return cy * 2 + cx;
|
||||
}
|
||||
|
||||
describe("findWaterPathFromSeedsCoarseToFine (Option A)", () => {
|
||||
it("finds a route inside a tight corridor on all-water maps", () => {
|
||||
const fineW = 8;
|
||||
const fineH = 8;
|
||||
const fine = makeGridWaterMap(fineW, fineH, new Array(64).fill(true));
|
||||
const coarse = makeGridWaterMap(2, 2, new Array(4).fill(true));
|
||||
|
||||
const res = findWaterPathFromSeedsCoarseToFine(
|
||||
fine,
|
||||
[0],
|
||||
[0],
|
||||
[63],
|
||||
{ kingMoves: true, noCornerCutting: true },
|
||||
coarse,
|
||||
{ corridorRadius: 0, maxAttempts: 1 },
|
||||
);
|
||||
|
||||
expect(res).not.toBeNull();
|
||||
// Corridor from coarse path [0,3] at radius 0 only allows coarse cells {0,3}.
|
||||
const usedCells = new Set(
|
||||
res!.path.map((t) => coarseCellOfFine(t, fineW)),
|
||||
);
|
||||
expect(Array.from(usedCells).sort()).toEqual([0, 3]);
|
||||
});
|
||||
|
||||
it("falls back to unrestricted fine BFS when the corridor is too tight", () => {
|
||||
const fineW = 8;
|
||||
const fineH = 8;
|
||||
const water = new Array(64).fill(true) as boolean[];
|
||||
|
||||
// Block the central no-corner-cutting diagonal from (3,3)->(4,4):
|
||||
// it requires both orthogonals (4,3) and (3,4) to be water.
|
||||
water[4 + 3 * fineW] = false; // (4,3)
|
||||
water[3 + 4 * fineW] = false; // (3,4)
|
||||
|
||||
const fine = makeGridWaterMap(fineW, fineH, water);
|
||||
const coarse = makeGridWaterMap(2, 2, new Array(4).fill(true));
|
||||
|
||||
const res = findWaterPathFromSeedsCoarseToFine(
|
||||
fine,
|
||||
[0],
|
||||
[0],
|
||||
[63],
|
||||
{ kingMoves: true, noCornerCutting: true },
|
||||
coarse,
|
||||
{ corridorRadius: 0, maxAttempts: 1 },
|
||||
);
|
||||
|
||||
expect(res).not.toBeNull();
|
||||
// With the diagonal blocked, any fine path from cell 0 to cell 3 must pass through cell 1 or 2.
|
||||
const usedCells = new Set(
|
||||
res!.path.map((t) => coarseCellOfFine(t, fineW)),
|
||||
);
|
||||
expect(usedCells.has(1) || usedCells.has(2)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user