Files
OpenFrontIO/src/core/pathfinding/PathFinder.Station.ts
T
Arkadiusz Sygulski 216e8ca29f Fix rail pathfinding (#2950)
## Description:

This PR resolves a crash related to rail pathfinding reported on
Discord.

```
git checkout c179249cdd
npm run dev:staging
Replay id: kEbHPSP3
```

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

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

moleole

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
2026-01-18 20:51:22 +00:00

79 lines
2.0 KiB
TypeScript

import { Game } from "../game/Game";
import { StationManager } from "../game/RailNetworkImpl";
import { TrainStation } from "../game/TrainStation";
import { AStar, AStarAdapter } from "./algorithms/AStar";
import { PathFinder } from "./types";
export class StationPathFinder implements PathFinder<TrainStation> {
private manager: StationManager;
private aStar: AStar;
constructor(game: Game) {
this.manager = game.railNetwork().stationManager();
const adapter = new StationGraphAdapter(game, this.manager);
this.aStar = new AStar({ adapter });
}
findPath(
from: TrainStation | TrainStation[],
to: TrainStation,
): TrainStation[] | null {
const toCluster = to.getCluster();
const fromArray = Array.isArray(from) ? from : [from];
const sameCluster = fromArray.filter((s) => s.getCluster() === toCluster);
if (sameCluster.length === 0) return null;
const fromIds = sameCluster.map((s) => s.id);
const path = this.aStar.findPath(fromIds, to.id);
if (!path) return null;
return path.map((id) => this.manager.getById(id)!);
}
}
class StationGraphAdapter implements AStarAdapter {
constructor(
private game: Game,
private manager: StationManager,
) {}
numNodes(): number {
return this.manager.count();
}
maxNeighbors(): number {
return 32;
}
maxPriority(): number {
return this.game.map().width() + this.game.map().height();
}
neighbors(node: number, buffer: Int32Array): number {
const station = this.manager.getById(node);
if (!station) return 0;
let count = 0;
for (const n of station.neighbors()) {
buffer[count++] = n.id;
}
return count;
}
cost(): number {
return 1;
}
heuristic(node: number, goal: number): number {
const a = this.manager.getById(node);
const b = this.manager.getById(goal);
if (!a || !b) return 0;
const ax = this.game.x(a.tile());
const ay = this.game.y(a.tile());
const bx = this.game.x(b.tile());
const by = this.game.y(b.tile());
return Math.abs(ax - bx) + Math.abs(ay - by);
}
}