diff --git a/src/core/execution/BattleshipExecution.ts b/src/core/execution/BattleshipExecution.ts index 7699d94b8..fcd0d7443 100644 --- a/src/core/execution/BattleshipExecution.ts +++ b/src/core/execution/BattleshipExecution.ts @@ -14,7 +14,7 @@ export class BattleshipExecution implements Execution { private battleship: MutableUnit = null private mg: MutableGame = null - private pathfinder = PathFinder.Serial(5000, t => t.isWater()) + private pathfinder: PathFinder private patrolTile: Tile; private patrolCenterTile: Tile @@ -31,6 +31,7 @@ export class BattleshipExecution implements Execution { init(mg: MutableGame, ticks: number): void { + this.pathfinder = PathFinder.Serial(mg, 5000, t => t.isWater()) this._owner = mg.player(this.playerID) this.mg = mg this.patrolCenterTile = mg.tile(this.cell) diff --git a/src/core/execution/DestroyerExecution.ts b/src/core/execution/DestroyerExecution.ts index 373189bfe..cd2605e13 100644 --- a/src/core/execution/DestroyerExecution.ts +++ b/src/core/execution/DestroyerExecution.ts @@ -1,4 +1,4 @@ -import { Cell, Execution, MutableGame, MutablePlayer, MutableUnit, PlayerID, Tile, UnitType } from "../game/Game"; +import { Cell, Execution, MutableGame, MutablePlayer, MutableUnit, PlayerID, TerrainType, Tile, UnitType } from "../game/Game"; import { PathFinder } from "../pathfinding/PathFinding"; import { PathFindResultType } from "../pathfinding/AStar"; import { SerialAStar } from "../pathfinding/SerialAStar"; @@ -14,7 +14,7 @@ export class DestroyerExecution implements Execution { private mg: MutableGame = null private target: MutableUnit = null - private pathfinder = PathFinder.Serial(5000, t => t.isWater()) + private pathfinder: PathFinder private patrolTile: Tile; private patrolCenterTile: Tile @@ -29,6 +29,7 @@ export class DestroyerExecution implements Execution { init(mg: MutableGame, ticks: number): void { + this.pathfinder = PathFinder.Serial(mg, 5000, t => t.terrainType() == TerrainType.Ocean) this._owner = mg.player(this.playerID) this.mg = mg this.patrolCenterTile = mg.tile(this.cell) diff --git a/src/core/execution/NukeExecution.ts b/src/core/execution/NukeExecution.ts index df4570c78..8e819350b 100644 --- a/src/core/execution/NukeExecution.ts +++ b/src/core/execution/NukeExecution.ts @@ -16,7 +16,7 @@ export class NukeExecution implements Execution { private nuke: MutableUnit private dst: Tile - private pathFinder: PathFinder = PathFinder.Serial(10_000, () => true) + private pathFinder: PathFinder constructor( private type: UnitType.AtomBomb | UnitType.HydrogenBomb, private senderID: PlayerID, @@ -26,6 +26,7 @@ export class NukeExecution implements Execution { init(mg: MutableGame, ticks: number): void { this.mg = mg + this.pathFinder = PathFinder.Serial(mg, 10_000, () => true) this.player = mg.player(this.senderID) this.dst = this.mg.tile(this.cell) } diff --git a/src/core/execution/PortExecution.ts b/src/core/execution/PortExecution.ts index 64d1589d2..df1d7ab67 100644 --- a/src/core/execution/PortExecution.ts +++ b/src/core/execution/PortExecution.ts @@ -72,7 +72,7 @@ export class PortExecution implements Execution { const aStar = this.computingPaths.get(port) switch (aStar.compute()) { case PathFindResultType.Completed: - this.portPaths.set(port, aStar.reconstructPath().map(sn => sn as Tile)) + this.portPaths.set(port, aStar.reconstructPath().map(cell => this.mg.tile(cell))) this.computingPaths.delete(port) break case PathFindResultType.Pending: @@ -101,7 +101,7 @@ export class PortExecution implements Execution { const port = this.random.randElement(portConnections) const path = this.portPaths.get(port) if (path != null) { - const pf = PathFinder.Parallel(this.worker, 30) + const pf = PathFinder.Parallel(this.mg, this.worker, 30) this.mg.addExecution(new TradeShipExecution(this.player().id(), this.port, port, pf, path)) } } diff --git a/src/core/execution/ShellExecution.ts b/src/core/execution/ShellExecution.ts index 7d92e5169..0be6cf368 100644 --- a/src/core/execution/ShellExecution.ts +++ b/src/core/execution/ShellExecution.ts @@ -5,7 +5,7 @@ import { PathFindResultType } from "../pathfinding/AStar"; export class ShellExecution implements Execution { private active = true - private pathFinder = PathFinder.Serial(2000, () => true, 10) + private pathFinder: PathFinder private shell: MutableUnit constructor(private spawn: Tile, private _owner: MutablePlayer, private target: MutableUnit) { @@ -13,6 +13,7 @@ export class ShellExecution implements Execution { } init(mg: MutableGame, ticks: number): void { + this.pathFinder = PathFinder.Serial(mg, 2000, () => true, 10) } tick(ticks: number): void { diff --git a/src/core/execution/TransportShipExecution.ts b/src/core/execution/TransportShipExecution.ts index 3126b8180..6c7c0ff40 100644 --- a/src/core/execution/TransportShipExecution.ts +++ b/src/core/execution/TransportShipExecution.ts @@ -27,7 +27,7 @@ export class TransportShipExecution implements Execution { private boat: MutableUnit - private pathFinder: PathFinder = PathFinder.Serial(10_000, t => t.isWater(), 2) + private pathFinder: PathFinder constructor( private attackerID: PlayerID, @@ -43,6 +43,7 @@ export class TransportShipExecution implements Execution { init(mg: MutableGame, ticks: number) { this.lastMove = ticks this.mg = mg + this.pathFinder = PathFinder.Serial(mg, 10_000, t => t.isWater(), 2) this.attacker = mg.player(this.attackerID) diff --git a/src/core/pathfinding/AStar.ts b/src/core/pathfinding/AStar.ts index 8863be5bd..e7a1188e4 100644 --- a/src/core/pathfinding/AStar.ts +++ b/src/core/pathfinding/AStar.ts @@ -2,7 +2,7 @@ import { Cell, TerrainType, Tile } from "../game/Game"; export interface AStar { compute(): PathFindResultType - reconstructPath(): SearchNode[] + reconstructPath(): Cell[] } export enum PathFindResultType { diff --git a/src/core/pathfinding/MiniAStar.ts b/src/core/pathfinding/MiniAStar.ts index 4cda90a52..e71795869 100644 --- a/src/core/pathfinding/MiniAStar.ts +++ b/src/core/pathfinding/MiniAStar.ts @@ -31,29 +31,22 @@ export class MiniAStar implements AStar { return this.aStar.compute() } - reconstructPath(): SearchNode[] { + reconstructPath(): Cell[] { const upscaled = upscalePath(this.aStar.reconstructPath()) - .map(p => this.terrainMap.terrain(new Cell(p.x, p.y))) as SearchNode[] - upscaled.push(this.dst) - return upscaled - } - - reconstructPathAsPoints(): Point[] { - const upscaled = upscalePath(this.aStar.reconstructPath()) - upscaled.push({ x: this.dst.cell().x, y: this.dst.cell().y }) + upscaled.push(this.dst.cell()) return upscaled } } -function upscalePath(path: SearchNode[], scaleFactor: number = 2): Point[] { +function upscalePath(path: Cell[], scaleFactor: number = 2): Cell[] { // Scale up each point - const scaledPath = path.map(point => ({ - x: point.cell().x * scaleFactor, - y: point.cell().y * scaleFactor - })); + const scaledPath = path.map(point => (new Cell( + point.x * scaleFactor, + point.y * scaleFactor + ))); - const smoothPath: Point[] = []; + const smoothPath: Cell[] = []; for (let i = 0; i < scaledPath.length - 1; i++) { const current = scaledPath[i]; @@ -72,10 +65,10 @@ function upscalePath(path: SearchNode[], scaleFactor: number = 2): Point[] { // Add intermediate points for (let step = 1; step < steps; step++) { - smoothPath.push({ - x: Math.round(current.x + (dx * step) / steps), - y: Math.round(current.y + (dy * step) / steps) - }); + smoothPath.push(new Cell( + Math.round(current.x + (dx * step) / steps), + Math.round(current.y + (dy * step) / steps) + )); } } diff --git a/src/core/pathfinding/PathFinding.ts b/src/core/pathfinding/PathFinding.ts index 968cbf9cc..4da1cb623 100644 --- a/src/core/pathfinding/PathFinding.ts +++ b/src/core/pathfinding/PathFinding.ts @@ -1,4 +1,4 @@ -import { Game, Tile } from "../game/Game"; +import { Cell, Game, Tile } from "../game/Game"; import { manhattanDist } from "../Util"; import { AStar, PathFindResultType, TileResult } from "./AStar"; import { ParallelAStar, WorkerClient } from "../worker/WorkerClient"; @@ -9,17 +9,19 @@ export class PathFinder { private curr: Tile = null private dst: Tile = null - private path: Tile[] + private path: Cell[] private aStar: AStar private computeFinished = true private constructor( + private game: Game, private newAStar: (curr: Tile, dst: Tile) => AStar ) { } public static Mini(game: Game, iterations: number, canMove: (t: Tile) => boolean, maxTries: number = 20) { return new PathFinder( + game, (curr: Tile, dst: Tile) => { return new MiniAStar( game.terrainMap(), @@ -34,8 +36,9 @@ export class PathFinder { ) } - public static Serial(iterations: number, canMove: (t: Tile) => boolean, maxTries: number = 20): PathFinder { + public static Serial(game: Game, iterations: number, canMove: (t: Tile) => boolean, maxTries: number = 20): PathFinder { return new PathFinder( + game, (curr: Tile, dst: Tile) => { return new SerialAStar( curr, @@ -48,8 +51,9 @@ export class PathFinder { ) } - public static Parallel(worker: WorkerClient, numTicks: number): PathFinder { + public static Parallel(game: Game, worker: WorkerClient, numTicks: number): PathFinder { return new PathFinder( + game, (curr: Tile, dst: Tile) => { return worker.createParallelAStar(curr, dst, numTicks) } @@ -77,14 +81,14 @@ export class PathFinder { this.computeFinished = false return this.nextTile(curr, dst) } else { - return { type: PathFindResultType.NextTile, tile: this.path.shift() } + return { type: PathFindResultType.NextTile, tile: this.game.tile(this.path.shift()) } } } switch (this.aStar.compute()) { case PathFindResultType.Completed: this.computeFinished = true - this.path = this.aStar.reconstructPath() as Tile[] + this.path = this.aStar.reconstructPath() // Remove the start tile this.path.shift() return this.nextTile(curr, dst) diff --git a/src/core/pathfinding/SerialAStar.ts b/src/core/pathfinding/SerialAStar.ts index 266e6df53..c35ad8423 100644 --- a/src/core/pathfinding/SerialAStar.ts +++ b/src/core/pathfinding/SerialAStar.ts @@ -1,6 +1,7 @@ import { PriorityQueue } from "@datastructures-js/priority-queue"; import { AStar, SearchNode } from "./AStar"; import { PathFindResultType } from "./AStar"; +import { Cell } from "../game/Game"; export class SerialAStar implements AStar { @@ -114,7 +115,7 @@ export class SerialAStar implements AStar { } } - public reconstructPath(): SearchNode[] { + public reconstructPath(): Cell[] { if (!this.meetingPoint) return []; // Reconstruct path from start to meeting point @@ -132,6 +133,6 @@ export class SerialAStar implements AStar { fwdPath.push(current); } - return fwdPath; + return fwdPath.map(sn => sn.cell()); } } diff --git a/src/core/worker/Worker.worker.ts b/src/core/worker/Worker.worker.ts index ce87b0c8e..cb8504cbe 100644 --- a/src/core/worker/Worker.worker.ts +++ b/src/core/worker/Worker.worker.ts @@ -10,16 +10,11 @@ let searches = new PriorityQueue((a: Search, b: Search) => (a.deadline - let processingInterval: number | null = null; let isProcessingSearch = false -interface Point { - x: number - y: number -} - interface Search { aStar: SerialAStar, deadline: number requestId: string, - end: Point + end: Cell } interface SearchRequest { @@ -27,8 +22,8 @@ interface SearchRequest { currentTick: number // duration in ticks duration: number - start: Point - end: Point + start: Cell + end: Cell } self.onmessage = (e) => { @@ -81,7 +76,7 @@ function computeSearches() { const search = searches.dequeue() switch (search.aStar.compute()) { case PathFindResultType.Completed: - const path = upscalePath(search.aStar.reconstructPath().map(sn => ({ x: sn.cell().x, y: sn.cell().y }))) + const path = upscalePath(search.aStar.reconstructPath()) path.push(search.end) self.postMessage({ type: 'pathFound', @@ -107,14 +102,14 @@ function computeSearches() { } } -function upscalePath(path: Point[], scaleFactor: number = 2): Point[] { +function upscalePath(path: Cell[], scaleFactor: number = 2): Cell[] { // Scale up each point - const scaledPath = path.map(point => ({ - x: point.x * scaleFactor, - y: point.y * scaleFactor - })); + const scaledPath = path.map(point => (new Cell( + point.x * scaleFactor, + point.y * scaleFactor + ))); - const smoothPath: Point[] = []; + const smoothPath: Cell[] = []; for (let i = 0; i < scaledPath.length - 1; i++) { const current = scaledPath[i]; @@ -133,10 +128,10 @@ function upscalePath(path: Point[], scaleFactor: number = 2): Point[] { // Add intermediate points for (let step = 1; step < steps; step++) { - smoothPath.push({ - x: Math.round(current.x + (dx * step) / steps), - y: Math.round(current.y + (dy * step) / steps) - }); + smoothPath.push(new Cell( + Math.round(current.x + (dx * step) / steps), + Math.round(current.y + (dy * step) / steps) + )); } } diff --git a/src/core/worker/WorkerClient.ts b/src/core/worker/WorkerClient.ts index 9f4766a39..5f28ea4b2 100644 --- a/src/core/worker/WorkerClient.ts +++ b/src/core/worker/WorkerClient.ts @@ -46,7 +46,7 @@ export class WorkerClient { } } export class ParallelAStar implements AStar { - private path: Tile[] | 'NOT_FOUND' | null = null; + private path: Cell[] | 'NOT_FOUND' | null = null; private promise: Promise; constructor( @@ -72,7 +72,7 @@ export class ParallelAStar implements AStar { this.worker.removeEventListener('message', handler); if (e.data.type === 'pathFound') { - this.path = e.data.path.map(pos => this.game.tile(new Cell(pos.x, pos.y))); + this.path = e.data.path resolve(); } else if (e.data.type === 'pathNotFound') { this.path = 'NOT_FOUND'; @@ -113,11 +113,11 @@ export class ParallelAStar implements AStar { return PathFindResultType.Pending; } - reconstructPath(): Tile[] { + reconstructPath(): Cell[] { if (this.path == "NOT_FOUND" || this.path == null) { throw Error(`cannot reconstruct path: ${this.path}`); } - return this.path as Tile[]; + return this.path } }