diff --git a/PokemonGo/RocketAPI/Extensions/LatLongExtensions.cs b/PokemonGo/RocketAPI/Extensions/LatLongExtensions.cs
new file mode 100644
index 0000000..94c98cd
--- /dev/null
+++ b/PokemonGo/RocketAPI/Extensions/LatLongExtensions.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PokemonGo.RocketAPI.Extensions
+{
+ public static class LatLongExtensions
+ {
+ public static double distanceFrom(this ILatLong c1, ILatLong c2)
+ {
+ double R = 6371e3;
+ Func<double, float> toRad = x => (float)(x * (Math.PI / 180));
+ float lat1 = toRad(c1.Latitude);
+ float lat2 = toRad(c2.Latitude);
+ float dLat = toRad(c2.Latitude - c1.Latitude);
+ float dLng = toRad(c2.Longitude - c1.Longitude);
+ double h = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Cos(lat1) * Math.Cos(lat2) * Math.Sin(dLng / 2) * Math.Sin(dLng / 2);
+ double c = 2 * Math.Atan2(Math.Sqrt(h), Math.Sqrt(1 - h));
+ return R * c;
+ }
+ }
+}
+
diff --git a/PokemonGo/RocketAPI/ICoordinate.cs b/PokemonGo/RocketAPI/ICoordinate.cs
deleted file mode 100644
index 14c6ae6..0000000
--- a/PokemonGo/RocketAPI/ICoordinate.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace PokemonGo.RocketAPI
-{
- public interface ICoordinate
- {
- double Latitude { get; }
- double Longitude { get; }
- }
- public interface MetricSpace<T>
- {
- double distance(T t1, T t2);
- }
- public class CoordinateMetric : MetricSpace<ICoordinate>
- {
- public double distance(ICoordinate c1, ICoordinate c2)
- {
- double R = 6371;
- Func<double, double> toRad = x => x * (Math.PI / 180);
- double dLat = toRad(c2.Latitude - c1.Latitude);
- double dLong = toRad(c2.Longitude - c2.Longitude);
- double lat1 = toRad(c1.Latitude);
- double lat2 = toRad(c2.Latitude);
- double a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) +
- Math.Sin(dLong / 2) * Math.Sin(dLong / 2) * Math.Cos(lat1) * Math.Cos(lat2);
- double c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
- return R * c;
- }
- }
-}
diff --git a/PokemonGo/RocketAPI/ILatLong.cs b/PokemonGo/RocketAPI/ILatLong.cs
new file mode 100644
index 0000000..c0fd3fc
--- /dev/null
+++ b/PokemonGo/RocketAPI/ILatLong.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PokemonGo.RocketAPI
+{
+ public interface ILatLong
+ {
+ double Latitude { get; }
+ double Longitude { get; }
+ }
+
+ public class LatLong : Tuple<double, double>, ILatLong
+ {
+ public LatLong(double item1, double item2) : base(item1, item2)
+ {
+ }
+
+ public double Latitude
+ {
+ get
+ {
+ return this.Item1;
+ }
+ }
+ public double Longitude
+ {
+ get
+ {
+ return this.Item2;
+ }
+ }
+ }
+}
diff --git a/PokemonGo/RocketAPI/PokemonGo.RocketAPI.csproj b/PokemonGo/RocketAPI/PokemonGo.RocketAPI.csproj
index b60f39c..8a6404d 100644
--- a/PokemonGo/RocketAPI/PokemonGo.RocketAPI.csproj
+++ b/PokemonGo/RocketAPI/PokemonGo.RocketAPI.csproj
@@ -68,7 +68,8 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
- <Compile Include="ICoordinate.cs" />
+ <Compile Include="Extensions\LatLongExtensions.cs" />
+ <Compile Include="ILatLong.cs" />
<Compile Include="Enums\AuthType.cs" />
<Compile Include="Enums\MiscEnums.cs" />
<Compile Include="Enums\RequestType.cs" />
diff --git a/PokemonGo/RocketAPI/ProtoAdditions.cs b/PokemonGo/RocketAPI/ProtoAdditions.cs
index fc5ce6d..ea0ecf2 100644
--- a/PokemonGo/RocketAPI/ProtoAdditions.cs
+++ b/PokemonGo/RocketAPI/ProtoAdditions.cs
@@ -6,12 +6,12 @@ using System.Threading.Tasks;
namespace PokemonGo.RocketAPI.GeneratedCode
{
- public partial class FortData : ICoordinate { }
+ public partial class FortData : ILatLong { }
- public partial class MapPokemon : ICoordinate { }
+ public partial class MapPokemon : ILatLong { }
- public partial class WildPokemon : ICoordinate { }
+ public partial class WildPokemon : ILatLong { }
- public partial class SpawnPoint : ICoordinate { }
+ public partial class SpawnPoint : ILatLong { }
}
diff --git a/PokemonGo/RocketAPI/Window/LocationManager.cs b/PokemonGo/RocketAPI/Window/LocationManager.cs
index 1bffc25..6b6f3b5 100644
--- a/PokemonGo/RocketAPI/Window/LocationManager.cs
+++ b/PokemonGo/RocketAPI/Window/LocationManager.cs
@@ -4,61 +4,33 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PokemonGo.RocketAPI;
+using PokemonGo.RocketAPI.Extensions;
namespace PokemonGo.RocketAPI.Window
{
public class LocationManager
{
private Client client;
- private double kilometersPerMillisecond;
+ private double metersPerMillisecond;
public LocationManager(Client client, double speed)
{
this.client = client;
- this.kilometersPerMillisecond = speed / 3600000;
+ this.metersPerMillisecond = speed / 3600;
}
public double getDistance(double lat, double lng)
{
- Coordinate currentLoc = new Coordinate(client.CurrentLatitude, client.CurrentLongitude);
- return currentLoc.distanceFrom(new Coordinate(lat, lng));
+ LatLong currentLoc = new LatLong(client.CurrentLatitude, client.CurrentLongitude);
+ return currentLoc.distanceFrom(new LatLong(lat, lng));
}
public async Task update(double lat, double lng)
{
- double waitTime = getDistance(lat, lng) / this.kilometersPerMillisecond;
+ double waitTime = getDistance(lat, lng) / this.metersPerMillisecond;
await Task.Delay((int)Math.Ceiling(waitTime));
await client.UpdatePlayerLocation(lat, lng);
}
}
- public class Coordinate : Tuple<double, double>, ICoordinate
- {
- public Coordinate(double item1, double item2) : base(item1, item2)
- {
- }
-
- public double Latitude
- {
- get
- {
- return this.Item1;
- }
- }
- public double Longitude
- {
- get
- {
- return this.Item2;
- }
- }
-
- public double distanceFrom(ICoordinate c2)
- {
- return new CoordinateMetric().distance(this, c2);
- }
-
-
- }
-
}
\ No newline at end of file
diff --git a/PokemonGo/RocketAPI/Window/MainForm.cs b/PokemonGo/RocketAPI/Window/MainForm.cs
index db448ce..b2c4c38 100644
--- a/PokemonGo/RocketAPI/Window/MainForm.cs
+++ b/PokemonGo/RocketAPI/Window/MainForm.cs
@@ -575,11 +575,15 @@ namespace PokemonGo.RocketAPI.Window
var mapObjects = await client.GetMapObjects();
FortData[] rawPokeStops = mapObjects.MapCells.SelectMany(i => i.Forts).Where(i => i.Type == FortType.Checkpoint && i.CooldownCompleteTimestampMs < DateTime.UtcNow.ToUnixTime()).ToArray();
- pokeStops = PokeStopOptimizer.Optimize(rawPokeStops, ClientSettings.DefaultLatitude, ClientSettings.DefaultLongitude, pokestopsOverlay);
+ pokeStops = rawPokeStops;
+ UpdateMap();
+ ColoredConsoleWrite(Color.Cyan, $"Finding fastest route through all PokeStops..");
+ LatLong startingLatLong = new LatLong(ClientSettings.DefaultLatitude, ClientSettings.DefaultLongitude);
+ pokeStops = RouteOptimizer<FortData>.Optimize(rawPokeStops, startingLatLong, pokestopsOverlay);
wildPokemons = mapObjects.MapCells.SelectMany(i => i.WildPokemons);
if (!ForceUnbanning && !Stopping)
ColoredConsoleWrite(Color.Cyan, $"Visiting {pokeStops.Count()} PokeStops");
- pokeStops = (IEnumerable<FortData>)TSP<ICoordinate>.getMinimumTour(pokeStops, (new CoordinateMetric()).distance);
+
UpdateMap();
foreach (var pokeStop in pokeStops)
{
diff --git a/PokemonGo/RocketAPI/Window/PokeStopOptimizer.cs b/PokemonGo/RocketAPI/Window/PokeStopOptimizer.cs
deleted file mode 100644
index a7768af..0000000
--- a/PokemonGo/RocketAPI/Window/PokeStopOptimizer.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-using GMap.NET.WindowsForms;
-using PokemonGo.RocketAPI.GeneratedCode;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace PokemonGo.RocketAPI.Window
-{
- public static class PokeStopOptimizer
- {
- public static List<FortData> Optimize(FortData[] pokeStops, double cLatitude, double cLongitude, GMapOverlay routeOverlay)
- {
- List<FortData> optimizedRoute = new List<FortData>(pokeStops);
-
- // NN
- FortData NN = FindNN(optimizedRoute, cLatitude, cLongitude);
- optimizedRoute.Remove(NN);
- optimizedRoute.Insert(0, NN);
- for (int i=1; i<pokeStops.Length; i++)
- {
- NN = FindNN(optimizedRoute.Skip(i), NN.Latitude, NN.Longitude);
- optimizedRoute.Remove(NN);
- optimizedRoute.Insert(i, NN);
- }
-
- // 2-Opt
-
-
- return optimizedRoute;
- }
-
- private static List<FortData> Optimize2Opt(List<FortData> pokeStops)
- {
- List<FortData> optimizedRoute = new List<FortData>();
-
- int n = pokeStops.Count;
-
- for (int ai = 0; ai < n; ai++)
- {
- for (int ci = 0; ci < n; ci++)
- {
- int bi = (ai + 1) % n;
- int di = (ci + 1) % n;
- }
- }
-
- return optimizedRoute;
- }
-
- private static FortData FindNN(IEnumerable<FortData> pokeStops, double cLatitude, double cLongitude)
- {
- return pokeStops.OrderBy(p => GetDistance(cLatitude, cLongitude, p.Latitude, p.Longitude)).First();
- }
-
- private static float GetDistance(double lat1, double lng1, double lat2, double lng2)
- {
- double R = 6371e3;
- Func<double, float> toRad = x => (float)(x * (Math.PI / 180));
- lat1 = toRad(lat1);
- lat2 = toRad(lat2);
- float dLng = toRad(lng2 - lng1);
-
- return (float)(Math.Acos(Math.Sin(lat1)*Math.Sin(lat2) + Math.Cos(lat1)*Math.Cos(lat2)*Math.Cos(dLng)) * R);
- }
- }
-}
diff --git a/PokemonGo/RocketAPI/Window/PokemonGo.RocketAPI.Window.csproj b/PokemonGo/RocketAPI/Window/PokemonGo.RocketAPI.Window.csproj
index e09572a..cb0252d 100644
--- a/PokemonGo/RocketAPI/Window/PokemonGo.RocketAPI.Window.csproj
+++ b/PokemonGo/RocketAPI/Window/PokemonGo.RocketAPI.Window.csproj
@@ -92,7 +92,7 @@
<Compile Include="PokemonForm.Designer.cs">
<DependentUpon>PokemonForm.cs</DependentUpon>
</Compile>
- <Compile Include="PokeStopOptimizer.cs" />
+ <Compile Include="RouteOptimizer.cs" />
<Compile Include="PokeUi.cs">
<SubType>Form</SubType>
</Compile>
diff --git a/PokemonGo/RocketAPI/Window/RouteOptimizer.cs b/PokemonGo/RocketAPI/Window/RouteOptimizer.cs
new file mode 100644
index 0000000..b6f1bd4
--- /dev/null
+++ b/PokemonGo/RocketAPI/Window/RouteOptimizer.cs
@@ -0,0 +1,93 @@
+using GMap.NET.WindowsForms;
+using PokemonGo.RocketAPI.GeneratedCode;
+using PokemonGo.RocketAPI.Extensions;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PokemonGo.RocketAPI.Window
+{
+ public static class RouteOptimizer<T> where T: ILatLong
+ {
+ public static List<T> Optimize(T[] stops, ILatLong startingPosition, GMapOverlay routeOverlay)
+ {
+ List<T> optimizedRoute = new List<T>(stops);
+
+ // NN
+ T NN = FindNN(optimizedRoute, startingPosition);
+ optimizedRoute.Remove(NN);
+ optimizedRoute.Insert(0, NN);
+ for (int i=1; i< stops.Length; i++)
+ {
+ NN = FindNN(optimizedRoute.Skip(i), NN);
+ optimizedRoute.Remove(NN);
+ optimizedRoute.Insert(i, NN);
+ }
+
+ // 2-Opt
+ optimizedRoute = Optimize2Opt(optimizedRoute);
+
+ return optimizedRoute;
+ }
+
+ public static float routeCost(List<T> stops)
+ {
+ return Enumerable.Range(0, stops.Count - 1).Aggregate<int, float>(0, (sum,i) =>
+ {
+ return sum + (float)stops[i].distanceFrom(stops[i + 1]);
+ });
+ }
+
+ private static List<T> reverseSublist(List<T> stops, int startIndex, int endIndex)
+ {
+ return stops
+ .Take(startIndex)
+ .Concat(
+ stops
+ .Skip(startIndex)
+ .Take(endIndex - startIndex)
+ .Reverse()
+ ).Concat(stops.Skip(endIndex)).ToList();
+ }
+
+ private static List<T> Optimize2Opt(List<T> stops)
+ {
+ List<T> optimizedRoute = stops;
+
+ int n = stops.Count;
+ bool foundCheaperRoute;
+ float minCost = routeCost(optimizedRoute);
+ do
+ {
+ foundCheaperRoute = false;
+ for (int i = 0; i < n - 1; i++)
+ {
+ for (int j = i + 1; j < n ; j++)
+ {
+ List<T> newRoute = reverseSublist(optimizedRoute, i, j);
+ float newCost = routeCost(newRoute);
+ if (newCost < minCost)
+ {
+ minCost = newCost;
+ optimizedRoute = newRoute;
+ foundCheaperRoute = true;
+ break;
+ }
+ }
+ if (foundCheaperRoute)
+ break;
+ }
+ }
+ while (foundCheaperRoute);
+ return optimizedRoute;
+ }
+
+ private static T FindNN(IEnumerable<T> stops, ILatLong fromPosition)
+ {
+ return stops.OrderBy(p => fromPosition.distanceFrom(p)).First();
+ }
+
+ }
+}
diff --git a/PokemonGo/RocketAPI/Window/TSP.cs b/PokemonGo/RocketAPI/Window/TSP.cs
deleted file mode 100644
index b47c966..0000000
--- a/PokemonGo/RocketAPI/Window/TSP.cs
+++ /dev/null
@@ -1,230 +0,0 @@
-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;
- if (nodes.Count() < 2)
- return nodes;
-
- //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.