diff --git a/OpenRA.Mods.RA/Activities/MoveAdjacentTo.cs b/OpenRA.Mods.RA/Activities/MoveAdjacentTo.cs index a2569415dc..d077a50de3 100755 --- a/OpenRA.Mods.RA/Activities/MoveAdjacentTo.cs +++ b/OpenRA.Mods.RA/Activities/MoveAdjacentTo.cs @@ -119,7 +119,7 @@ namespace OpenRA.Mods.RA.Activities var path = pathFinder.FindBidiPath( PathSearch.FromPoints(self.World, mobile.Info, self, searchCells, loc, true), - PathSearch.FromPoint(self.World, mobile.Info, self, loc, targetPosition, true).InReverse() + PathSearch.FromPoint(self.World, mobile.Info, self, loc, targetPosition, true).Reverse() ); inner = mobile.MoveTo(() => path); diff --git a/OpenRA.Mods.RA/Move/PathFinder.cs b/OpenRA.Mods.RA/Move/PathFinder.cs old mode 100755 new mode 100644 index bbf4a3ad39..fc899d1cc1 --- a/OpenRA.Mods.RA/Move/PathFinder.cs +++ b/OpenRA.Mods.RA/Move/PathFinder.cs @@ -68,7 +68,7 @@ namespace OpenRA.Mods.RA.Move var pb = FindBidiPath( PathSearch.FromPoint(world, mi, self, target, from, true), - PathSearch.FromPoint(world, mi, self, from, target, true).InReverse() + PathSearch.FromPoint(world, mi, self, from, target, true).Reverse() ); CheckSanePath2(pb, from, target); @@ -109,7 +109,7 @@ namespace OpenRA.Mods.RA.Move var path = FindBidiPath( PathSearch.FromPoints(world, mi, self, tilesInRange, src, true), - PathSearch.FromPoint(world, mi, self, src, targetCell, true).InReverse() + PathSearch.FromPoint(world, mi, self, src, targetCell, true).Reverse() ); return path; @@ -124,12 +124,12 @@ namespace OpenRA.Mods.RA.Move { List path = null; - while (!search.queue.Empty) + while (!search.Queue.Empty) { var p = search.Expand(world); - if (search.heuristic(p) == 0) + if (search.Heuristic(p) == 0) { - path = MakePath(search.cellInfo, p); + path = MakePath(search.CellInfo, p); break; } } @@ -137,7 +137,7 @@ namespace OpenRA.Mods.RA.Move var dbg = world.WorldActor.TraitOrDefault(); if (dbg != null) { - dbg.AddLayer(search.considered.Select(p => new Pair(p, search.cellInfo[p.X, p.Y].MinCost)), search.maxCost, search.owner); + dbg.AddLayer(search.Considered.Select(p => new Pair(p, search.CellInfo[p.X, p.Y].MinCost)), search.MaxCost, search.Owner); } if (path != null) @@ -176,13 +176,13 @@ namespace OpenRA.Mods.RA.Move { List path = null; - while (!fromSrc.queue.Empty && !fromDest.queue.Empty) + while (!fromSrc.Queue.Empty && !fromDest.Queue.Empty) { /* make some progress on the first search */ var p = fromSrc.Expand(world); - if (fromDest.cellInfo[p.X, p.Y].Seen && - fromDest.cellInfo[p.X, p.Y].MinCost < float.PositiveInfinity) + if (fromDest.CellInfo[p.X, p.Y].Seen && + fromDest.CellInfo[p.X, p.Y].MinCost < float.PositiveInfinity) { path = MakeBidiPath(fromSrc, fromDest, p); break; @@ -191,8 +191,8 @@ namespace OpenRA.Mods.RA.Move /* make some progress on the second search */ var q = fromDest.Expand(world); - if (fromSrc.cellInfo[q.X, q.Y].Seen && - fromSrc.cellInfo[q.X, q.Y].MinCost < float.PositiveInfinity) + if (fromSrc.CellInfo[q.X, q.Y].Seen && + fromSrc.CellInfo[q.X, q.Y].MinCost < float.PositiveInfinity) { path = MakeBidiPath(fromSrc, fromDest, q); break; @@ -202,8 +202,8 @@ namespace OpenRA.Mods.RA.Move var dbg = world.WorldActor.TraitOrDefault(); if (dbg != null) { - dbg.AddLayer(fromSrc.considered.Select(p => new Pair(p, fromSrc.cellInfo[p.X, p.Y].MinCost)), fromSrc.maxCost, fromSrc.owner); - dbg.AddLayer(fromDest.considered.Select(p => new Pair(p, fromDest.cellInfo[p.X, p.Y].MinCost)), fromDest.maxCost, fromDest.owner); + dbg.AddLayer(fromSrc.Considered.Select(p => new Pair(p, fromSrc.CellInfo[p.X, p.Y].MinCost)), fromSrc.MaxCost, fromSrc.Owner); + dbg.AddLayer(fromDest.Considered.Select(p => new Pair(p, fromDest.CellInfo[p.X, p.Y].MinCost)), fromDest.MaxCost, fromDest.Owner); } if (path != null) @@ -216,8 +216,8 @@ namespace OpenRA.Mods.RA.Move static List MakeBidiPath(PathSearch a, PathSearch b, CPos p) { - var ca = a.cellInfo; - var cb = b.cellInfo; + var ca = a.CellInfo; + var cb = b.CellInfo; var ret = new List(); diff --git a/OpenRA.Mods.RA/Move/PathSearch.cs b/OpenRA.Mods.RA/Move/PathSearch.cs index 3eafb2409f..e34c403213 100755 --- a/OpenRA.Mods.RA/Move/PathSearch.cs +++ b/OpenRA.Mods.RA/Move/PathSearch.cs @@ -1,6 +1,6 @@ #region Copyright & License Information /* - * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation. For more information, @@ -17,38 +17,88 @@ namespace OpenRA.Mods.RA.Move { public sealed class PathSearch : IDisposable { - World world; - public CellInfo[,] cellInfo; - public PriorityQueue queue; - public Func heuristic; + public CellInfo[,] CellInfo; + public PriorityQueue Queue; + public Func Heuristic; + public bool CheckForBlocked; + public Actor IgnoreBuilding; + public bool InReverse; + public HashSet Considered; + public Player Owner { get { return self.Owner; } } + public int MaxCost; + + Actor self; + MobileInfo mobileInfo; Func customCost; Func customBlock; - public bool checkForBlocked; - public Actor ignoreBuilding; - public bool inReverse; - public HashSet considered; - public int maxCost; Pair[] nextDirections; - MobileInfo mobileInfo; - Actor self; - public Player owner { get { return self.Owner; } } + int laneBias = 1; public PathSearch(World world, MobileInfo mobileInfo, Actor self) { - this.world = world; - cellInfo = InitCellInfo(); + this.self = self; + CellInfo = InitCellInfo(); this.mobileInfo = mobileInfo; this.self = self; customCost = null; - queue = new PriorityQueue(); - considered = new HashSet(); - maxCost = 0; + Queue = new PriorityQueue(); + Considered = new HashSet(); + MaxCost = 0; nextDirections = CVec.directions.Select(d => new Pair(d, 0)).ToArray(); } - public PathSearch InReverse() + public static PathSearch Search(World world, MobileInfo mi, Actor self, bool checkForBlocked) { - inReverse = true; + var search = new PathSearch(world, mi, self) + { + CheckForBlocked = checkForBlocked + }; + + return search; + } + + public static PathSearch FromPoint(World world, MobileInfo mi, Actor self, CPos from, CPos target, bool checkForBlocked) + { + var search = new PathSearch(world, mi, self) + { + Heuristic = DefaultEstimator(target), + CheckForBlocked = checkForBlocked + }; + + search.AddInitialCell(from); + return search; + } + + public static PathSearch FromPoints(World world, MobileInfo mi, Actor self, IEnumerable froms, CPos target, bool checkForBlocked) + { + var search = new PathSearch(world, mi, self) + { + Heuristic = DefaultEstimator(target), + CheckForBlocked = checkForBlocked + }; + + foreach (var sl in froms) + search.AddInitialCell(sl); + + return search; + } + + public static Func DefaultEstimator(CPos destination) + { + return here => + { + var diag = Math.Min(Math.Abs(here.X - destination.X), Math.Abs(here.Y - destination.Y)); + var straight = Math.Abs(here.X - destination.X) + Math.Abs(here.Y - destination.Y); + + // HACK: this relies on fp and cell-size assumptions. + var h = (3400 * diag / 24) + 100 * (straight - (2 * diag)); + return (int)(h * 1.001); + }; + } + + public PathSearch Reverse() + { + InReverse = true; return this; } @@ -60,13 +110,13 @@ namespace OpenRA.Mods.RA.Move public PathSearch WithIgnoredBuilding(Actor b) { - ignoreBuilding = b; + IgnoreBuilding = b; return this; } public PathSearch WithHeuristic(Func h) { - heuristic = h; + Heuristic = h; return this; } @@ -78,7 +128,7 @@ namespace OpenRA.Mods.RA.Move public PathSearch WithoutLaneBias() { - LaneBias = 0; + laneBias = 0; return this; } @@ -88,18 +138,18 @@ namespace OpenRA.Mods.RA.Move return this; } - int LaneBias = 1; - public CPos Expand(World world) { - var p = queue.Pop(); - while (cellInfo[p.Location.X, p.Location.Y].Seen) - if (queue.Empty) + var p = Queue.Pop(); + while (CellInfo[p.Location.X, p.Location.Y].Seen) + { + if (Queue.Empty) return p.Location; - else - p = queue.Pop(); - cellInfo[p.Location.X, p.Location.Y].Seen = true; + p = Queue.Pop(); + } + + CellInfo[p.Location.X, p.Location.Y].Seen = true; var thisCost = mobileInfo.MovementCostForCell(world, p.Location); @@ -114,7 +164,7 @@ namespace OpenRA.Mods.RA.Move } // This current cell is ok; check all immediate directions: - considered.Add(p.Location); + Considered.Add(p.Location); for (var i = 0; i < nextDirections.Length; ++i) { @@ -126,7 +176,8 @@ namespace OpenRA.Mods.RA.Move // Is this direction flat-out unusable or already seen? if (!world.Map.IsInMap(newHere)) continue; - if (cellInfo[newHere.X, newHere.Y].Seen) + + if (CellInfo[newHere.X, newHere.Y].Seen) continue; // Now we may seriously consider this direction using heuristics: @@ -135,18 +186,19 @@ namespace OpenRA.Mods.RA.Move if (costHere == int.MaxValue) continue; - if (!mobileInfo.CanEnterCell(world, self, newHere, ignoreBuilding, checkForBlocked, false)) + if (!mobileInfo.CanEnterCell(world, self, newHere, IgnoreBuilding, CheckForBlocked, false)) continue; if (customBlock != null && customBlock(newHere)) continue; - var est = heuristic(newHere); + var est = Heuristic(newHere); if (est == int.MaxValue) continue; var cellCost = costHere; - if (d.X * d.Y != 0) cellCost = (cellCost * 34) / 24; + if (d.X * d.Y != 0) + cellCost = (cellCost * 34) / 24; var userCost = 0; if (customCost != null) @@ -156,105 +208,80 @@ namespace OpenRA.Mods.RA.Move } // directional bonuses for smoother flow! - if (LaneBias != 0) + if (laneBias != 0) { - var ux = (newHere.X + (inReverse ? 1 : 0) & 1); - var uy = (newHere.Y + (inReverse ? 1 : 0) & 1); + var ux = newHere.X + (InReverse ? 1 : 0) & 1; + var uy = newHere.Y + (InReverse ? 1 : 0) & 1; - if (ux == 0 && d.Y < 0) cellCost += LaneBias; - else if (ux == 1 && d.Y > 0) cellCost += LaneBias; - if (uy == 0 && d.X < 0) cellCost += LaneBias; - else if (uy == 1 && d.X > 0) cellCost += LaneBias; + if (ux == 0 && d.Y < 0) + cellCost += laneBias; + else if (ux == 1 && d.Y > 0) + cellCost += laneBias; + + if (uy == 0 && d.X < 0) + cellCost += laneBias; + else if (uy == 1 && d.X > 0) + cellCost += laneBias; } - var newCost = cellInfo[p.Location.X, p.Location.Y].MinCost + cellCost; + var newCost = CellInfo[p.Location.X, p.Location.Y].MinCost + cellCost; // Cost is even higher; next direction: - if (newCost > cellInfo[newHere.X, newHere.Y].MinCost) + if (newCost > CellInfo[newHere.X, newHere.Y].MinCost) continue; - cellInfo[newHere.X, newHere.Y].Path = p.Location; - cellInfo[newHere.X, newHere.Y].MinCost = newCost; + CellInfo[newHere.X, newHere.Y].Path = p.Location; + CellInfo[newHere.X, newHere.Y].MinCost = newCost; nextDirections[i].Second = newCost + est; - queue.Add(new PathDistance(newCost + est, newHere)); + Queue.Add(new PathDistance(newCost + est, newHere)); - if (newCost > maxCost) maxCost = newCost; - considered.Add(newHere); + if (newCost > MaxCost) + MaxCost = newCost; + + Considered.Add(newHere); } // Sort to prefer the cheaper direction: - //Array.Sort(nextDirections, (a, b) => a.Second.CompareTo(b.Second)); - + // Array.Sort(nextDirections, (a, b) => a.Second.CompareTo(b.Second)); return p.Location; } public void AddInitialCell(CPos location) { - if (!world.Map.IsInMap(location)) + if (!self.World.Map.IsInMap(location)) return; - cellInfo[location.X, location.Y] = new CellInfo(0, location, false); - queue.Add(new PathDistance(heuristic(location), location)); + CellInfo[location.X, location.Y] = new CellInfo(0, location, false); + Queue.Add(new PathDistance(Heuristic(location), location)); } - public static PathSearch Search(World world, MobileInfo mi, Actor self, bool checkForBlocked) - { - var search = new PathSearch(world, mi, self) - { - checkForBlocked = checkForBlocked - }; - return search; - } - - public static PathSearch FromPoint(World world, MobileInfo mi, Actor self, CPos from, CPos target, bool checkForBlocked) - { - var search = new PathSearch(world, mi, self) - { - heuristic = DefaultEstimator(target), - checkForBlocked = checkForBlocked - }; - - search.AddInitialCell(from); - return search; - } - - public static PathSearch FromPoints(World world, MobileInfo mi, Actor self, IEnumerable froms, CPos target, bool checkForBlocked) - { - var search = new PathSearch(world, mi, self) - { - heuristic = DefaultEstimator(target), - checkForBlocked = checkForBlocked - }; - - foreach (var sl in froms) - search.AddInitialCell(sl); - - return search; - } - - static readonly Queue cellInfoPool = new Queue(); + static readonly Queue CellInfoPool = new Queue(); static CellInfo[,] GetFromPool() { - lock (cellInfoPool) - return cellInfoPool.Dequeue(); + lock (CellInfoPool) + return CellInfoPool.Dequeue(); } static void PutBackIntoPool(CellInfo[,] ci) { - lock (cellInfoPool) - cellInfoPool.Enqueue(ci); + lock (CellInfoPool) + CellInfoPool.Enqueue(ci); } CellInfo[,] InitCellInfo() { CellInfo[,] result = null; - while (cellInfoPool.Count > 0) + + // 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 + // explicitly to the things that need to share it. + while (CellInfoPool.Count > 0) { var cellInfo = GetFromPool(); - if (cellInfo.GetUpperBound(0) != world.Map.MapSize.X - 1 || - cellInfo.GetUpperBound(1) != world.Map.MapSize.Y - 1) + if (cellInfo.GetUpperBound(0) != self.World.Map.MapSize.X - 1 || + cellInfo.GetUpperBound(1) != self.World.Map.MapSize.Y - 1) { Log.Write("debug", "Discarding old pooled CellInfo of wrong size."); continue; @@ -265,36 +292,25 @@ namespace OpenRA.Mods.RA.Move } if (result == null) - result = new CellInfo[world.Map.MapSize.X, world.Map.MapSize.Y]; + result = new CellInfo[self.World.Map.MapSize.X, self.World.Map.MapSize.Y]; - for (var x = 0; x < world.Map.MapSize.X; x++) - for (var y = 0; y < world.Map.MapSize.Y; y++) - result[ x, y ] = new CellInfo( int.MaxValue, new CPos( x, y ), false ); + for (var x = 0; x < self.World.Map.MapSize.X; x++) + for (var y = 0; y < self.World.Map.MapSize.Y; y++) + result[x, y] = new CellInfo(int.MaxValue, new CPos(x, y), false); return result; } - public static Func DefaultEstimator(CPos destination) - { - return here => - { - var diag = Math.Min(Math.Abs(here.X - destination.X), Math.Abs(here.Y - destination.Y)); - var straight = (Math.Abs(here.X - destination.X) + Math.Abs(here.Y - destination.Y)); - var h = (3400 * diag / 24) + 100 * (straight - (2 * diag)); - h = (int)(h * 1.001); - return h; - }; - } - bool disposed; public void Dispose() { if (disposed) return; + disposed = true; - PutBackIntoPool(cellInfo); - cellInfo = null; + PutBackIntoPool(CellInfo); + CellInfo = null; GC.SuppressFinalize(this); }