Added traveling salesman problem solver class and modified MainForm to use it.
Added traveling salesman problem solver class and modified MainForm to use it.
diff --git a/PokemonGo/RocketAPI/Window/MainForm.cs b/PokemonGo/RocketAPI/Window/MainForm.cs
index fa85042..a0e27f5 100644
--- a/PokemonGo/RocketAPI/Window/MainForm.cs
+++ b/PokemonGo/RocketAPI/Window/MainForm.cs
@@ -427,6 +427,7 @@ namespace PokemonGo.RocketAPI.Window
IEnumerable<FortData> nextPokeStopList = null;
if (!ForceUnbanning)
ColoredConsoleWrite(Color.Cyan, $"Visiting {pokeStops.Count()} PokeStops");
+ pokeStops = (IEnumerable<FortData>)TSP<ICoordinate>.getMinimumTour(pokeStops, (new CoordinateMetric()).distance);
foreach (var pokeStop in pokeStops)
{
if (ForceUnbanning)
diff --git a/PokemonGo/RocketAPI/Window/PokemonGo.RocketAPI.Window.csproj b/PokemonGo/RocketAPI/Window/PokemonGo.RocketAPI.Window.csproj
index 1c8ccf1..8902e1d 100644
--- a/PokemonGo/RocketAPI/Window/PokemonGo.RocketAPI.Window.csproj
+++ b/PokemonGo/RocketAPI/Window/PokemonGo.RocketAPI.Window.csproj
@@ -99,6 +99,7 @@
<Compile Include="SettingsForm.Designer.cs">
<DependentUpon>SettingsForm.cs</DependentUpon>
</Compile>
+ <Compile Include="TSP.cs" />
<EmbeddedResource Include="MainForm.resx">
<DependentUpon>MainForm.cs</DependentUpon>
</EmbeddedResource>
diff --git a/PokemonGo/RocketAPI/Window/TSP.cs b/PokemonGo/RocketAPI/Window/TSP.cs
new file mode 100644
index 0000000..90482c6
--- /dev/null
+++ b/PokemonGo/RocketAPI/Window/TSP.cs
@@ -0,0 +1,227 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace PokemonGo.RocketAPI.Window
+{
+ /* Copied from here, with modifications to make more generic
+ * http://stackoverflow.com/questions/2927469/traveling-salesman-problem-2-opt-algorithm-c-sharp-implementation
+ There's probably a much better implementation of TSP's solution */
+ public static class TSP<T>
+ {
+ private static Func<T, T, double> distance;
+ public static IEnumerable<T> getMinimumTour(IEnumerable<T> nodes, Func<T,T,double> distance)
+ {
+ TSP<T>.distance = distance;
+ //create an initial tour out of nearest neighbors
+ var stops = nodes
+ .Select(i => new Stop(i))
+ .NearestNeighbors()
+ .ToList();
+
+ //create next pointers between them
+ stops.Connect(true);
+
+ //wrap in a tour object
+ Tour startingTour = new Tour(stops);
+
+ //the actual algorithm
+ while (true)
+ {
+ var newTour = startingTour.GenerateMutations()
+ .MinBy(tour => tour.Cost());
+ if (newTour.Cost() < startingTour.Cost()) startingTour = newTour;
+ else break;
+ }
+ return startingTour.Cycle().Select(s => s.City);
+ }
+
+ internal class Stop
+ {
+ public Stop(T city)
+ {
+ City = city;
+ }
+
+
+ public Stop Next { get; set; }
+
+ public T City { get; set; }
+
+
+ public Stop Clone()
+ {
+ return new Stop(City);
+ }
+
+
+ public static double Distance(Stop first, Stop other)
+ {
+ return TSP<T>.distance(first.City, other.City);
+ }
+
+
+ //list of nodes, including this one, that we can get to
+ public IEnumerable<Stop> CanGetTo()
+ {
+ var current = this;
+ while (true)
+ {
+ yield return current;
+ current = current.Next;
+ if (current == this) break;
+ }
+ }
+
+
+ public override bool Equals(object obj)
+ {
+ return EqualityComparer<T>.Default.Equals(City,((Stop)obj).City);
+ }
+
+
+ public override int GetHashCode()
+ {
+ return City.GetHashCode();
+ }
+
+ }
+
+
+ internal class Tour
+ {
+ public Tour(IEnumerable<Stop> stops)
+ {
+ Anchor = stops.First();
+ }
+
+
+ //the set of tours we can make with 2-opt out of this one
+ public IEnumerable<Tour> GenerateMutations()
+ {
+ for (Stop stop = Anchor; stop.Next != Anchor; stop = stop.Next)
+ {
+ //skip the next one, since you can't swap with that
+ Stop current = stop.Next.Next;
+ while (current != Anchor)
+ {
+ yield return CloneWithSwap(stop.City, current.City);
+ current = current.Next;
+ }
+ }
+ }
+
+
+ public Stop Anchor { get; set; }
+
+
+ public Tour CloneWithSwap(T firstCity, T secondCity)
+ {
+ Stop firstFrom = null, secondFrom = null;
+ var stops = UnconnectedClones();
+ stops.Connect(true);
+
+ foreach (Stop stop in stops)
+ {
+ if (EqualityComparer<T>.Default.Equals(stop.City, firstCity)) firstFrom = stop;
+
+ if (EqualityComparer<T>.Default.Equals(stop.City, secondCity)) secondFrom = stop;
+ }
+
+ //the swap part
+ var firstTo = firstFrom.Next;
+ var secondTo = secondFrom.Next;
+
+ //reverse all of the links between the swaps
+ firstTo.CanGetTo()
+ .TakeWhile(stop => stop != secondTo)
+ .Reverse()
+ .Connect(false);
+
+ firstTo.Next = secondTo;
+ firstFrom.Next = secondFrom;
+
+ var tour = new Tour(stops);
+ return tour;
+ }
+
+
+ public IList<Stop> UnconnectedClones()
+ {
+ return Cycle().Select(stop => stop.Clone()).ToList();
+ }
+
+
+ public double Cost()
+ {
+ return Cycle().Aggregate(
+ 0.0,
+ (sum, stop) =>
+ sum + Stop.Distance(stop, stop.Next));
+ }
+
+
+ public IEnumerable<Stop> Cycle()
+ {
+ return Anchor.CanGetTo();
+ }
+
+
+ public override string ToString()
+ {
+ string path = String.Join(
+ "->",
+ Cycle().Select(stop => stop.ToString()).ToArray());
+ return String.Format("Cost: {0}, Path:{1}", Cost(), path);
+ }
+
+ }
+ }
+ public static class ExtensionMethods
+ {
+
+ //take an ordered list of nodes and set their next properties
+ internal static void Connect<T>(this IEnumerable<TSP<T>.Stop> stops, bool loop)
+
+ {
+ TSP<T>.Stop prev = null, first = null;
+ foreach (var stop in stops)
+ {
+ if (first == null) first = stop;
+ if (prev != null) prev.Next = stop;
+ prev = stop;
+ }
+
+ if (loop)
+ {
+ prev.Next = first;
+ }
+ }
+
+
+ //T with the smallest func(T)
+ internal static T MinBy<T, TComparable> (
+ this IEnumerable<T> xs,
+ Func<T, TComparable> func)
+ where TComparable : IComparable<TComparable>
+ {
+ return xs.DefaultIfEmpty().Aggregate(
+ (maxSoFar, elem) =>
+ func(elem).CompareTo(func(maxSoFar)) > 0 ? maxSoFar : elem);
+ }
+
+
+ //return an ordered nearest neighbor set
+ internal static IEnumerable<TSP<T>.Stop> NearestNeighbors<T>(this IEnumerable<TSP<T>.Stop> stops)
+ {
+ var stopsLeft = stops.ToList();
+ for (var stop = stopsLeft.First();
+ stop != null;
+ stop = stopsLeft.MinBy(s => TSP<T>.Stop.Distance(stop, s)))
+ {
+ stopsLeft.Remove(stop);
+ yield return stop;
+ }
+ }
+ }
+}
You may download the files in Public Git.