diff --git a/PokemonGo/RocketAPI/Client.cs b/PokemonGo/RocketAPI/Client.cs
index e682118..cc3dc8e 100644
--- a/PokemonGo/RocketAPI/Client.cs
+++ b/PokemonGo/RocketAPI/Client.cs
@@ -422,14 +422,20 @@ namespace PokemonGo.RocketAPI
releasePokemonRequest);
}
- public double getCurrentLat()
+ public double CurrentLatitude
{
- return this._currentLat;
+ get
+ {
+ return this._currentLat;
+ }
}
- public double getCurrentLong()
+ public double CurrentLongitude
{
- return this._currentLng;
+ get
+ {
+ return this._currentLng;
+ }
}
public async Task<PlayerUpdateResponse> UpdatePlayerLocation(double lat, double lng)
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/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 8760094..8a6404d 100644
--- a/PokemonGo/RocketAPI/PokemonGo.RocketAPI.csproj
+++ b/PokemonGo/RocketAPI/PokemonGo.RocketAPI.csproj
@@ -68,6 +68,8 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="Extensions\LatLongExtensions.cs" />
+ <Compile Include="ILatLong.cs" />
<Compile Include="Enums\AuthType.cs" />
<Compile Include="Enums\MiscEnums.cs" />
<Compile Include="Enums\RequestType.cs" />
@@ -92,6 +94,7 @@
<Compile Include="Extensions\HttpClientExtensions.cs" />
<Compile Include="Helpers\RandomHelper.cs" />
<Compile Include="Helpers\RequestBuilder.cs" />
+ <Compile Include="ProtoAdditions.cs" />
<Compile Include="Resources.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
diff --git a/PokemonGo/RocketAPI/ProtoAdditions.cs b/PokemonGo/RocketAPI/ProtoAdditions.cs
new file mode 100644
index 0000000..ea0ecf2
--- /dev/null
+++ b/PokemonGo/RocketAPI/ProtoAdditions.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PokemonGo.RocketAPI.GeneratedCode
+{
+ public partial class FortData : ILatLong { }
+
+ public partial class MapPokemon : ILatLong { }
+
+ public partial class WildPokemon : ILatLong { }
+
+ public partial class SpawnPoint : ILatLong { }
+
+}
diff --git a/PokemonGo/RocketAPI/Window/LocationManager.cs b/PokemonGo/RocketAPI/Window/LocationManager.cs
index 6896d41..6b6f3b5 100644
--- a/PokemonGo/RocketAPI/Window/LocationManager.cs
+++ b/PokemonGo/RocketAPI/Window/LocationManager.cs
@@ -3,59 +3,34 @@ using System.Collections.Generic;
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.getCurrentLat(), client.getCurrentLong());
- 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 struct Coordinate
- {
-
- public Coordinate(double lat, double lng)
- {
- this.latitude = lat;
- this.longitude = lng;
- }
- public double latitude;
- public double longitude;
-
- //returns distance in kilometers
- public double distanceFrom(Coordinate c2)
- {
- double R = 6371;
- Func<double, double> toRad = x => x * (Math.PI / 180);
- double dLat = toRad(c2.latitude - this.latitude);
- double dLong = toRad(c2.longitude - c2.longitude);
- double lat1 = toRad(this.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;
- }
- }
}
}
\ No newline at end of file
diff --git a/PokemonGo/RocketAPI/Window/MainForm.cs b/PokemonGo/RocketAPI/Window/MainForm.cs
index 2d1f167..96eeb1b 100644
--- a/PokemonGo/RocketAPI/Window/MainForm.cs
+++ b/PokemonGo/RocketAPI/Window/MainForm.cs
@@ -575,13 +575,16 @@ 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");
UpdateMap();
-
foreach (var pokeStop in pokeStops)
{
if (ForceUnbanning || Stopping)
diff --git a/PokemonGo/RocketAPI/Window/MapForm.cs b/PokemonGo/RocketAPI/Window/MapForm.cs
index 595a80b..cc49274 100644
--- a/PokemonGo/RocketAPI/Window/MapForm.cs
+++ b/PokemonGo/RocketAPI/Window/MapForm.cs
@@ -50,7 +50,7 @@ namespace PokemonGo.RocketAPI.Window
private void tmrUpdate_Tick(object sender, EventArgs e)
{
- gMapControl1.Position = new PointLatLng(Client.getCurrentLat(), Client.getCurrentLong());
+ gMapControl1.Position = new PointLatLng(Client.CurrentLatitude, Client.CurrentLongitude);
}
}
}
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 bfc3390..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>
@@ -109,6 +109,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/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();
+ }
+
+ }
+}
You may download the files in Public Git.