Multi src astar (#594)

## Description:
Samples border shore tiles and uses multi-a* for determining the
transport ship spawn cell.

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:

<DISCORD USERNAME>
evan

---------

Co-authored-by: evan <openfrontio@gmail.com>
This commit is contained in:
evanpelle
2025-04-23 10:16:43 -07:00
committed by GitHub
parent b2c3a8add6
commit 84287b8dfa
14 changed files with 167 additions and 126 deletions
+60 -23
View File
@@ -4,29 +4,42 @@ import { GameMap, TileRef } from "../game/GameMap";
import { AStar, PathFindResultType } from "./AStar";
export class SerialAStar implements AStar {
private fwdOpenSet: PriorityQueue<{ tile: TileRef; fScore: number }>;
private bwdOpenSet: PriorityQueue<{ tile: TileRef; fScore: number }>;
private fwdOpenSet: PriorityQueue<{
tile: TileRef;
fScore: number;
}>;
private bwdOpenSet: PriorityQueue<{
tile: TileRef;
fScore: number;
}>;
private fwdCameFrom: Map<TileRef, TileRef>;
private bwdCameFrom: Map<TileRef, TileRef>;
private fwdGScore: Map<TileRef, number>;
private bwdGScore: Map<TileRef, number>;
private meetingPoint: TileRef | null;
public completed: boolean;
private sources: TileRef[];
private closestSource: TileRef;
constructor(
private src: TileRef,
src: TileRef | TileRef[],
private dst: TileRef,
private canMove: (t: TileRef) => boolean,
private iterations: number,
private maxTries: number,
private gameMap: GameMap,
) {
this.fwdOpenSet = new PriorityQueue<{ tile: TileRef; fScore: number }>(
(a, b) => a.fScore - b.fScore,
);
this.bwdOpenSet = new PriorityQueue<{ tile: TileRef; fScore: number }>(
(a, b) => a.fScore - b.fScore,
);
this.fwdOpenSet = new PriorityQueue<{
tile: TileRef;
fScore: number;
}>((a, b) => a.fScore - b.fScore);
this.bwdOpenSet = new PriorityQueue<{
tile: TileRef;
fScore: number;
}>((a, b) => a.fScore - b.fScore);
this.fwdCameFrom = new Map<TileRef, TileRef>();
this.bwdCameFrom = new Map<TileRef, TileRef>();
this.fwdGScore = new Map<TileRef, number>();
@@ -34,13 +47,32 @@ export class SerialAStar implements AStar {
this.meetingPoint = null;
this.completed = false;
// Initialize forward search
this.fwdGScore.set(src, 0);
this.fwdOpenSet.enqueue({ tile: src, fScore: this.heuristic(src, dst) });
this.sources = Array.isArray(src) ? src : [src];
this.closestSource = this.findClosestSource(dst);
// Initialize backward search
// Initialize forward search with source point(s)
this.sources.forEach((startPoint) => {
this.fwdGScore.set(startPoint, 0);
this.fwdOpenSet.enqueue({
tile: startPoint,
fScore: this.heuristic(startPoint, dst),
});
});
// Initialize backward search from destination
this.bwdGScore.set(dst, 0);
this.bwdOpenSet.enqueue({ tile: dst, fScore: this.heuristic(dst, src) });
this.bwdOpenSet.enqueue({
tile: dst,
fScore: this.heuristic(dst, this.findClosestSource(dst)),
});
}
private findClosestSource(tile: TileRef): TileRef {
return this.sources.reduce((closest, source) =>
this.heuristic(tile, source) < this.heuristic(tile, closest)
? source
: closest,
);
}
compute(): PathFindResultType {
@@ -60,8 +92,9 @@ export class SerialAStar implements AStar {
// Process forward search
const fwdCurrent = this.fwdOpenSet.dequeue()!.tile;
// Check if we've found a meeting point
if (this.bwdGScore.has(fwdCurrent)) {
// We found a meeting point!
this.meetingPoint = fwdCurrent;
this.completed = true;
return PathFindResultType.Completed;
@@ -71,8 +104,9 @@ export class SerialAStar implements AStar {
// Process backward search
const bwdCurrent = this.bwdOpenSet.dequeue()!.tile;
// Check if we've found a meeting point
if (this.fwdGScore.has(bwdCurrent)) {
// We found a meeting point!
this.meetingPoint = bwdCurrent;
this.completed = true;
return PathFindResultType.Completed;
@@ -89,8 +123,8 @@ export class SerialAStar implements AStar {
private expandTileRef(current: TileRef, isForward: boolean) {
for (const neighbor of this.gameMap.neighbors(current)) {
if (
neighbor != (isForward ? this.dst : this.src) &&
!this.canMove(neighbor)
neighbor != (isForward ? this.dst : this.closestSource) &&
!this.gameMap.isWater(neighbor)
)
continue;
@@ -106,21 +140,22 @@ export class SerialAStar implements AStar {
gScore.set(neighbor, tentativeGScore);
const fScore =
tentativeGScore +
this.heuristic(neighbor, isForward ? this.dst : this.src);
this.heuristic(neighbor, isForward ? this.dst : this.closestSource);
openSet.enqueue({ tile: neighbor, fScore: fScore });
}
}
}
private heuristic(a: TileRef, b: TileRef): number {
// TODO use wrapped
try {
return (
1.1 * Math.abs(this.gameMap.x(a) - this.gameMap.x(b)) +
Math.abs(this.gameMap.y(a) - this.gameMap.y(b))
1.1 *
(Math.abs(this.gameMap.x(a) - this.gameMap.x(b)) +
Math.abs(this.gameMap.y(a) - this.gameMap.y(b)))
);
} catch {
consolex.log("uh oh");
return 0;
}
}
@@ -130,6 +165,7 @@ export class SerialAStar implements AStar {
// Reconstruct path from start to meeting point
const fwdPath: TileRef[] = [this.meetingPoint];
let current = this.meetingPoint;
while (this.fwdCameFrom.has(current)) {
current = this.fwdCameFrom.get(current)!;
fwdPath.unshift(current);
@@ -137,6 +173,7 @@ export class SerialAStar implements AStar {
// Reconstruct path from meeting point to goal
current = this.meetingPoint;
while (this.bwdCameFrom.has(current)) {
current = this.bwdCameFrom.get(current)!;
fwdPath.push(current);