diff --git a/src/core/execution/PortExecution.ts b/src/core/execution/PortExecution.ts index 6f1857954..f34e3c906 100644 --- a/src/core/execution/PortExecution.ts +++ b/src/core/execution/PortExecution.ts @@ -87,7 +87,7 @@ export class PortExecution implements Execution { const pf = new MiniAStar( this.mg.terrainMap(), this.mg.terrainMiniMap(), - this.port.tile(), port.tile(), + this.port.tile().terrain(), port.tile().terrain(), sn => sn.type() == TerrainType.Ocean, 10_000, 25 diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index e8b24d50f..88382ba83 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -1,7 +1,6 @@ import { Config } from "../configuration/Config" import { GameEvent } from "../EventBus" import { ClientID, GameConfig, GameID } from "../Schemas" -import { SearchNode } from "../pathfinding/AStar" export type PlayerID = string export type Tick = number @@ -185,7 +184,11 @@ export interface TerrainMap { numLandTiles(): number } -export interface TerrainTile extends SearchNode { +export type TerrainTileKey = string + + + +export interface TerrainTile { isLand(): boolean isShore(): boolean isOceanShore(): boolean @@ -195,6 +198,11 @@ export interface TerrainTile extends SearchNode { isLake(): boolean type(): TerrainType magnitude(): number + equals(other: TerrainTile): boolean + cell(): Cell + neighbors(): TerrainTile[] + cost(): number + key(): TerrainTileKey } export interface DefenseBonus { @@ -204,7 +212,7 @@ export interface DefenseBonus { tile: Tile } -export interface Tile extends SearchNode { +export interface Tile { owner(): Player | TerraNullius hasOwner(): boolean isBorder(): boolean diff --git a/src/core/game/TerrainMapLoader.ts b/src/core/game/TerrainMapLoader.ts index b77c9ac62..e4bcd01dc 100644 --- a/src/core/game/TerrainMapLoader.ts +++ b/src/core/game/TerrainMapLoader.ts @@ -25,6 +25,14 @@ export class TerrainTileImpl implements TerrainTile { public land = false constructor(private map: TerrainMap, public _type: TerrainType, private _cell: Cell) { } + + key(): string { + return this._cell.toString() + } + + equals(other: TerrainTile): boolean { + return this._cell.x == other.cell().x && this._cell.y == other.cell().y + } type(): TerrainType { return this._type } diff --git a/src/core/game/TileImpl.ts b/src/core/game/TileImpl.ts index 6e42bf100..fe16d1949 100644 --- a/src/core/game/TileImpl.ts +++ b/src/core/game/TileImpl.ts @@ -1,5 +1,4 @@ import { Tile, Cell, TerrainType, Player, TerraNullius, MutablePlayer, TerrainTile, DefenseBonus, MutableTile, TileUpdate, GameUpdateType } from "./Game"; -import { SearchNode } from "../pathfinding/AStar"; import { TerrainMapImpl, TerrainTileImpl } from "./TerrainMapLoader"; import { GameImpl } from "./GameImpl"; import { PlayerImpl } from "./PlayerImpl"; diff --git a/src/core/pathfinding/AStar.ts b/src/core/pathfinding/AStar.ts index d9931d80f..9e4f8cb97 100644 --- a/src/core/pathfinding/AStar.ts +++ b/src/core/pathfinding/AStar.ts @@ -22,12 +22,6 @@ export enum PathFindResultType { type: PathFindResultType.PathNotFound; }; -export interface SearchNode { - cost(): number - cell(): Cell - neighbors(): SearchNode[] - type(): TerrainType -} export interface Point { x: number; y: number; diff --git a/src/core/pathfinding/MiniAStar.ts b/src/core/pathfinding/MiniAStar.ts index a3695b219..dbbcf68c7 100644 --- a/src/core/pathfinding/MiniAStar.ts +++ b/src/core/pathfinding/MiniAStar.ts @@ -1,5 +1,5 @@ import { Cell, Game, TerrainMap, TerrainTile, TerrainType } from "../game/Game"; -import { AStar, PathFindResultType, Point, SearchNode } from "./AStar"; +import { AStar, PathFindResultType, } from "./AStar"; import { SerialAStar } from "./SerialAStar"; // TODO: test this, get it work @@ -10,9 +10,9 @@ export class MiniAStar implements AStar { constructor( private terrainMap: TerrainMap, private miniMap: TerrainMap, - private src: SearchNode, - private dst: SearchNode, - private canMove: (t: SearchNode) => boolean, + private src: TerrainTile, + private dst: TerrainTile, + private canMove: (t: TerrainTile) => boolean, private iterations: number, private maxTries: number ) { diff --git a/src/core/pathfinding/PathFinding.ts b/src/core/pathfinding/PathFinding.ts index 508f78793..2d8e6e55e 100644 --- a/src/core/pathfinding/PathFinding.ts +++ b/src/core/pathfinding/PathFinding.ts @@ -1,6 +1,6 @@ import { Cell, Game, TerrainTile, TerrainType, Tile } from "../game/Game"; import { manhattanDist } from "../Util"; -import { AStar, PathFindResultType, SearchNode, TileResult } from "./AStar"; +import { AStar, PathFindResultType, TileResult } from "./AStar"; import { SerialAStar } from "./SerialAStar"; import { MiniAStar } from "./MiniAStar"; import { consolex } from "../Consolex"; @@ -19,15 +19,15 @@ export class PathFinder { ) { } - public static Mini(game: Game, iterations: number, canMove: (s: SearchNode) => boolean, maxTries: number = 20) { + public static Mini(game: Game, iterations: number, canMove: (s: TerrainTile) => boolean, maxTries: number = 20) { return new PathFinder( game, (curr: Tile, dst: Tile) => { return new MiniAStar( game.terrainMap(), game.terrainMiniMap(), - curr, - dst, + curr.terrain(), + dst.terrain(), canMove, iterations, maxTries @@ -36,13 +36,13 @@ export class PathFinder { ) } - public static Serial(game: Game, iterations: number, canMove: (t: Tile) => boolean, maxTries: number = 20): PathFinder { + 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, - dst, + curr.terrain(), + dst.terrain(), canMove, iterations, maxTries diff --git a/src/core/pathfinding/SerialAStar.ts b/src/core/pathfinding/SerialAStar.ts index 8251c71ee..214ba7900 100644 --- a/src/core/pathfinding/SerialAStar.ts +++ b/src/core/pathfinding/SerialAStar.ts @@ -1,46 +1,46 @@ import { PriorityQueue } from "@datastructures-js/priority-queue"; -import { AStar, SearchNode } from "./AStar"; +import { AStar} from "./AStar"; import { PathFindResultType } from "./AStar"; -import { Cell } from "../game/Game"; +import { Cell, TerrainTile, TerrainTileKey } from "../game/Game"; import { consolex } from "../Consolex"; export class SerialAStar implements AStar { - private fwdOpenSet: PriorityQueue<{ tile: SearchNode; fScore: number; }>; - private bwdOpenSet: PriorityQueue<{ tile: SearchNode; fScore: number; }>; - private fwdCameFrom: Map; - private bwdCameFrom: Map; - private fwdGScore: Map; - private bwdGScore: Map; - private meetingPoint: SearchNode | null; + 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; public completed: boolean; constructor( - private src: SearchNode, - private dst: SearchNode, - private canMove: (t: SearchNode) => boolean, + private src: TerrainTile, + private dst: TerrainTile, + private canMove: (t: TerrainTile) => boolean, private iterations: number, private maxTries: number ) { - this.fwdOpenSet = new PriorityQueue<{ tile: SearchNode; fScore: number; }>( + this.fwdOpenSet = new PriorityQueue<{ tile: TerrainTile; fScore: number; }>( (a, b) => a.fScore - b.fScore ); - this.bwdOpenSet = new PriorityQueue<{ tile: SearchNode; fScore: number; }>( + this.bwdOpenSet = new PriorityQueue<{ tile: TerrainTile; 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, 0); + this.fwdGScore.set(src.key(), 0); this.fwdOpenSet.enqueue({ tile: src, fScore: this.heuristic(src, dst) }); // Initialize backward search - this.bwdGScore.set(dst, 0); + this.bwdGScore.set(dst.key(), 0); this.bwdOpenSet.enqueue({ tile: dst, fScore: this.heuristic(dst, src) }); } @@ -61,43 +61,43 @@ export class SerialAStar implements AStar { // Process forward search const fwdCurrent = this.fwdOpenSet.dequeue()!.tile; - if (this.bwdGScore.has(fwdCurrent)) { + if (this.bwdGScore.has(fwdCurrent.key())) { // We found a meeting point! this.meetingPoint = fwdCurrent; this.completed = true; return PathFindResultType.Completed; } - this.expandSearchNode(fwdCurrent, true); + this.expandTerrainTile(fwdCurrent, true); // Process backward search const bwdCurrent = this.bwdOpenSet.dequeue()!.tile; - if (this.fwdGScore.has(bwdCurrent)) { + if (this.fwdGScore.has(bwdCurrent.key())) { // We found a meeting point! this.meetingPoint = bwdCurrent; this.completed = true; return PathFindResultType.Completed; } - this.expandSearchNode(bwdCurrent, false); + this.expandTerrainTile(bwdCurrent, false); } return this.completed ? PathFindResultType.Completed : PathFindResultType.PathNotFound; } - private expandSearchNode(current: SearchNode, isForward: boolean) { + private expandTerrainTile(current: TerrainTile, isForward: boolean) { for (const neighbor of current.neighbors()) { - if (neighbor !== (isForward ? this.dst : this.src) && !this.canMove(neighbor)) continue; + if (!neighbor.equals(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)! + neighbor.cost(); + let tentativeGScore = gScore.get(current.key())! + neighbor.cost(); - if (!gScore.has(neighbor) || tentativeGScore < gScore.get(neighbor)!) { - cameFrom.set(neighbor, current); - gScore.set(neighbor, tentativeGScore); + if (!gScore.has(neighbor.key()) || tentativeGScore < gScore.get(neighbor.key())!) { + cameFrom.set(neighbor.key(), current); + gScore.set(neighbor.key(), tentativeGScore); const fScore = tentativeGScore + this.heuristic( neighbor, isForward ? this.dst : this.src @@ -107,7 +107,7 @@ export class SerialAStar implements AStar { } } - private heuristic(a: SearchNode, b: SearchNode): number { + private heuristic(a: TerrainTile, b: TerrainTile): number { // TODO use wrapped try { return 1.1 * Math.abs(a.cell().x - b.cell().x) + Math.abs(a.cell().y - b.cell().y); @@ -120,17 +120,17 @@ export class SerialAStar implements AStar { if (!this.meetingPoint) return []; // Reconstruct path from start to meeting point - const fwdPath: SearchNode[] = [this.meetingPoint]; + const fwdPath: TerrainTile[] = [this.meetingPoint]; let current = this.meetingPoint; - while (this.fwdCameFrom.has(current)) { - current = this.fwdCameFrom.get(current)!; + while (this.fwdCameFrom.has(current.key())) { + current = this.fwdCameFrom.get(current.key())!; fwdPath.unshift(current); } // Reconstruct path from meeting point to goal current = this.meetingPoint; - while (this.bwdCameFrom.has(current)) { - current = this.bwdCameFrom.get(current)!; + while (this.bwdCameFrom.has(current.key())) { + current = this.bwdCameFrom.get(current.key())!; fwdPath.push(current); }