diff --git a/OpenRa.Game/OpenRa.Game.csproj b/OpenRa.Game/OpenRa.Game.csproj index a620d9e68f..a1610c7cc9 100644 --- a/OpenRa.Game/OpenRa.Game.csproj +++ b/OpenRa.Game/OpenRa.Game.csproj @@ -40,6 +40,7 @@ x86 false prompt + true bin\x86\Release\ diff --git a/OpenRa.Game/PathFinder.cs b/OpenRa.Game/PathFinder.cs index 9cda2e0e11..b1e812746d 100644 --- a/OpenRa.Game/PathFinder.cs +++ b/OpenRa.Game/PathFinder.cs @@ -97,7 +97,14 @@ namespace OpenRa.Game prev = sl; } if (queue.Empty) return new List(); - var ret = FindPath(cellInfo, queue, estimator, umt, true); + + var h2 = DefaultEstimator(path[0]); + var otherQueue = new PriorityQueue(); + otherQueue.Add(new PathDistance(h2(from), from)); + + var ret = FindBidiPath(cellInfo, InitCellInfo(), queue, otherQueue, estimator, h2, umt, true); + + //var ret = FindPath(cellInfo, queue, estimator, umt, true); ret.Reverse(); return ret; } @@ -124,53 +131,26 @@ namespace OpenRa.Game List FindPath( CellInfo[ , ] cellInfo, PriorityQueue queue, Func estimator, UnitMovementType umt, bool checkForBlock ) { - int samples = 0; + int nodesExpanded = 0; using (new PerfSample("find_path_inner")) { while (!queue.Empty) { PathDistance p = queue.Pop(); - int2 here = p.Location; - cellInfo[here.X, here.Y].Seen = true; + cellInfo[p.Location.X, p.Location.Y].Seen = true; - if (estimator(here) == 0.0) + if (estimator(p.Location) == 0) { - PerfHistory.Increment("nodes_expanded", samples * .01); - return MakePath(cellInfo, here); + PerfHistory.Increment("nodes_expanded", nodesExpanded * .01); + return MakePath(cellInfo, p.Location); } - samples++; + nodesExpanded++; - foreach (int2 d in Util.directions) - { - int2 newHere = here + d; - - if (cellInfo[newHere.X, newHere.Y].Seen) - continue; - if (passableCost[(int)umt][newHere.X, newHere.Y] == float.PositiveInfinity) - continue; - if (!Game.BuildingInfluence.CanMoveHere(newHere)) - continue; - if (checkForBlock && Game.UnitInfluence.GetUnitAt(newHere) != null) - continue; - var est = estimator(newHere); - if (est == float.PositiveInfinity) - continue; - - float cellCost = ((d.X * d.Y != 0) ? 1.414213563f : 1.0f) * passableCost[(int)umt][newHere.X, newHere.Y]; - float newCost = cellInfo[here.X, here.Y].MinCost + cellCost; - - if (newCost >= cellInfo[newHere.X, newHere.Y].MinCost) - continue; - - cellInfo[newHere.X, newHere.Y].Path = here; - cellInfo[newHere.X, newHere.Y].MinCost = newCost; - - queue.Add(new PathDistance(newCost + est, newHere)); - } + ExpandNode(cellInfo, queue, p, umt, checkForBlock, estimator); } - PerfHistory.Increment("nodes_expanded", samples * .01); + PerfHistory.Increment("nodes_expanded", nodesExpanded * .01); // no path exists return new List(); } @@ -211,6 +191,98 @@ namespace OpenRa.Game return 1.5f * diag + straight; }; } + + void ExpandNode(CellInfo[,] ci, PriorityQueue q, PathDistance p, UnitMovementType umt, bool checkForBlock, Func h) + { + foreach (int2 d in Util.directions) + { + int2 newHere = p.Location + d; + + if (ci[newHere.X, newHere.Y].Seen) + continue; + if (passableCost[(int)umt][newHere.X, newHere.Y] == float.PositiveInfinity) + continue; + if (!Game.BuildingInfluence.CanMoveHere(newHere)) + continue; + if (checkForBlock && Game.UnitInfluence.GetUnitAt(newHere) != null) + continue; + var est = h(newHere); + if (est == float.PositiveInfinity) + continue; + + float cellCost = ((d.X * d.Y != 0) ? 1.414213563f : 1.0f) * passableCost[(int)umt][newHere.X, newHere.Y]; + float newCost = ci[p.Location.X, p.Location.Y].MinCost + cellCost; + + if (newCost >= ci[newHere.X, newHere.Y].MinCost) + continue; + + ci[newHere.X, newHere.Y].Path = p.Location; + ci[newHere.X, newHere.Y].MinCost = newCost; + + q.Add(new PathDistance(newCost + est, newHere)); + } + } + + List FindBidiPath( /* searches from both ends toward each other */ + CellInfo[,] ca, + CellInfo[,] cb, + PriorityQueue qa, + PriorityQueue qb, + Func ha, + Func hb, + UnitMovementType umt, + bool checkForBlocked) + { + while (!qa.Empty && !qb.Empty) + { + { /* make some progress on the first search */ + var p = qa.Pop(); + ca[p.Location.X, p.Location.Y].Seen = true; + + if (cb[p.Location.X, p.Location.Y].MinCost < float.PositiveInfinity) + return MakeBidiPath(ca, cb, p.Location); + else + ExpandNode(ca, qa, p, umt, checkForBlocked, ha); + } + + { /* make some progress on the second search */ + var p = qb.Pop(); + cb[p.Location.X, p.Location.Y].Seen = true; + + //if (ca[p.Location.X, p.Location.Y].MinCost < float.PositiveInfinity) + // return MakeBidiPath(ca, cb, p.Location); + //else + ExpandNode(cb, qb, p, umt, checkForBlocked, hb); + } + } + + return new List(); + } + + static List MakeBidiPath(CellInfo[,] ca, CellInfo[,] cb, int2 p) + { + var a = new List(); + var b = new List(); + + var q = p; + while (ca[q.X, q.Y].Path != q) + { + q = ca[q.X, q.Y].Path; + a.Add(q); + } + + q = p; + while (cb[q.X, q.Y].Path != q) + { + q = cb[q.X, q.Y].Path; + b.Add(q); + } + + a.Add(p); + for (var v = b.Count - 1; v >= 0; v--) a.Add(b[v]); + + return a; + } } struct CellInfo