From 0ea3509ee479d81ed87b9918720425a1ae68344a Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Thu, 22 May 2014 01:30:48 +0100 Subject: [PATCH] Added MinBy, MaxBy, MinByOrDefault and MaxByOrDefault methods and replaced calls of the style OrderBy[Descending]().First[OrDefault]() which is not as performant. --- OpenRA.Editor/Form1.cs | 2 +- OpenRA.Game/Exts.cs | 48 +++++++++++++++++++ OpenRA.Game/FileSystem/GlobalFileSystem.cs | 6 +-- OpenRA.Game/Orders/UnitOrderGenerator.cs | 22 +++++---- OpenRA.Game/Server/Server.cs | 2 +- .../Widgets/ViewportControllerWidget.cs | 6 +-- .../WorldInteractionControllerWidget.cs | 3 +- OpenRA.Game/WorldUtils.cs | 5 +- .../Widgets/ProductionPaletteWidget.cs | 2 +- OpenRA.Mods.RA/AI/HackyAI.cs | 22 ++++----- OpenRA.Mods.RA/Activities/Teleport.cs | 2 +- OpenRA.Mods.RA/Air/ReturnToBase.cs | 3 +- OpenRA.Mods.RA/MPStartLocations.cs | 4 +- OpenRA.Mods.RA/Minelayer.cs | 5 +- OpenRA.Mods.RA/TeslaZapRenderable.cs | 2 +- OpenRA.Utility/Command.cs | 3 +- 16 files changed, 90 insertions(+), 47 deletions(-) diff --git a/OpenRA.Editor/Form1.cs b/OpenRA.Editor/Form1.cs index d557ce85e9..2cdcdddaa6 100644 --- a/OpenRA.Editor/Form1.cs +++ b/OpenRA.Editor/Form1.cs @@ -162,7 +162,7 @@ namespace OpenRA.Editor foreach (var p in palettes) { p.Visible = false; p.SuspendLayout(); } var templateOrder = tileset.EditorTemplateOrder ?? new string[] { }; - foreach (var tc in tileset.Templates.GroupBy(t => t.Value.Category).OrderBy(t => templateOrder.ToList().IndexOf(t.Key))) + foreach (var tc in tileset.Templates.GroupBy(t => t.Value.Category).OrderBy(t => Array.IndexOf(templateOrder, t.Key))) { var category = tc.Key ?? "(Uncategorized)"; var categoryHeader = new Label diff --git a/OpenRA.Game/Exts.cs b/OpenRA.Game/Exts.cs index af75d68d85..8ad658b828 100644 --- a/OpenRA.Game/Exts.cs +++ b/OpenRA.Game/Exts.cs @@ -131,6 +131,54 @@ namespace OpenRA for (;;) { yield return t; t = f(t); } } + public static T MinBy(this IEnumerable ts, Func selector) + { + return ts.CompareBy(selector, 1, true); + } + + public static T MaxBy(this IEnumerable ts, Func selector) + { + return ts.CompareBy(selector, -1, true); + } + + public static T MinByOrDefault(this IEnumerable ts, Func selector) + { + return ts.CompareBy(selector, 1, false); + } + + public static T MaxByOrDefault(this IEnumerable ts, Func selector) + { + return ts.CompareBy(selector, -1, false); + } + + static T CompareBy(this IEnumerable ts, Func selector, int modifier, bool throws) + { + var comparer = Comparer.Default; + T t; + U u; + using (var e = ts.GetEnumerator()) + { + if (!e.MoveNext()) + if (throws) + throw new ArgumentException("Collection must not be empty.", "ts"); + else + return default(T); + t = e.Current; + u = selector(t); + while (e.MoveNext()) + { + T nextT = e.Current; + U nextU = selector(nextT); + if (comparer.Compare(nextU, u) * modifier < 0) + { + t = nextT; + u = nextU; + } + } + return t; + } + } + public static int NextPowerOf2(int v) { --v; diff --git a/OpenRA.Game/FileSystem/GlobalFileSystem.cs b/OpenRA.Game/FileSystem/GlobalFileSystem.cs index 45ca6af2bf..21a8aee4c5 100644 --- a/OpenRA.Game/FileSystem/GlobalFileSystem.cs +++ b/OpenRA.Game/FileSystem/GlobalFileSystem.cs @@ -156,8 +156,7 @@ namespace OpenRA.FileSystem var index = type == PackageHashType.CRC32 ? crcHashIndex : classicHashIndex; var folder = index[PackageEntry.HashFilename(filename, type)] .Where(x => x.Exists(filename)) - .OrderBy(x => x.Priority) - .FirstOrDefault(); + .MinByOrDefault(x => x.Priority); if (folder != null) return folder.GetContent(filename); @@ -196,8 +195,7 @@ namespace OpenRA.FileSystem { var folder = MountedFolders .Where(x => x.Exists(filename + ext)) - .OrderByDescending(x => x.Priority) - .FirstOrDefault(); + .MaxByOrDefault(x => x.Priority); if (folder != null) { diff --git a/OpenRA.Game/Orders/UnitOrderGenerator.cs b/OpenRA.Game/Orders/UnitOrderGenerator.cs index da741d7a87..ce334a5a7c 100644 --- a/OpenRA.Game/Orders/UnitOrderGenerator.cs +++ b/OpenRA.Game/Orders/UnitOrderGenerator.cs @@ -21,8 +21,7 @@ namespace OpenRA.Orders { var underCursor = world.ScreenMap.ActorsAt(mi) .Where(a => !world.FogObscures(a) && a.HasTrait()) - .OrderByDescending(a => a.Info.SelectionPriority()) - .FirstOrDefault(); + .WithHighestSelectionPriority(); Target target; if (underCursor != null) @@ -31,8 +30,7 @@ namespace OpenRA.Orders { var frozen = world.ScreenMap.FrozenActorsAt(world.RenderPlayer, mi) .Where(a => a.Info.Traits.Contains()) - .OrderByDescending(a => a.Info.SelectionPriority()) - .FirstOrDefault(); + .WithHighestSelectionPriority(); target = frozen != null ? Target.FromFrozenActor(frozen) : Target.FromCell(xy); } @@ -61,8 +59,7 @@ namespace OpenRA.Orders var useSelect = false; var underCursor = world.ScreenMap.ActorsAt(mi) .Where(a => !world.FogObscures(a) && a.HasTrait()) - .OrderByDescending(a => a.Info.SelectionPriority()) - .FirstOrDefault(); + .WithHighestSelectionPriority(); if (underCursor != null && (mi.Modifiers.HasModifier(Modifiers.Shift) || !world.Selection.Actors.Any())) { @@ -78,8 +75,7 @@ namespace OpenRA.Orders { var frozen = world.ScreenMap.FrozenActorsAt(world.RenderPlayer, mi) .Where(a => a.Info.Traits.Contains()) - .OrderByDescending(a => a.Info.SelectionPriority()) - .FirstOrDefault(); + .WithHighestSelectionPriority(); target = frozen != null ? Target.FromFrozenActor(frozen) : Target.FromCell(xy); } @@ -161,5 +157,15 @@ namespace OpenRA.Orders var selectableInfo = a.Traits.GetOrDefault(); return selectableInfo != null ? selectableInfo.Priority : int.MinValue; } + + public static Actor WithHighestSelectionPriority(this IEnumerable actors) + { + return actors.MaxByOrDefault(a => a.Info.SelectionPriority()); + } + + public static FrozenActor WithHighestSelectionPriority(this IEnumerable actors) + { + return actors.MaxByOrDefault(a => a.Info.SelectionPriority()); + } } } diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index 7675ce5c09..fcf04441bd 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -535,7 +535,7 @@ namespace OpenRA.Server LobbyInfo.Clients.RemoveAll(c => c.Bot != null && c.BotControllerClientIndex == toDrop.PlayerIndex); var nextAdmin = LobbyInfo.Clients.Where(c1 => c1.Bot == null) - .OrderBy(c => c.Index).FirstOrDefault(); + .MinByOrDefault(c => c.Index); if (nextAdmin != null) { diff --git a/OpenRA.Game/Widgets/ViewportControllerWidget.cs b/OpenRA.Game/Widgets/ViewportControllerWidget.cs index bb1eec757c..f1ee4fe5e4 100644 --- a/OpenRA.Game/Widgets/ViewportControllerWidget.cs +++ b/OpenRA.Game/Widgets/ViewportControllerWidget.cs @@ -106,8 +106,7 @@ namespace OpenRA.Widgets var underCursor = world.ScreenMap.ActorsAt(worldRenderer.Viewport.ViewToWorldPx(Viewport.LastMousePos)) .Where(a => !world.FogObscures(a) && a.HasTrait()) - .OrderByDescending(a => a.Info.SelectionPriority()) - .FirstOrDefault(); + .WithHighestSelectionPriority(); if (underCursor != null) { @@ -118,8 +117,7 @@ namespace OpenRA.Widgets var frozen = world.ScreenMap.FrozenActorsAt(world.RenderPlayer, worldRenderer.Viewport.ViewToWorldPx(Viewport.LastMousePos)) .Where(a => a.TooltipName != null) - .OrderByDescending(a => a.Info.SelectionPriority()) - .FirstOrDefault(); + .WithHighestSelectionPriority(); if (frozen != null) { diff --git a/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs b/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs index 481b085398..ec3fe22fdb 100644 --- a/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs +++ b/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs @@ -93,8 +93,7 @@ namespace OpenRA.Widgets if (multiClick) { var unit = World.ScreenMap.ActorsAt(xy) - .OrderByDescending(a => a.Info.SelectionPriority()) - .FirstOrDefault(); + .WithHighestSelectionPriority(); var newSelection2 = SelectActorsInBox(World, worldRenderer.Viewport.TopLeft, worldRenderer.Viewport.BottomRight, a => unit != null && a.Info.Name == unit.Info.Name && a.Owner == unit.Owner); diff --git a/OpenRA.Game/WorldUtils.cs b/OpenRA.Game/WorldUtils.cs index 90cce2b55e..348d916f00 100644 --- a/OpenRA.Game/WorldUtils.cs +++ b/OpenRA.Game/WorldUtils.cs @@ -35,13 +35,12 @@ namespace OpenRA public static Actor ClosestTo(this IEnumerable actors, Actor a) { - var pos = a.CenterPosition; - return actors.OrderBy(b => (b.CenterPosition - pos).LengthSquared).FirstOrDefault(); + return actors.ClosestTo(a.CenterPosition); } public static Actor ClosestTo(this IEnumerable actors, WPos pos) { - return actors.OrderBy(a => (a.CenterPosition - pos).LengthSquared).FirstOrDefault(); + return actors.MinByOrDefault(a => (a.CenterPosition - pos).LengthSquared); } public static IEnumerable FindActorsInCircle(this World world, WPos origin, WRange r) diff --git a/OpenRA.Mods.Cnc/Widgets/ProductionPaletteWidget.cs b/OpenRA.Mods.Cnc/Widgets/ProductionPaletteWidget.cs index 00d56db16a..f034f07991 100644 --- a/OpenRA.Mods.Cnc/Widgets/ProductionPaletteWidget.cs +++ b/OpenRA.Mods.Cnc/Widgets/ProductionPaletteWidget.cs @@ -213,7 +213,7 @@ namespace OpenRA.Mods.Cnc.Widgets if (CurrentQueue == null) return; - var buildableItems = CurrentQueue.BuildableItems().OrderBy(a => a.Traits.Get().BuildPaletteOrder); + var buildableItems = CurrentQueue.BuildableItems(); // Background foreach (var rect in icons.Keys) diff --git a/OpenRA.Mods.RA/AI/HackyAI.cs b/OpenRA.Mods.RA/AI/HackyAI.cs index 814d815723..6ca5e8c032 100644 --- a/OpenRA.Mods.RA/AI/HackyAI.cs +++ b/OpenRA.Mods.RA/AI/HackyAI.cs @@ -298,7 +298,7 @@ namespace OpenRA.Mods.RA.AI // Try to maintain 20% excess power if (!HasAdequatePower()) return buildableThings.Where(a => GetPowerProvidedBy(a) > 0) - .OrderByDescending(a => GetPowerProvidedBy(a)).FirstOrDefault(); + .MaxByOrDefault(a => GetPowerProvidedBy(a)); if (playerResource.AlertSilo) return GetBuildingInfoByCommonName("Silo", p); @@ -365,9 +365,13 @@ namespace OpenRA.Mods.RA.AI case BuildingType.Refinery: var tilesPos = world.FindTilesInCircle(baseCenter, MaxBaseDistance) - .Where(a => resourceTypes.Contains(world.GetTerrainType(new CPos(a.X, a.Y)))) - .OrderBy(a => (a.CenterPosition - baseCenter.CenterPosition).LengthSquared); - return tilesPos.Any() ? findPos(tilesPos.First().CenterPosition, baseCenter) : null; + .Where(a => resourceTypes.Contains(world.GetTerrainType(new CPos(a.X, a.Y)))); + if (tilesPos.Any()) + { + var pos = tilesPos.MinBy(a => (a.CenterPosition - baseCenter.CenterPosition).LengthSquared); + return findPos(pos.CenterPosition, baseCenter); + } + return null; case BuildingType.Building: for (var k = 0; k < maxBaseDistance; k++) @@ -426,8 +430,7 @@ namespace OpenRA.Mods.RA.AI var leastLikedEnemies = liveEnemies .GroupBy(e => aggro[e].Aggro) - .OrderByDescending(g => g.Key) - .FirstOrDefault(); + .MaxByOrDefault(g => g.Key); var enemy = (leastLikedEnemies != null) ? leastLikedEnemies.Random(random) : liveEnemies.FirstOrDefault(); @@ -457,12 +460,9 @@ namespace OpenRA.Mods.RA.AI { var allEnemyUnits = world.Actors .Where(unit => p.Stances[unit.Owner] == Stance.Enemy && !unit.HasTrait() && - unit.HasTrait()).ToList(); + unit.HasTrait()); - if (allEnemyUnits.Count > 0) - return allEnemyUnits.ClosestTo(pos); - - return null; + return allEnemyUnits.ClosestTo(pos); } internal Actor FindClosestEnemy(WPos pos, WRange radius) diff --git a/OpenRA.Mods.RA/Activities/Teleport.cs b/OpenRA.Mods.RA/Activities/Teleport.cs index 05197c3ace..b9b25f80ae 100755 --- a/OpenRA.Mods.RA/Activities/Teleport.cs +++ b/OpenRA.Mods.RA/Activities/Teleport.cs @@ -93,7 +93,7 @@ namespace OpenRA.Mods.RA.Activities var restrictTo = maximumDistance == null ? null : self.World.FindTilesInCircle(self.Location, maximumDistance.Value); if (maximumDistance != null) - destination = restrictTo.OrderBy(x => (x - destination).Length).First(); + destination = restrictTo.MinBy(x => (x - destination).LengthSquared); var pos = self.Trait(); if (pos.CanEnterCell(destination) && self.Owner.Shroud.IsExplored(destination)) diff --git a/OpenRA.Mods.RA/Air/ReturnToBase.cs b/OpenRA.Mods.RA/Air/ReturnToBase.cs index 9e241793ea..c9b996553a 100755 --- a/OpenRA.Mods.RA/Air/ReturnToBase.cs +++ b/OpenRA.Mods.RA/Air/ReturnToBase.cs @@ -69,8 +69,7 @@ namespace OpenRA.Mods.RA.Air var side = new WVec(-fwd.Y, fwd.X, fwd.Z); var approachDelta = self.CenterPosition - approachStart; var sideTowardBase = new[] { side, -side } - .OrderBy(a => WVec.Dot(a, approachDelta)) - .First(); + .MinBy(a => WVec.Dot(a, approachDelta)); // Calculate the tangent line that joins the turning circles at the current and approach positions var cp = self.CenterPosition + turnRadius * sideTowardBase / 1024; diff --git a/OpenRA.Mods.RA/MPStartLocations.cs b/OpenRA.Mods.RA/MPStartLocations.cs index 6569df50d9..695dcf6467 100755 --- a/OpenRA.Mods.RA/MPStartLocations.cs +++ b/OpenRA.Mods.RA/MPStartLocations.cs @@ -77,9 +77,7 @@ namespace OpenRA.Mods.RA ? world.SharedRandom.Next(available.Count) : available // pick the most distant spawnpoint from everyone else .Select((k, i) => Pair.New(k, i)) - .OrderByDescending(a => taken.Sum(t => (t - a.First).LengthSquared)) - .Select(a => a.Second) - .First(); + .MaxBy(a => taken.Sum(t => (t - a.First).LengthSquared)).Second; var sp = available[n]; available.RemoveAt(n); diff --git a/OpenRA.Mods.RA/Minelayer.cs b/OpenRA.Mods.RA/Minelayer.cs index 9ced402a6f..1910930ef9 100644 --- a/OpenRA.Mods.RA/Minelayer.cs +++ b/OpenRA.Mods.RA/Minelayer.cs @@ -134,9 +134,8 @@ namespace OpenRA.Mods.RA var underCursor = world.ScreenMap.ActorsAt(mi) .Where(a => !world.FogObscures(a)) - .OrderByDescending(a => a.Info.Traits.Contains() - ? a.Info.Traits.Get().Priority : int.MinValue) - .FirstOrDefault(); + .MaxByOrDefault(a => a.Info.Traits.Contains() + ? a.Info.Traits.Get().Priority : int.MinValue); if (mi.Button == Game.mouseButtonPreference.Action && underCursor == null) { diff --git a/OpenRA.Mods.RA/TeslaZapRenderable.cs b/OpenRA.Mods.RA/TeslaZapRenderable.cs index b4ee8b629e..b3ac8bf6bf 100755 --- a/OpenRA.Mods.RA/TeslaZapRenderable.cs +++ b/OpenRA.Mods.RA/TeslaZapRenderable.cs @@ -132,7 +132,7 @@ namespace OpenRA.Mods.RA while ((to - z).X > 5 || (to - z).X < -5 || (to - z).Y > 5 || (to - z).Y < -5) { var step = steps.Where(t => (to - (z + new float2(t[0], t[1]))).LengthSquared < (to - z).LengthSquared) - .OrderBy(t => Math.Abs(float2.Dot(z + new float2(t[0], t[1]), q) + c)).First(); + .MinBy(t => Math.Abs(float2.Dot(z + new float2(t[0], t[1]), q) + c)); var pos = wr.Position((z + new float2(step[2], step[3])).ToInt2()); rs.Add(new SpriteRenderable(s.GetSprite(step[4]), pos, WVec.Zero, 0, pal, 1f, true)); diff --git a/OpenRA.Utility/Command.cs b/OpenRA.Utility/Command.cs index aa2705e834..76631b3242 100644 --- a/OpenRA.Utility/Command.cs +++ b/OpenRA.Utility/Command.cs @@ -222,8 +222,7 @@ namespace OpenRA.Utility if (!remap.ContainsKey(i)) remap[i] = fullIndexRange .Where(a => !remap.ContainsValue(a)) - .OrderBy(a => ColorDistance(destPalette.Values[a], srcPalette.Values[i])) - .First(); + .MinBy(a => ColorDistance(destPalette.Values[a], srcPalette.Values[i])); var srcImage = ShpReader.Load(args[3]);