mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-14 17:56:05 +00:00
Fix cache pathfinding (#848)
## Description: Add full path to caches Fix A* to always start and end at given start and end Remove pathfinder from tradeshipexecution Should fix tradeship looping and getting stuck at ports Fix #843 Dont know what to add so that description check is happy [More lines to lines to make it happy] ## 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: Vivacious Box --------- Co-authored-by: evanpelle <evanpelle@gmail.com>
This commit is contained in:
co-authored by
evanpelle
parent
5b27bee3bd
commit
27792428a0
@@ -8,7 +8,6 @@ import {
|
||||
UnitType,
|
||||
} from "../game/Game";
|
||||
import { TileRef } from "../game/GameMap";
|
||||
import { PathFinder } from "../pathfinding/PathFinding";
|
||||
import { PseudoRandom } from "../PseudoRandom";
|
||||
import { TradeShipExecution } from "./TradeShipExecution";
|
||||
|
||||
@@ -79,9 +78,8 @@ export class PortExecution implements Execution {
|
||||
}
|
||||
|
||||
const port = this.random.randElement(ports);
|
||||
const pf = PathFinder.Mini(this.mg, 2500);
|
||||
this.mg.addExecution(
|
||||
new TradeShipExecution(this.player().id(), this.port, port, pf),
|
||||
new TradeShipExecution(this.player().id(), this.port, port),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ import {
|
||||
UnitType,
|
||||
} from "../game/Game";
|
||||
import { TileRef } from "../game/GameMap";
|
||||
import { PathFindResultType } from "../pathfinding/AStar";
|
||||
import { PathFinder } from "../pathfinding/PathFinding";
|
||||
import { AStar, PathFindResultType } from "../pathfinding/AStar";
|
||||
import { MiniAStar } from "../pathfinding/MiniAStar";
|
||||
import { distSortUnit } from "../Util";
|
||||
|
||||
export class TradeShipExecution implements Execution {
|
||||
@@ -19,15 +19,14 @@ export class TradeShipExecution implements Execution {
|
||||
private mg: Game | null = null;
|
||||
private origOwner: Player | null = null;
|
||||
private tradeShip: Unit | null = null;
|
||||
private index = 0;
|
||||
private wasCaptured = false;
|
||||
private tilesTraveled = 0;
|
||||
private aStar: AStar | null = null;
|
||||
|
||||
constructor(
|
||||
private _owner: PlayerID,
|
||||
private srcPort: Unit,
|
||||
private _dstPort: Unit,
|
||||
private pathFinder: PathFinder,
|
||||
) {}
|
||||
|
||||
init(mg: Game, ticks: number): void {
|
||||
@@ -103,46 +102,82 @@ export class TradeShipExecution implements Execution {
|
||||
|
||||
const cachedNextTile = this._dstPort.cacheGet(this.tradeShip.tile());
|
||||
if (cachedNextTile !== undefined) {
|
||||
if (
|
||||
this.mg.isWater(cachedNextTile) &&
|
||||
this.mg.isShoreline(cachedNextTile)
|
||||
) {
|
||||
this.tradeShip.setSafeFromPirates();
|
||||
}
|
||||
this.tradeShip.move(cachedNextTile);
|
||||
this.tilesTraveled++;
|
||||
this.moveTradeShip(cachedNextTile);
|
||||
return;
|
||||
}
|
||||
|
||||
const result = this.pathFinder.nextTile(
|
||||
this.tradeShip.tile(),
|
||||
this._dstPort.tile(),
|
||||
);
|
||||
this.computeNewPath();
|
||||
}
|
||||
|
||||
switch (result.type) {
|
||||
case PathFindResultType.Completed:
|
||||
this.complete();
|
||||
break;
|
||||
private fillCachePath(port: Unit, path: TileRef[]): void {
|
||||
if (path.length < 2) {
|
||||
throw new Error("path must have at least 2 points");
|
||||
}
|
||||
for (let i = 0; i < path.length - 1; i++) {
|
||||
if (port.cacheGet(path[i]) !== undefined) {
|
||||
continue;
|
||||
}
|
||||
const from = path[i];
|
||||
const to = path[i + 1];
|
||||
port.cachePut(from, to);
|
||||
}
|
||||
}
|
||||
|
||||
private moveTradeShip(nextTile?: TileRef): void {
|
||||
if (nextTile === undefined) {
|
||||
throw new Error("missing tile");
|
||||
}
|
||||
|
||||
if (nextTile === this._dstPort.tile()) {
|
||||
this.complete();
|
||||
return;
|
||||
}
|
||||
// Update safeFromPirates status
|
||||
if (this.mg!.isWater(nextTile) && this.mg!.isShoreline(nextTile)) {
|
||||
this.tradeShip!.setSafeFromPirates();
|
||||
}
|
||||
this.tradeShip!.move(nextTile);
|
||||
this.tilesTraveled++;
|
||||
}
|
||||
|
||||
private computeNewPath(): void {
|
||||
if (this.aStar === null) {
|
||||
this.aStar = new MiniAStar(
|
||||
this.mg!,
|
||||
this.mg!.miniMap(),
|
||||
this.tradeShip!.tile(),
|
||||
this._dstPort.tile(),
|
||||
2500,
|
||||
20,
|
||||
);
|
||||
}
|
||||
|
||||
switch (this.aStar.compute()) {
|
||||
case PathFindResultType.Pending:
|
||||
// Fire unit event to rerender.
|
||||
this.tradeShip.touch();
|
||||
this.tradeShip!.touch();
|
||||
break;
|
||||
case PathFindResultType.NextTile:
|
||||
this._dstPort.cachePut(this.tradeShip.tile(), result.tile);
|
||||
// Update safeFromPirates status
|
||||
if (this.mg.isWater(result.tile) && this.mg.isShoreline(result.tile)) {
|
||||
this.tradeShip.setSafeFromPirates();
|
||||
case PathFindResultType.Completed: {
|
||||
const fullPath = this.aStar.reconstructPath();
|
||||
if (fullPath === null || fullPath.length === 0) {
|
||||
throw new Error("missing path");
|
||||
}
|
||||
this.tradeShip.move(result.tile);
|
||||
this.tilesTraveled++;
|
||||
this.fillCachePath(this._dstPort, fullPath);
|
||||
if (!this.wasCaptured) {
|
||||
this.fillCachePath(this.srcPort, fullPath.slice().reverse());
|
||||
}
|
||||
this.moveTradeShip(fullPath.shift());
|
||||
break;
|
||||
}
|
||||
case PathFindResultType.PathNotFound:
|
||||
consolex.warn("captured trade ship cannot find route");
|
||||
if (this.tradeShip.isActive()) {
|
||||
this.tradeShip.delete(false);
|
||||
consolex.warn("trade ship cannot find route");
|
||||
if (this.tradeShip!.isActive()) {
|
||||
this.tradeShip!.delete(false);
|
||||
}
|
||||
this.active = false;
|
||||
break;
|
||||
default:
|
||||
throw new Error("unexpected path finding compute result");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -215,7 +215,8 @@ export class TransportShipExecution implements Execution {
|
||||
break;
|
||||
case PathFindResultType.PathNotFound:
|
||||
// TODO: add to poisoned port list
|
||||
consolex.warn(`path not found tot dst`);
|
||||
consolex.warn(`path not found to dst`);
|
||||
this.attacker.addTroops(this.troops);
|
||||
this.boat.delete(false);
|
||||
this.active = false;
|
||||
return;
|
||||
|
||||
@@ -9,7 +9,7 @@ export class MiniAStar implements AStar {
|
||||
constructor(
|
||||
private gameMap: GameMap,
|
||||
private miniMap: GameMap,
|
||||
src: TileRef | TileRef[],
|
||||
private src: TileRef | TileRef[],
|
||||
private dst: TileRef,
|
||||
iterations: number,
|
||||
maxTries: number,
|
||||
@@ -41,16 +41,52 @@ export class MiniAStar implements AStar {
|
||||
}
|
||||
|
||||
reconstructPath(): TileRef[] {
|
||||
const upscaled = upscalePath(
|
||||
this.aStar
|
||||
.reconstructPath()
|
||||
.map((tr) => new Cell(this.miniMap.x(tr), this.miniMap.y(tr))),
|
||||
let cellSrc: Cell | undefined;
|
||||
if (!Array.isArray(this.src)) {
|
||||
cellSrc = new Cell(this.gameMap.x(this.src), this.gameMap.y(this.src));
|
||||
}
|
||||
const cellDst = new Cell(
|
||||
this.gameMap.x(this.dst),
|
||||
this.gameMap.y(this.dst),
|
||||
);
|
||||
const upscaled = fixExtremes(
|
||||
upscalePath(
|
||||
this.aStar
|
||||
.reconstructPath()
|
||||
.map((tr) => new Cell(this.miniMap.x(tr), this.miniMap.y(tr))),
|
||||
),
|
||||
cellDst,
|
||||
cellSrc,
|
||||
);
|
||||
upscaled.push(new Cell(this.gameMap.x(this.dst), this.gameMap.y(this.dst)));
|
||||
return upscaled.map((c) => this.gameMap.ref(c.x, c.y));
|
||||
}
|
||||
}
|
||||
|
||||
function fixExtremes(upscaled: Cell[], cellDst: Cell, cellSrc?: Cell): Cell[] {
|
||||
if (cellSrc !== undefined) {
|
||||
const srcIndex = findCell(upscaled, cellSrc);
|
||||
if (srcIndex === -1) {
|
||||
// didnt find the start tile in the path
|
||||
upscaled.unshift(cellSrc);
|
||||
} else if (srcIndex !== 0) {
|
||||
// found start tile but not at the start
|
||||
// remove all tiles before the start tile
|
||||
upscaled = upscaled.slice(srcIndex);
|
||||
}
|
||||
}
|
||||
|
||||
const dstIndex = findCell(upscaled, cellDst);
|
||||
if (dstIndex === -1) {
|
||||
// didnt find the dst tile in the path
|
||||
upscaled.push(cellDst);
|
||||
} else if (dstIndex !== upscaled.length - 1) {
|
||||
// found dst tile but not at the end
|
||||
// remove all tiles after the dst tile
|
||||
upscaled = upscaled.slice(0, dstIndex + 1);
|
||||
}
|
||||
return upscaled;
|
||||
}
|
||||
|
||||
function upscalePath(path: Cell[], scaleFactor: number = 2): Cell[] {
|
||||
// Scale up each point
|
||||
const scaledPath = path.map(
|
||||
@@ -92,3 +128,12 @@ function upscalePath(path: Cell[], scaleFactor: number = 2): Cell[] {
|
||||
|
||||
return smoothPath;
|
||||
}
|
||||
|
||||
function findCell(upscaled: Cell[], cellDst: Cell): number {
|
||||
for (let i = 0; i < upscaled.length; i++) {
|
||||
if (upscaled[i].x === cellDst.x && upscaled[i].y === cellDst.y) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user