Use CellLayer for pathfinding.

This commit is contained in:
Paul Chote
2014-05-17 14:12:43 +12:00
parent 435f47158f
commit 441971f6d7
2 changed files with 42 additions and 39 deletions

View File

@@ -12,6 +12,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using OpenRA;
using OpenRA.Primitives; using OpenRA.Primitives;
using OpenRA.Support; using OpenRA.Support;
using OpenRA.Traits; using OpenRA.Traits;
@@ -136,9 +137,7 @@ namespace OpenRA.Mods.RA.Move
var dbg = world.WorldActor.TraitOrDefault<PathfinderDebugOverlay>(); var dbg = world.WorldActor.TraitOrDefault<PathfinderDebugOverlay>();
if (dbg != null) if (dbg != null)
{ dbg.AddLayer(search.Considered.Select(p => new Pair<CPos, int>(p, search.CellInfo[p].MinCost)), search.MaxCost, search.Owner);
dbg.AddLayer(search.Considered.Select(p => new Pair<CPos, int>(p, search.CellInfo[p.X, p.Y].MinCost)), search.MaxCost, search.Owner);
}
if (path != null) if (path != null)
return path; return path;
@@ -149,15 +148,15 @@ namespace OpenRA.Mods.RA.Move
} }
} }
static List<CPos> MakePath(CellInfo[,] cellInfo, CPos destination) static List<CPos> MakePath(CellLayer<CellInfo> cellInfo, CPos destination)
{ {
var ret = new List<CPos>(); var ret = new List<CPos>();
var pathNode = destination; var pathNode = destination;
while (cellInfo[pathNode.X, pathNode.Y].Path != pathNode) while (cellInfo[pathNode].Path != pathNode)
{ {
ret.Add(pathNode); ret.Add(pathNode);
pathNode = cellInfo[pathNode.X, pathNode.Y].Path; pathNode = cellInfo[pathNode].Path;
} }
ret.Add(pathNode); ret.Add(pathNode);
@@ -165,9 +164,8 @@ namespace OpenRA.Mods.RA.Move
return ret; return ret;
} }
public List<CPos> FindBidiPath( /* searches from both ends toward each other */ // Searches from both ends toward each other
PathSearch fromSrc, public List<CPos> FindBidiPath(PathSearch fromSrc, PathSearch fromDest)
PathSearch fromDest)
{ {
using (new PerfSample("Pathfinder")) using (new PerfSample("Pathfinder"))
{ {
@@ -181,8 +179,8 @@ namespace OpenRA.Mods.RA.Move
/* make some progress on the first search */ /* make some progress on the first search */
var p = fromSrc.Expand(world); var p = fromSrc.Expand(world);
if (fromDest.CellInfo[p.X, p.Y].Seen && if (fromDest.CellInfo[p].Seen &&
fromDest.CellInfo[p.X, p.Y].MinCost < float.PositiveInfinity) fromDest.CellInfo[p].MinCost < float.PositiveInfinity)
{ {
path = MakeBidiPath(fromSrc, fromDest, p); path = MakeBidiPath(fromSrc, fromDest, p);
break; break;
@@ -191,8 +189,8 @@ namespace OpenRA.Mods.RA.Move
/* make some progress on the second search */ /* make some progress on the second search */
var q = fromDest.Expand(world); var q = fromDest.Expand(world);
if (fromSrc.CellInfo[q.X, q.Y].Seen && if (fromSrc.CellInfo[q].Seen &&
fromSrc.CellInfo[q.X, q.Y].MinCost < float.PositiveInfinity) fromSrc.CellInfo[q].MinCost < float.PositiveInfinity)
{ {
path = MakeBidiPath(fromSrc, fromDest, q); path = MakeBidiPath(fromSrc, fromDest, q);
break; break;
@@ -202,8 +200,8 @@ namespace OpenRA.Mods.RA.Move
var dbg = world.WorldActor.TraitOrDefault<PathfinderDebugOverlay>(); var dbg = world.WorldActor.TraitOrDefault<PathfinderDebugOverlay>();
if (dbg != null) if (dbg != null)
{ {
dbg.AddLayer(fromSrc.Considered.Select(p => new Pair<CPos, int>(p, fromSrc.CellInfo[p.X, p.Y].MinCost)), fromSrc.MaxCost, fromSrc.Owner); dbg.AddLayer(fromSrc.Considered.Select(p => new Pair<CPos, int>(p, fromSrc.CellInfo[p].MinCost)), fromSrc.MaxCost, fromSrc.Owner);
dbg.AddLayer(fromDest.Considered.Select(p => new Pair<CPos, int>(p, fromDest.CellInfo[p.X, p.Y].MinCost)), fromDest.MaxCost, fromDest.Owner); dbg.AddLayer(fromDest.Considered.Select(p => new Pair<CPos, int>(p, fromDest.CellInfo[p].MinCost)), fromDest.MaxCost, fromDest.Owner);
} }
if (path != null) if (path != null)
@@ -222,19 +220,19 @@ namespace OpenRA.Mods.RA.Move
var ret = new List<CPos>(); var ret = new List<CPos>();
var q = p; var q = p;
while (ca[q.X, q.Y].Path != q) while (ca[q].Path != q)
{ {
ret.Add(q); ret.Add(q);
q = ca[q.X, q.Y].Path; q = ca[q].Path;
} }
ret.Add(q); ret.Add(q);
ret.Reverse(); ret.Reverse();
q = p; q = p;
while (cb[q.X, q.Y].Path != q) while (cb[q].Path != q)
{ {
q = cb[q.X, q.Y].Path; q = cb[q].Path;
ret.Add(q); ret.Add(q);
} }

View File

@@ -10,14 +10,16 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing;
using System.Linq; using System.Linq;
using OpenRA;
using OpenRA.Primitives; using OpenRA.Primitives;
namespace OpenRA.Mods.RA.Move namespace OpenRA.Mods.RA.Move
{ {
public sealed class PathSearch : IDisposable public sealed class PathSearch : IDisposable
{ {
public CellInfo[,] CellInfo; public CellLayer<CellInfo> CellInfo;
public PriorityQueue<PathDistance> Queue; public PriorityQueue<PathDistance> Queue;
public Func<CPos, int> Heuristic; public Func<CPos, int> Heuristic;
public bool CheckForBlocked; public bool CheckForBlocked;
@@ -141,7 +143,7 @@ namespace OpenRA.Mods.RA.Move
public CPos Expand(World world) public CPos Expand(World world)
{ {
var p = Queue.Pop(); var p = Queue.Pop();
while (CellInfo[p.Location.X, p.Location.Y].Seen) while (CellInfo[p.Location].Seen)
{ {
if (Queue.Empty) if (Queue.Empty)
return p.Location; return p.Location;
@@ -149,7 +151,9 @@ namespace OpenRA.Mods.RA.Move
p = Queue.Pop(); p = Queue.Pop();
} }
CellInfo[p.Location.X, p.Location.Y].Seen = true; var pCell = CellInfo[p.Location];
pCell.Seen = true;
CellInfo[p.Location] = pCell;
var thisCost = mobileInfo.MovementCostForCell(world, p.Location); var thisCost = mobileInfo.MovementCostForCell(world, p.Location);
@@ -177,7 +181,7 @@ namespace OpenRA.Mods.RA.Move
if (!world.Map.IsInMap(newHere)) if (!world.Map.IsInMap(newHere))
continue; continue;
if (CellInfo[newHere.X, newHere.Y].Seen) if (CellInfo[newHere].Seen)
continue; continue;
// Now we may seriously consider this direction using heuristics: // Now we may seriously consider this direction using heuristics:
@@ -224,14 +228,16 @@ namespace OpenRA.Mods.RA.Move
cellCost += laneBias; cellCost += laneBias;
} }
var newCost = CellInfo[p.Location.X, p.Location.Y].MinCost + cellCost; var newCost = CellInfo[p.Location].MinCost + cellCost;
// Cost is even higher; next direction: // Cost is even higher; next direction:
if (newCost > CellInfo[newHere.X, newHere.Y].MinCost) if (newCost > CellInfo[newHere].MinCost)
continue; continue;
CellInfo[newHere.X, newHere.Y].Path = p.Location; var hereCell = CellInfo[newHere];
CellInfo[newHere.X, newHere.Y].MinCost = newCost; hereCell.Path = p.Location;
hereCell.MinCost = newCost;
CellInfo[newHere] = hereCell;
nextDirections[i].Second = newCost + est; nextDirections[i].Second = newCost + est;
Queue.Add(new PathDistance(newCost + est, newHere)); Queue.Add(new PathDistance(newCost + est, newHere));
@@ -252,27 +258,28 @@ namespace OpenRA.Mods.RA.Move
if (!self.World.Map.IsInMap(location)) if (!self.World.Map.IsInMap(location))
return; return;
CellInfo[location.X, location.Y] = new CellInfo(0, location, false); CellInfo[location] = new CellInfo(0, location, false);
Queue.Add(new PathDistance(Heuristic(location), location)); Queue.Add(new PathDistance(Heuristic(location), location));
} }
static readonly Queue<CellInfo[,]> CellInfoPool = new Queue<CellInfo[,]>(); static readonly Queue<CellLayer<CellInfo>> CellInfoPool = new Queue<CellLayer<CellInfo>>();
static CellInfo[,] GetFromPool() static CellLayer<CellInfo> GetFromPool()
{ {
lock (CellInfoPool) lock (CellInfoPool)
return CellInfoPool.Dequeue(); return CellInfoPool.Dequeue();
} }
static void PutBackIntoPool(CellInfo[,] ci) static void PutBackIntoPool(CellLayer<CellInfo> ci)
{ {
lock (CellInfoPool) lock (CellInfoPool)
CellInfoPool.Enqueue(ci); CellInfoPool.Enqueue(ci);
} }
CellInfo[,] InitCellInfo() CellLayer<CellInfo> InitCellInfo()
{ {
CellInfo[,] result = null; CellLayer<CellInfo> result = null;
var mapSize = new Size(self.World.Map.MapSize.X, self.World.Map.MapSize.Y);
// HACK: Uses a static cache so that double-ended searches (which have two PathSearch instances) // HACK: Uses a static cache so that double-ended searches (which have two PathSearch instances)
// can implicitly share data. The PathFinder should allocate the CellInfo array and pass it // can implicitly share data. The PathFinder should allocate the CellInfo array and pass it
@@ -280,8 +287,7 @@ namespace OpenRA.Mods.RA.Move
while (CellInfoPool.Count > 0) while (CellInfoPool.Count > 0)
{ {
var cellInfo = GetFromPool(); var cellInfo = GetFromPool();
if (cellInfo.GetUpperBound(0) != self.World.Map.MapSize.X - 1 || if (cellInfo.Size != mapSize)
cellInfo.GetUpperBound(1) != self.World.Map.MapSize.Y - 1)
{ {
Log.Write("debug", "Discarding old pooled CellInfo of wrong size."); Log.Write("debug", "Discarding old pooled CellInfo of wrong size.");
continue; continue;
@@ -292,11 +298,10 @@ namespace OpenRA.Mods.RA.Move
} }
if (result == null) if (result == null)
result = new CellInfo[self.World.Map.MapSize.X, self.World.Map.MapSize.Y]; result = new CellLayer<CellInfo>(self.World.Map);
for (var x = 0; x < self.World.Map.MapSize.X; x++) foreach (var cell in self.World.Map.Cells)
for (var y = 0; y < self.World.Map.MapSize.Y; y++) result[cell] = new CellInfo(int.MaxValue, cell, false);
result[x, y] = new CellInfo(int.MaxValue, new CPos(x, y), false);
return result; return result;
} }