diff --git a/OpenRa.Game/Game.cs b/OpenRa.Game/Game.cs
index 07bbfc8a8f..cc457ae3ef 100644
--- a/OpenRa.Game/Game.cs
+++ b/OpenRa.Game/Game.cs
@@ -146,10 +146,6 @@ namespace OpenRa.Game
public static double RenderTime = 0.0;
public static double TickTime = 0.0;
public static double OreTime = 0.0;
- public static double PathToPathTime = 0.0;
- public static double NormalPathTime = 0.0;
- public static int PathToPathCount = 0;
- public static int NormalPathCount = 0;
public static Stopwatch sw;
@@ -162,10 +158,6 @@ namespace OpenRa.Game
using (new PerfSample("tick_time"))
{
sw.Reset();
- PathToPathTime = 0;
- NormalPathTime = 0;
- PathToPathCount = 0;
- NormalPathCount = 0;
lastTime += timestep;
if (orderManager.Tick())
diff --git a/OpenRa.Game/OpenRa.Game.csproj b/OpenRa.Game/OpenRa.Game.csproj
index a1610c7cc9..36dddba6b3 100644
--- a/OpenRa.Game/OpenRa.Game.csproj
+++ b/OpenRa.Game/OpenRa.Game.csproj
@@ -34,13 +34,13 @@
true
bin\x86\Debug\
- DEBUG;TRACE
+ TRACE;DEBUG;SANITY_CHECKS
true
full
x86
false
prompt
- true
+ false
bin\x86\Release\
@@ -79,6 +79,7 @@
+
diff --git a/OpenRa.Game/PathFinder.cs b/OpenRa.Game/PathFinder.cs
index b50c72497c..bc2f017ab8 100644
--- a/OpenRa.Game/PathFinder.cs
+++ b/OpenRa.Game/PathFinder.cs
@@ -27,22 +27,6 @@ namespace OpenRa.Game
: float.PositiveInfinity;
}
- public List FindUnitPath(int2 src, int2 dest, UnitMovementType umt)
- {
- using (new PerfSample("find_unit_path"))
- return FindUnitPath(src, DefaultEstimator(dest), umt);
- }
-
- public List FindUnitPathToRange(int2 src, int2 dest, UnitMovementType umt, int range)
- {
- var tilesInRange = Game.FindTilesInCircle(dest, range)
- .Where(t => Game.IsCellBuildable(t, umt));
-
- var path = FindUnitPath(tilesInRange, DefaultEstimator(src), umt);
- path.Reverse();
- return path;
- }
-
bool IsBlocked(int2 from, UnitMovementType umt)
{
for (int v = -1; v < 2; v++)
@@ -57,103 +41,63 @@ namespace OpenRa.Game
return true;
}
+ public List FindUnitPath( int2 from, int2 target, UnitMovementType umt )
+ {
+ using( new PerfSample( "find_unit_path" ) )
+ return FindPath( PathSearch.FromPoint( from, target, umt, false ) );
+ }
+
+ public List FindUnitPathToRange( int2 src, int2 target, UnitMovementType umt, int range )
+ {
+ using( new PerfSample( "find_unit_path_multiple_src" ) )
+ {
+ var tilesInRange = Game.FindTilesInCircle( src, range )
+ .Where( t => Game.IsCellBuildable( t, umt ) );
+
+ var path = FindPath( PathSearch.FromPoints( tilesInRange, target, umt, false ));
+ path.Reverse();
+ return path;
+ }
+ }
+
public List FindPathToPath( int2 from, List path, UnitMovementType umt )
{
- using (new PerfSample("find_path_to_path"))
- {
- if (IsBlocked(from, umt))
- return new List();
+ if( IsBlocked( from, umt ) )
+ return new List();
- CellInfo[,] cellInfo = null;
- var queue = new PriorityQueue();
- var estimator = DefaultEstimator(from);
-
- var cost = 0.0f;
- var prev = path[0];
- for (int i = 0; i < path.Count; i++)
- {
- var sl = path[i];
- if ( /*i == 0 || */(Game.BuildingInfluence.CanMoveHere(path[i]) && Game.UnitInfluence.GetUnitAt(path[i]) == null))
- {
- queue.Add(new PathDistance(estimator(sl), sl));
- if (cellInfo == null)
- cellInfo = InitCellInfo();
-
- cellInfo[sl.X, sl.Y] = new CellInfo(cost, prev, false);
- }
- var d = sl - prev;
- cost += ((d.X * d.Y != 0) ? 1.414213563f : 1.0f) * passableCost[(int)umt][sl.X, sl.Y];
- prev = sl;
- }
- if (queue.Empty) return new List();
-
- var h2 = DefaultEstimator(path[0]);
- var otherQueue = new PriorityQueue();
- otherQueue.Add(new PathDistance(h2(from), from));
-
- var otherCellInfo = InitCellInfo();
- otherCellInfo[from.X, from.Y] = new CellInfo( 0, from, false );
- var ret = FindBidiPath(cellInfo, otherCellInfo, queue, otherQueue, estimator, h2, umt, true);
- return ret;
- }
+ using( new PerfSample( "find_path_to_path" ) )
+ return FindBidiPath(
+ PathSearch.FromPath( path, from, umt, true ),
+ PathSearch.FromPoint( from, path[ 0 ], umt, true ) );
}
- public List FindUnitPath( int2 unitLocation, Func estimator, UnitMovementType umt )
- {
- return FindUnitPath( new[] { unitLocation }, estimator, umt );
- }
- public List FindUnitPath( IEnumerable startLocations, Func estimator, UnitMovementType umt )
- {
- var cellInfo = InitCellInfo();
- var queue = new PriorityQueue();
- foreach (var sl in startLocations)
- {
- queue.Add(new PathDistance(estimator(sl), sl));
- cellInfo[sl.X, sl.Y].MinCost = 0;
- }
-
- return FindPath( cellInfo, queue, estimator, umt, false );
- }
-
- List FindPath( CellInfo[ , ] cellInfo, PriorityQueue queue, Func estimator, UnitMovementType umt, bool checkForBlock )
+ public List FindPath( PathSearch search )
{
int nodesExpanded = 0;
using (new PerfSample("find_path_inner"))
{
- while (!queue.Empty)
+ while (!search.queue.Empty)
{
- PathDistance p = queue.Pop();
- cellInfo[p.Location.X, p.Location.Y].Seen = true;
+ var p = search.Expand( passableCost );
- if (estimator(p.Location) == 0)
+ if (search.heuristic(p) == 0)
{
PerfHistory.Increment("nodes_expanded", nodesExpanded * .01);
- return MakePath(cellInfo, p.Location);
+ return MakePath(search.cellInfo, p);
}
nodesExpanded++;
-
- ExpandNode(cellInfo, queue, p, umt, checkForBlock, estimator);
+ PerfHistory.Increment( "nodes_expanded", nodesExpanded * .01 );
}
- PerfHistory.Increment("nodes_expanded", nodesExpanded * .01);
// no path exists
return new List();
}
}
- static CellInfo[ , ] InitCellInfo()
- {
- var cellInfo = new CellInfo[ 128, 128 ];
- for( int x = 0 ; x < 128 ; x++ )
- for( int y = 0 ; y < 128 ; y++ )
- cellInfo[ x, y ] = new CellInfo( float.PositiveInfinity, new int2( x, y ), false );
- return cellInfo;
- }
-
- List MakePath( CellInfo[ , ] cellInfo, int2 destination )
+ static List MakePath( CellInfo[ , ] cellInfo, int2 destination )
{
List ret = new List();
int2 pathNode = destination;
@@ -165,112 +109,60 @@ namespace OpenRa.Game
}
ret.Add(pathNode);
-
+ CheckSanePath(ret);
return ret;
}
- static Func DefaultEstimator(int2 destination)
- {
- return here =>
- {
- int2 d = ( here - destination ).Abs();
- int diag = Math.Min( d.X, d.Y );
- int straight = Math.Abs( d.X - d.Y );
- 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)
+ PathSearch fromSrc,
+ PathSearch fromDest)
{
- while (!qa.Empty && !qb.Empty)
+ while (!fromSrc.queue.Empty && !fromDest.queue.Empty)
{
- { /* make some progress on the first search */
- var p = qa.Pop();
- ca[p.Location.X, p.Location.Y].Seen = true;
+ /* make some progress on the first search */
+ var p = fromSrc.Expand( passableCost );
+
+ if (fromDest.cellInfo[p.X, p.Y].MinCost < float.PositiveInfinity)
+ return MakeBidiPath(fromSrc, fromDest, p);
- 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);
- }
+ /* make some progress on the second search */
+ fromDest.Expand( passableCost );
}
return new List();
}
- static List MakeBidiPath(CellInfo[,] ca, CellInfo[,] cb, int2 p)
+ static List MakeBidiPath(PathSearch a, PathSearch b, int2 p)
{
- var a = new List();
+ var ca = a.cellInfo;
+ var cb = b.cellInfo;
+
+ var ret = new List();
var q = p;
while (ca[q.X, q.Y].Path != q)
{
- a.Add( q );
+ ret.Add( q );
q = ca[ q.X, q.Y ].Path;
}
- a.Reverse();
+ ret.Reverse();
q = p;
while (cb[q.X, q.Y].Path != q)
{
q = cb[q.X, q.Y].Path;
- a.Add(q);
+ ret.Add(q);
}
- CheckSanePath( a );
- return a;
+ CheckSanePath( ret );
+ return ret;
}
+
+
[System.Diagnostics.Conditional( "SANITY_CHECKS" )]
static void CheckSanePath( List path )
{
diff --git a/OpenRa.Game/PathSearch.cs b/OpenRa.Game/PathSearch.cs
new file mode 100755
index 0000000000..fcf140e490
--- /dev/null
+++ b/OpenRa.Game/PathSearch.cs
@@ -0,0 +1,142 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using OpenRa.Game.Graphics;
+using IjwFramework.Collections;
+
+namespace OpenRa.Game
+{
+ class PathSearch
+ {
+ public CellInfo[ , ] cellInfo;
+ public PriorityQueue queue;
+ public Func heuristic;
+ public UnitMovementType umt;
+ public bool checkForBlocked;
+
+ public PathSearch()
+ {
+ cellInfo = InitCellInfo();
+ queue = new PriorityQueue();
+ }
+
+ public int2 Expand( float[][ , ] passableCost )
+ {
+ var p = queue.Pop();
+ cellInfo[ p.Location.X, p.Location.Y ].Seen = true;
+
+ foreach( int2 d in Util.directions )
+ {
+ int2 newHere = p.Location + 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( checkForBlocked && Game.UnitInfluence.GetUnitAt( newHere ) != null )
+ continue;
+ var est = heuristic( 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[ p.Location.X, p.Location.Y ].MinCost + cellCost;
+
+ if( newCost >= cellInfo[ newHere.X, newHere.Y ].MinCost )
+ continue;
+
+ cellInfo[ newHere.X, newHere.Y ].Path = p.Location;
+ cellInfo[ newHere.X, newHere.Y ].MinCost = newCost;
+
+ queue.Add( new PathDistance( newCost + est, newHere ) );
+ }
+ return p.Location;
+ }
+
+ public void AddInitialCell( int2 location )
+ {
+ cellInfo[ location.X, location.Y ] = new CellInfo( 0, location, false );
+ queue.Add( new PathDistance( heuristic( location ), location ) );
+ }
+
+
+
+
+
+
+
+ public static PathSearch FromPoint( int2 from, int2 target, UnitMovementType umt, bool checkForBlocked )
+ {
+ var search = new PathSearch {
+ heuristic = DefaultEstimator( target ),
+ umt = umt,
+ checkForBlocked = checkForBlocked };
+
+ search.AddInitialCell( from );
+
+ return search;
+ }
+
+ public static PathSearch FromPoints( IEnumerable froms, int2 target, UnitMovementType umt, bool checkForBlocked )
+ {
+ var search = new PathSearch {
+ heuristic = DefaultEstimator( target ),
+ umt = umt,
+ checkForBlocked = checkForBlocked };
+
+ foreach( var sl in froms )
+ search.AddInitialCell( sl );
+
+ return search;
+ }
+
+ public static PathSearch FromPath( List path, int2 target, UnitMovementType umt, bool checkForBlocked )
+ {
+ var search = new PathSearch {
+ heuristic = DefaultEstimator( target ),
+ umt = umt,
+ checkForBlocked = checkForBlocked };
+
+ var cost = 0.0f;
+ var prev = path[ 0 ];
+ for( int i = 0 ; i < path.Count ; i++ )
+ {
+ var sl = path[ i ];
+ if( Game.BuildingInfluence.CanMoveHere( path[ i ] ) && Game.UnitInfluence.GetUnitAt( path[ i ] ) == null )
+ search.AddInitialCell( sl );
+
+ var d = sl - prev;
+ cost += ( ( d.X * d.Y != 0 ) ? 1.414213563f : 1.0f );// *passableCost[ (int)umt ][ sl.X, sl.Y ];
+ prev = sl;
+ }
+ return search;
+ }
+
+
+
+
+
+ static CellInfo[ , ] InitCellInfo()
+ {
+ var cellInfo = new CellInfo[ 128, 128 ];
+ for( int x = 0 ; x < 128 ; x++ )
+ for( int y = 0 ; y < 128 ; y++ )
+ cellInfo[ x, y ] = new CellInfo( float.PositiveInfinity, new int2( x, y ), false );
+ return cellInfo;
+ }
+
+ static Func DefaultEstimator( int2 destination )
+ {
+ return here =>
+ {
+ int2 d = ( here - destination ).Abs();
+ int diag = Math.Min( d.X, d.Y );
+ int straight = Math.Abs( d.X - d.Y );
+ return 1.5f * diag + straight;
+ };
+ }
+ }
+}
diff --git a/OpenRa.Game/Traits/Activities/Harvest.cs b/OpenRa.Game/Traits/Activities/Harvest.cs
index df28fc6d67..5ba012d293 100644
--- a/OpenRa.Game/Traits/Activities/Harvest.cs
+++ b/OpenRa.Game/Traits/Activities/Harvest.cs
@@ -75,20 +75,25 @@ namespace OpenRa.Game.Traits.Activities
/* find a nearby patch */
/* todo: add the queries we need to support this! */
- var path = Game.PathFinder.FindUnitPath( self.Location, loc =>
- {
- if( Game.UnitInfluence.GetUnitAt( loc ) != null ) return float.PositiveInfinity;
- return Game.map.ContainsResource( loc ) ? 0 : 1;
- }, UnitMovementType.Wheel )
+ var search = new PathSearch
+ {
+ heuristic = loc => ( Game.map.ContainsResource( loc ) ? 0 : 1 ),
+ umt = UnitMovementType.Wheel,
+ checkForBlocked = true
+ };
+ search.AddInitialCell( self.Location );
+
+ var path = Game.PathFinder.FindPath( search )
.TakeWhile( a => a != self.Location )
.ToList();
+
if( path.Count != 0 )
{
mobile.QueueActivity( new Move( path ) );
mobile.QueueActivity( new Harvest() );
}
- mobile.InternalSetActivity(NextActivity);
+ mobile.InternalSetActivity( NextActivity );
}
public void Cancel(Actor self, Mobile mobile)