From b22532d41fd7becb3c091a9d70974e3f10350f39 Mon Sep 17 00:00:00 2001 From: evanpelle Date: Tue, 14 Jan 2025 10:52:55 -0800 Subject: [PATCH] use TileRef instead of TerrainTile for astar --- src/client/ClientGameRunner.ts | 1 - src/core/GameRunner.ts | 2 +- src/core/GameView.ts | 7 ++ src/core/execution/BattleshipExecution.ts | 2 +- src/core/execution/DestroyerExecution.ts | 2 +- src/core/execution/NukeExecution.ts | 2 +- src/core/execution/PortExecution.ts | 12 +-- src/core/execution/ShellExecution.ts | 2 +- src/core/execution/TransportShipExecution.ts | 2 +- src/core/game/Game.ts | 5 ++ src/core/game/GameImpl.ts | 33 +++++--- src/core/game/GameMap.ts | 20 +++-- src/core/game/TileImpl.ts | 17 ++-- src/core/pathfinding/MiniAStar.ts | 35 +++++---- src/core/pathfinding/PathFinding.ts | 35 ++++----- src/core/pathfinding/SerialAStar.ts | 82 ++++++++++---------- 16 files changed, 149 insertions(+), 110 deletions(-) diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts index dce0b402e..2b097427a 100644 --- a/src/client/ClientGameRunner.ts +++ b/src/client/ClientGameRunner.ts @@ -1,6 +1,5 @@ import { Executor } from "../core/execution/ExecutionManager"; import { Cell, MutableGame, PlayerID, GameMapType, Difficulty, GameType } from "../core/game/Game"; -import { createGame } from "../core/game/GameImpl"; import { EventBus } from "../core/EventBus"; import { createRenderer, GameRenderer } from "./graphics/GameRenderer"; import { InputHandler, MouseUpEvent, ZoomEvent, DragEvent, MouseDownEvent } from "./InputHandler" diff --git a/src/core/GameRunner.ts b/src/core/GameRunner.ts index b3a868fa3..0bdd73d63 100644 --- a/src/core/GameRunner.ts +++ b/src/core/GameRunner.ts @@ -14,7 +14,7 @@ import { GameUpdateViewData, packTileData } from "./GameView"; export async function createGameRunner(gameID: string, gameConfig: GameConfig, callBack: (gu: GameUpdateViewData) => void): Promise { const config = getConfig(gameConfig) const terrainMap = await loadTerrainMap(gameConfig.gameMap); - const game = createGame(terrainMap.map, terrainMap.miniMap, config) + const game = createGame(terrainMap.gameMap, terrainMap.miniGameMap, terrainMap.map, terrainMap.miniMap, config) const gr = new GameRunner(game as MutableGame, new Executor(game, gameID), callBack) gr.init() return gr diff --git a/src/core/GameView.ts b/src/core/GameView.ts index 02bd9851f..58bb286da 100644 --- a/src/core/GameView.ts +++ b/src/core/GameView.ts @@ -4,6 +4,7 @@ import { Alliance, AllianceRequest, AllPlayers, Cell, DefenseBonus, EmojiMessage import { ClientID } from "./Schemas"; import { TerraNulliusImpl } from './game/TerraNulliusImpl'; import { WorkerClient } from './worker/WorkerClient'; +import { TileRef } from './game/GameMap'; export class TileView { @@ -12,6 +13,9 @@ export class TileView { constructor(private game: GameView, public data: TileUpdate, private _terrain: TerrainTile) { } + ref(): TileRef { + throw new Error('uh oh') + } type(): TerrainType { return this._terrain.type() } @@ -32,6 +36,9 @@ export class TileView { } return false } + isBorderUpdated(): boolean { + return this.data.isBorder + } cell(): Cell { return this._terrain.cell() } diff --git a/src/core/execution/BattleshipExecution.ts b/src/core/execution/BattleshipExecution.ts index 64b9b90c4..beef3abf1 100644 --- a/src/core/execution/BattleshipExecution.ts +++ b/src/core/execution/BattleshipExecution.ts @@ -34,7 +34,7 @@ export class BattleshipExecution implements Execution { init(mg: MutableGame, ticks: number): void { - this.pathfinder = PathFinder.Mini(mg, 5000, t => t.type() == TerrainType.Ocean) + this.pathfinder = PathFinder.Mini(mg, 5000, false) 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 08bb93929..cfeddd36d 100644 --- a/src/core/execution/DestroyerExecution.ts +++ b/src/core/execution/DestroyerExecution.ts @@ -30,7 +30,7 @@ export class DestroyerExecution implements Execution { init(mg: MutableGame, ticks: number): void { - this.pathfinder = PathFinder.Mini(mg, 5000, t => t.type() == TerrainType.Ocean) + this.pathfinder = PathFinder.Mini(mg, 5000, false) 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 701c4ef1a..eb929127a 100644 --- a/src/core/execution/NukeExecution.ts +++ b/src/core/execution/NukeExecution.ts @@ -27,7 +27,7 @@ export class NukeExecution implements Execution { init(mg: MutableGame, ticks: number): void { this.mg = mg - this.pathFinder = PathFinder.Mini(mg, 10_000, () => true) + this.pathFinder = PathFinder.Mini(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 f34e3c906..5a61af92e 100644 --- a/src/core/execution/PortExecution.ts +++ b/src/core/execution/PortExecution.ts @@ -7,6 +7,7 @@ import { bfs, dist, manhattanDist } from "../Util"; import { TradeShipExecution } from "./TradeShipExecution"; import { consolex } from "../Consolex"; import { MiniAStar } from "../pathfinding/MiniAStar"; +import { TileRef } from "../game/GameMap"; export class PortExecution implements Execution { @@ -85,10 +86,11 @@ export class PortExecution implements Execution { } const pf = new MiniAStar( - this.mg.terrainMap(), - this.mg.terrainMiniMap(), - this.port.tile().terrain(), port.tile().terrain(), - sn => sn.type() == TerrainType.Ocean, + this.mg.map(), + this.mg.miniMap(), + this.port.tile().ref(), + port.tile().ref(), + (tr: TileRef) => this.mg.miniMap().isOcean(tr), 10_000, 25 ) @@ -108,7 +110,7 @@ export class PortExecution implements Execution { const port = this.random.randElement(portConnections) const path = this.portPaths.get(port) if (path != null) { - const pf = PathFinder.Mini(this.mg, 10, (sn) => sn.type() == TerrainType.Ocean) + const pf = PathFinder.Mini(this.mg, 10, false) 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 75e75c1ad..47cc88980 100644 --- a/src/core/execution/ShellExecution.ts +++ b/src/core/execution/ShellExecution.ts @@ -14,7 +14,7 @@ export class ShellExecution implements Execution { } init(mg: MutableGame, ticks: number): void { - this.pathFinder = PathFinder.Mini(mg, 2000, () => true, 10) + this.pathFinder = PathFinder.Mini(mg, 2000, true, 10) } tick(ticks: number): void { diff --git a/src/core/execution/TransportShipExecution.ts b/src/core/execution/TransportShipExecution.ts index 24970212b..c5caa165d 100644 --- a/src/core/execution/TransportShipExecution.ts +++ b/src/core/execution/TransportShipExecution.ts @@ -45,7 +45,7 @@ export class TransportShipExecution implements Execution { init(mg: MutableGame, ticks: number) { this.lastMove = ticks this.mg = mg - this.pathFinder = PathFinder.Mini(mg, 10_000, t => t.type() == TerrainType.Ocean, 2) + this.pathFinder = PathFinder.Mini(mg, 10_000, false, 2) this.attacker = mg.player(this.attackerID) diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index d3eb0df9a..21650f158 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -1,6 +1,7 @@ import { Config } from "../configuration/Config" import { GameEvent } from "../EventBus" import { ClientID, GameConfig, GameID } from "../Schemas" +import { GameMap, TileRef } from "./GameMap" export type PlayerID = string export type Tick = number @@ -221,6 +222,7 @@ export interface Tile { terrain(): TerrainTile neighbors(): Tile[] hasDefenseBonus(): boolean + ref(): TileRef } export interface MutableTile extends Tile { @@ -376,6 +378,9 @@ export interface Game { unitInfo(type: UnitType): UnitInfo terrainMap(): TerrainMap terrainMiniMap(): TerrainMap + + map(): GameMap + miniMap(): GameMap } export interface MutableGame extends Game { diff --git a/src/core/game/GameImpl.ts b/src/core/game/GameImpl.ts index ffcef1691..70256ca3e 100644 --- a/src/core/game/GameImpl.ts +++ b/src/core/game/GameImpl.ts @@ -11,9 +11,10 @@ import { MessageType } from './Game'; import { UnitImpl } from "./UnitImpl"; import { consolex } from "../Consolex"; import { string } from "zod"; +import { GameMap } from "./GameMap"; -export function createGame(terrainMap: TerrainMapImpl, miniMap: TerrainMap, config: Config): Game { - return new GameImpl(terrainMap, miniMap, config) +export function createGame(gameMap: GameMap, miniGameMap: GameMap, terrainMap: TerrainMapImpl, miniMap: TerrainMap, config: Config): Game { + return new GameImpl(terrainMap, miniMap, gameMap, miniGameMap, config) } export type CellString = string @@ -23,7 +24,7 @@ export class GameImpl implements MutableGame { private unInitExecs: Execution[] = [] - map: TileImpl[][] + _map: TileImpl[][] private nations_: Nation[] = [] @@ -45,17 +46,19 @@ export class GameImpl implements MutableGame { constructor( private _terrainMap: TerrainMapImpl, private _miniMap: TerrainMap, + private gameMap: GameMap, + private miniGameMap: GameMap, private _config: Config, ) { this._terraNullius = new TerraNulliusImpl() this._width = _terrainMap.width(); this._height = _terrainMap.height(); - this.map = new Array(this._width); + this._map = new Array(this._width); for (let x = 0; x < this._width; x++) { - this.map[x] = new Array(this._height); + this._map[x] = new Array(this._height); for (let y = 0; y < this._height; y++) { let cell = new Cell(x, y); - this.map[x][y] = new TileImpl(this, this._terraNullius, cell, _terrainMap); + this._map[x][y] = new TileImpl(this, this._terraNullius, cell, _terrainMap); } } this.nations_ = _terrainMap.nationMap.nations @@ -65,6 +68,12 @@ export class GameImpl implements MutableGame { n.strength )) } + map(): GameMap { + return this.gameMap + } + miniMap(): GameMap { + return this.miniGameMap + } addUpdate(update: GameUpdate) { (this.updates[update.type] as any[]).push(update); @@ -296,7 +305,7 @@ export class GameImpl implements MutableGame { tile(cell: Cell): MutableTile { this.assertIsOnMap(cell) - return this.map[cell.x][cell.y] as MutableTile + return this._map[cell.x][cell.y] as MutableTile } isOnMap(cell: Cell): boolean { @@ -311,16 +320,16 @@ export class GameImpl implements MutableGame { const y = tile.cell().y const ns: TileImpl[] = [] if (y > 0) { - ns.push(this.map[x][y - 1]) + ns.push(this._map[x][y - 1]) } if (y < this._height - 1) { - ns.push(this.map[x][y + 1]) + ns.push(this._map[x][y + 1]) } if (x > 0) { - ns.push(this.map[x - 1][y]) + ns.push(this._map[x - 1][y]) } if (x < this._width - 1) { - ns.push(this.map[x + 1][y]) + ns.push(this._map[x + 1][y]) } return ns } @@ -335,7 +344,7 @@ export class GameImpl implements MutableGame { const newX = x + dx const newY = y + dy if (newX >= 0 && newX < this._width && newY >= 0 && newY < this._height) { - ns.push(this.map[newX][newY]) + ns.push(this._map[newX][newY]) } } } diff --git a/src/core/game/GameMap.ts b/src/core/game/GameMap.ts index 168b2b191..323ff2e3e 100644 --- a/src/core/game/GameMap.ts +++ b/src/core/game/GameMap.ts @@ -1,4 +1,4 @@ -import { TerrainType } from "./Game"; +import { Cell, TerrainType } from "./Game"; export type TileRef = number; @@ -9,10 +9,10 @@ export class GameMap { private readonly height_: number; // Terrain bits (Uint8Array) - private static readonly IS_LAND_BIT = 0; - private static readonly SHORELINE_BIT = 1; - private static readonly OCEAN_BIT = 2; - private static readonly MAGNITUDE_OFFSET = 3; // Uses bits 3-7 (5 bits) + private static readonly IS_LAND_BIT = 7; + private static readonly SHORELINE_BIT = 6; + private static readonly OCEAN_BIT = 5; + private static readonly MAGNITUDE_OFFSET = 4; // Uses bits 3-7 (5 bits) private static readonly MAGNITUDE_MASK = 0x1F; // 11111 in binary // State bits (Uint16Array) @@ -48,6 +48,10 @@ export class GameMap { return Math.floor(ref / this.width_); } + cell(ref: TileRef): Cell { + return new Cell(this.x(ref), this.y(ref)) + } + width(): number { return this.width_; } height(): number { return this.height_; } numLandTiles(): number { return this.numLandTiles_; } @@ -70,7 +74,7 @@ export class GameMap { } magnitude(ref: TileRef): number { - return (this.terrain[ref] >> GameMap.MAGNITUDE_OFFSET) & GameMap.MAGNITUDE_MASK; + return this.terrain[ref] & GameMap.MAGNITUDE_MASK; } // State getters and setters (mutable) @@ -157,6 +161,10 @@ export class GameMap { if (ref % w !== 0) neighbors.push(ref - 1); if (ref % w !== w - 1) neighbors.push(ref + 1); + for (const n of neighbors) { + (this.ref(this.x(n), this.y(n))) + } + return neighbors; } } \ No newline at end of file diff --git a/src/core/game/TileImpl.ts b/src/core/game/TileImpl.ts index fe16d1949..f443e1927 100644 --- a/src/core/game/TileImpl.ts +++ b/src/core/game/TileImpl.ts @@ -3,6 +3,7 @@ import { TerrainMapImpl, TerrainTileImpl } from "./TerrainMapLoader"; import { GameImpl } from "./GameImpl"; import { PlayerImpl } from "./PlayerImpl"; import { TerraNulliusImpl } from "./TerraNulliusImpl"; +import { TileRef } from "./GameMap"; export class TileImpl implements MutableTile { @@ -21,6 +22,10 @@ export class TileImpl implements MutableTile { private terrainMap: TerrainMapImpl ) { } + ref(): TileRef { + return this.gs.map().ref(this._cell.x, this._cell.y) + } + toUpdate(): TileUpdate { return { type: GameUpdateType.Tile, @@ -71,26 +76,26 @@ export class TileImpl implements MutableTile { // Check top neighbor if (y > 0) { - ns.push(this.gs.map[x][y - 1]); + ns.push(this.gs._map[x][y - 1]); } // Check bottom neighbor if (y < this.gs.height() - 1) { - ns.push(this.gs.map[x][y + 1]); + ns.push(this.gs._map[x][y + 1]); } // Check left neighbor (wrap around) if (x > 0) { - ns.push(this.gs.map[x - 1][y]); + ns.push(this.gs._map[x - 1][y]); } else { - ns.push(this.gs.map[this.gs.width() - 1][y]); + ns.push(this.gs._map[this.gs.width() - 1][y]); } // Check right neighbor (wrap around) if (x < this.gs.width() - 1) { - ns.push(this.gs.map[x + 1][y]); + ns.push(this.gs._map[x + 1][y]); } else { - ns.push(this.gs.map[0][y]); + ns.push(this.gs._map[0][y]); } return ns; } diff --git a/src/core/pathfinding/MiniAStar.ts b/src/core/pathfinding/MiniAStar.ts index dbbcf68c7..851224164 100644 --- a/src/core/pathfinding/MiniAStar.ts +++ b/src/core/pathfinding/MiniAStar.ts @@ -1,5 +1,7 @@ -import { Cell, Game, TerrainMap, TerrainTile, TerrainType } from "../game/Game"; -import { AStar, PathFindResultType, } from "./AStar"; +import { GameManager } from "../../server/GameManager"; +import { Cell, Game, TerrainMap, TerrainType } from "../game/Game"; +import { GameMap, TileRef } from "../game/GameMap"; +import { AStar, PathFindResultType, } from "./AStar"; import { SerialAStar } from "./SerialAStar"; // TODO: test this, get it work @@ -8,22 +10,29 @@ export class MiniAStar implements AStar { private aStar: SerialAStar constructor( - private terrainMap: TerrainMap, - private miniMap: TerrainMap, - private src: TerrainTile, - private dst: TerrainTile, - private canMove: (t: TerrainTile) => boolean, + private gameMap: GameMap, + private miniMap: GameMap, + private src: TileRef, + private dst: TileRef, + private canMove: (t: TileRef) => boolean, private iterations: number, private maxTries: number ) { - const miniSrc = miniMap.terrain(new Cell(Math.floor(src.cell().x / 2), Math.floor(src.cell().y / 2))) - const miniDst = miniMap.terrain(new Cell(Math.floor(dst.cell().x / 2), Math.floor(dst.cell().y / 2))) + const miniSrc = this.miniMap.ref( + Math.floor(gameMap.x(src) / 2), + Math.floor(gameMap.y(src) / 2) + ) + const miniDst = this.miniMap.ref( + Math.floor(gameMap.x(dst) / 2), + Math.floor(gameMap.y(dst) / 2) + ) this.aStar = new SerialAStar( miniSrc, miniDst, canMove, iterations, - maxTries + maxTries, + this.miniMap ) } @@ -33,7 +42,7 @@ export class MiniAStar implements AStar { reconstructPath(): Cell[] { const upscaled = upscalePath(this.aStar.reconstructPath()) - upscaled.push(this.dst.cell()) + upscaled.push(new Cell(this.gameMap.x(this.dst), this.gameMap.y(this.dst))) return upscaled } @@ -42,8 +51,8 @@ export class MiniAStar implements AStar { function upscalePath(path: Cell[], scaleFactor: number = 2): Cell[] { // Scale up each point const scaledPath = path.map(point => (new Cell( - point.x * scaleFactor, - point.y * scaleFactor + point.x * scaleFactor, + point.y * scaleFactor ))); const smoothPath: Cell[] = []; diff --git a/src/core/pathfinding/PathFinding.ts b/src/core/pathfinding/PathFinding.ts index 2d8e6e55e..c5337a522 100644 --- a/src/core/pathfinding/PathFinding.ts +++ b/src/core/pathfinding/PathFinding.ts @@ -4,6 +4,7 @@ import { AStar, PathFindResultType, TileResult } from "./AStar"; import { SerialAStar } from "./SerialAStar"; import { MiniAStar } from "./MiniAStar"; import { consolex } from "../Consolex"; +import { TileRef } from "../game/GameMap"; export class PathFinder { @@ -19,31 +20,23 @@ export class PathFinder { ) { } - public static Mini(game: Game, iterations: number, canMove: (s: TerrainTile) => boolean, maxTries: number = 20) { + public static Mini(game: Game, iterations: number, canMoveOnLand: boolean, maxTries: number = 20) { return new PathFinder( game, (curr: Tile, dst: Tile) => { + const currRef = game.map().ref(curr.cell().x, curr.cell().y) + const dstRef = game.map().ref(dst.cell().x, dst.cell().y) return new MiniAStar( - game.terrainMap(), - game.terrainMiniMap(), - curr.terrain(), - dst.terrain(), - canMove, - iterations, - maxTries - ) - } - ) - } - - public static Serial(game: Game, iterations: number, canMove: (t: TerrainTile) => boolean, maxTries: number = 20): PathFinder { - return new PathFinder( - game, - (curr: Tile, dst: Tile) => { - return new SerialAStar( - curr.terrain(), - dst.terrain(), - canMove, + game.map(), + game.miniMap(), + currRef, + dstRef, + (tr: TileRef): boolean => { + if (canMoveOnLand) { + return true + } + return game.miniMap().isOcean(tr) + }, iterations, maxTries ) diff --git a/src/core/pathfinding/SerialAStar.ts b/src/core/pathfinding/SerialAStar.ts index 214ba7900..a14e2546b 100644 --- a/src/core/pathfinding/SerialAStar.ts +++ b/src/core/pathfinding/SerialAStar.ts @@ -1,46 +1,48 @@ import { PriorityQueue } from "@datastructures-js/priority-queue"; -import { AStar} from "./AStar"; +import { AStar } from "./AStar"; import { PathFindResultType } from "./AStar"; -import { Cell, TerrainTile, TerrainTileKey } from "../game/Game"; +import { Cell } from "../game/Game"; import { consolex } from "../Consolex"; +import { GameMap, TileRef } from "../game/GameMap"; export class SerialAStar implements AStar { - private fwdOpenSet: PriorityQueue<{ tile: TerrainTile; fScore: number; }>; - private bwdOpenSet: PriorityQueue<{ tile: TerrainTile; fScore: number; }>; - private fwdCameFrom: Map; - private bwdCameFrom: Map; - private fwdGScore: Map; - private bwdGScore: Map; - private meetingPoint: TerrainTile | null; + private fwdOpenSet: PriorityQueue<{ tile: TileRef; fScore: number; }>; + private bwdOpenSet: PriorityQueue<{ tile: TileRef; fScore: number; }>; + private fwdCameFrom: Map; + private bwdCameFrom: Map; + private fwdGScore: Map; + private bwdGScore: Map; + private meetingPoint: TileRef | null; public completed: boolean; constructor( - private src: TerrainTile, - private dst: TerrainTile, - private canMove: (t: TerrainTile) => boolean, + private src: TileRef, + private dst: TileRef, + private canMove: (t: TileRef) => boolean, private iterations: number, - private maxTries: number + private maxTries: number, + private gameMap: GameMap ) { - this.fwdOpenSet = new PriorityQueue<{ tile: TerrainTile; fScore: number; }>( + this.fwdOpenSet = new PriorityQueue<{ tile: TileRef; fScore: number; }>( (a, b) => a.fScore - b.fScore ); - this.bwdOpenSet = new PriorityQueue<{ tile: TerrainTile; fScore: number; }>( + this.bwdOpenSet = new PriorityQueue<{ tile: TileRef; fScore: number; }>( (a, b) => a.fScore - b.fScore ); - this.fwdCameFrom = new Map(); - this.bwdCameFrom = new Map(); - this.fwdGScore = new Map(); - this.bwdGScore = new Map(); + this.fwdCameFrom = new Map(); + this.bwdCameFrom = new Map(); + this.fwdGScore = new Map(); + this.bwdGScore = new Map(); this.meetingPoint = null; this.completed = false; // Initialize forward search - this.fwdGScore.set(src.key(), 0); + this.fwdGScore.set(src, 0); this.fwdOpenSet.enqueue({ tile: src, fScore: this.heuristic(src, dst) }); // Initialize backward search - this.bwdGScore.set(dst.key(), 0); + this.bwdGScore.set(dst, 0); this.bwdOpenSet.enqueue({ tile: dst, fScore: this.heuristic(dst, src) }); } @@ -61,43 +63,43 @@ export class SerialAStar implements AStar { // Process forward search const fwdCurrent = this.fwdOpenSet.dequeue()!.tile; - if (this.bwdGScore.has(fwdCurrent.key())) { + if (this.bwdGScore.has(fwdCurrent)) { // We found a meeting point! this.meetingPoint = fwdCurrent; this.completed = true; return PathFindResultType.Completed; } - this.expandTerrainTile(fwdCurrent, true); + this.expandTileRef(fwdCurrent, true); // Process backward search const bwdCurrent = this.bwdOpenSet.dequeue()!.tile; - if (this.fwdGScore.has(bwdCurrent.key())) { + if (this.fwdGScore.has(bwdCurrent)) { // We found a meeting point! this.meetingPoint = bwdCurrent; this.completed = true; return PathFindResultType.Completed; } - this.expandTerrainTile(bwdCurrent, false); + this.expandTileRef(bwdCurrent, false); } return this.completed ? PathFindResultType.Completed : PathFindResultType.PathNotFound; } - private expandTerrainTile(current: TerrainTile, isForward: boolean) { - for (const neighbor of current.neighbors()) { - if (!neighbor.equals(isForward ? this.dst : this.src) && !this.canMove(neighbor)) continue; + private expandTileRef(current: TileRef, isForward: boolean) { + for (const neighbor of this.gameMap.neighbors(current)) { + if (neighbor != (isForward ? this.dst : this.src) && !this.canMove(neighbor)) continue; const gScore = isForward ? this.fwdGScore : this.bwdGScore; const openSet = isForward ? this.fwdOpenSet : this.bwdOpenSet; const cameFrom = isForward ? this.fwdCameFrom : this.bwdCameFrom; - let tentativeGScore = gScore.get(current.key())! + neighbor.cost(); + let tentativeGScore = gScore.get(current)! + this.gameMap.cost(neighbor); - if (!gScore.has(neighbor.key()) || tentativeGScore < gScore.get(neighbor.key())!) { - cameFrom.set(neighbor.key(), current); - gScore.set(neighbor.key(), tentativeGScore); + if (!gScore.has(neighbor) || tentativeGScore < gScore.get(neighbor)!) { + cameFrom.set(neighbor, current); + gScore.set(neighbor, tentativeGScore); const fScore = tentativeGScore + this.heuristic( neighbor, isForward ? this.dst : this.src @@ -107,10 +109,10 @@ export class SerialAStar implements AStar { } } - private heuristic(a: TerrainTile, b: TerrainTile): number { + private heuristic(a: TileRef, b: TileRef): number { // TODO use wrapped try { - return 1.1 * Math.abs(a.cell().x - b.cell().x) + Math.abs(a.cell().y - b.cell().y); + return 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') } @@ -120,20 +122,20 @@ export class SerialAStar implements AStar { if (!this.meetingPoint) return []; // Reconstruct path from start to meeting point - const fwdPath: TerrainTile[] = [this.meetingPoint]; + const fwdPath: TileRef[] = [this.meetingPoint]; let current = this.meetingPoint; - while (this.fwdCameFrom.has(current.key())) { - current = this.fwdCameFrom.get(current.key())!; + while (this.fwdCameFrom.has(current)) { + current = this.fwdCameFrom.get(current)!; fwdPath.unshift(current); } // Reconstruct path from meeting point to goal current = this.meetingPoint; - while (this.bwdCameFrom.has(current.key())) { - current = this.bwdCameFrom.get(current.key())!; + while (this.bwdCameFrom.has(current)) { + current = this.bwdCameFrom.get(current)!; fwdPath.push(current); } - return fwdPath.map(sn => sn.cell()); + return fwdPath.map(sn => new Cell(this.gameMap.x(sn), this.gameMap.y(sn))); } }