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)