Skip to content
This repository has been archived by the owner on Jul 22, 2019. It is now read-only.

Implemented walking following streets with option to disable #599

Merged
merged 11 commits into from
Jul 28, 2016
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,12 @@ dependencies {

compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile "com.corundumstudio.socketio:netty-socketio:1.7.7"

compile group: 'org.json', name: 'json', version: '20160212'
compile group: 'org.slf4j', name: 'slf4j-nop', version: '1.7.21'

testCompile group: 'junit', name: 'junit', version: '4.11'

}
task fatJar(type: Jar) {
manifest {
Expand Down
3 changes: 3 additions & 0 deletions config.properties.template
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ random_next_pokestop_selection=5
# Meters per second
speed=2.8

# Should the bot follow the streets (true) or just go directly to pokestops/waypoints
follow_streets = false

# Should the bot loot the pokestops (true/false)
loot_pokestop=true

Expand Down
5 changes: 3 additions & 2 deletions src/main/kotlin/ink/abb/pogo/scraper/Settings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ import ink.abb.pogo.scraper.util.Log
import java.io.BufferedReader
import java.io.FileOutputStream
import java.io.FileReader
import java.util.Base64
import java.util.Properties
import java.util.*


class SettingsParser(val properties: Properties) {
Expand All @@ -39,6 +38,7 @@ class SettingsParser(val properties: Properties) {
},

speed = getPropertyIfSet("Speed", "speed", defaults.speed, String::toDouble),
shouldFollowStreets = getPropertyIfSet("Should the bot follow the streets (true) or just go directly to pokestops/waypoints", "follow_streets", defaults.shouldFollowStreets, String::toBoolean),
shouldDropItems = shouldDropItems,

uselessItems = if(shouldDropItems) mapOf(
Expand Down Expand Up @@ -144,6 +144,7 @@ data class Settings(
val startingLocation: S2LatLng = S2LatLng.fromDegrees(startingLatitude, startingLongitude),
val credentials: Credentials,
val speed: Double = 2.778,
val shouldFollowStreets: Boolean = false,
val shouldDropItems: Boolean = false,
val uselessItems: Map<ItemId, Int> = mapOf(
Pair(ItemId.ITEM_REVIVE, 20),
Expand Down
164 changes: 157 additions & 7 deletions src/main/kotlin/ink/abb/pogo/scraper/tasks/Walk.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import ink.abb.pogo.scraper.Context
import ink.abb.pogo.scraper.Settings
import ink.abb.pogo.scraper.Task
import ink.abb.pogo.scraper.util.Log
import ink.abb.pogo.scraper.util.map.canLoot;
import ink.abb.pogo.scraper.util.directions.getRouteCoordinates
import ink.abb.pogo.scraper.util.map.canLoot
import ink.abb.pogo.scraper.util.map.getCatchablePokemon

class Walk(val sortedPokestops: List<Pokestop>, val lootTimeouts: Map<String, Long>) : Task {
Expand All @@ -28,7 +29,12 @@ class Walk(val sortedPokestops: List<Pokestop>, val lootTimeouts: Map<String, Lo
val coordinates = ctx.server.coordinatesToGoTo.first()
ctx.server.coordinatesToGoTo.removeAt(0)
Log.normal("Walking to ${coordinates.latRadians()}, ${coordinates.lngRadians()}")
walk(bot, ctx, settings, S2LatLng.fromDegrees(coordinates.latRadians(), coordinates.lngRadians()), settings.speed, true)

if (settings.shouldFollowStreets) {
walkRoute(bot, ctx, settings, S2LatLng.fromDegrees(coordinates.latRadians(), coordinates.lngRadians()), settings.speed, true)
} else {
walk(bot, ctx, settings, S2LatLng.fromDegrees(coordinates.latRadians(), coordinates.lngRadians()), settings.speed, true)
}
} else {
val nearestUnused: List<Pokestop> = sortedPokestops.filter {
val canLoot = it.canLoot(ignoreDistance = true, lootTimeouts = lootTimeouts, api = ctx.api)
Expand All @@ -49,7 +55,11 @@ class Walk(val sortedPokestops: List<Pokestop>, val lootTimeouts: Map<String, Lo
if (settings.shouldDisplayPokestopName)
Log.normal("Walking to pokestop \"${chosenPokestop.details.name}\"")

walk(bot, ctx, settings, S2LatLng.fromDegrees(chosenPokestop.latitude, chosenPokestop.longitude), settings.speed, false)
if (settings.shouldFollowStreets) {
walkRoute(bot, ctx, settings, S2LatLng.fromDegrees(chosenPokestop.latitude, chosenPokestop.longitude), settings.speed, false)
} else {
walk(bot, ctx, settings, S2LatLng.fromDegrees(chosenPokestop.latitude, chosenPokestop.longitude), settings.speed, false)
}
}
}
}
Expand Down Expand Up @@ -78,20 +88,20 @@ class Walk(val sortedPokestops: List<Pokestop>, val lootTimeouts: Map<String, Lo
var walking = true
bot.runLoop(timeout, "WalkingLoop") { cancel ->
// don't run away when there are still Pokemon around
if(walking) {
if(bot.api.map.getCatchablePokemon(ctx.blacklistedEncounters).size > 0 && settings.shouldCatchPokemons) {
if (walking) {
if (bot.api.map.getCatchablePokemon(ctx.blacklistedEncounters).size > 0 && settings.shouldCatchPokemons) {
// Stop walking
walking = false
Log.normal("Pausing to catch pokemon...")
} // Else continue walking.
} else {
if(bot.api.map.getCatchablePokemon(ctx.blacklistedEncounters).size <= 0) {
if (bot.api.map.getCatchablePokemon(ctx.blacklistedEncounters).size <= 0) {
walking = true
Log.normal("Resuming walk.")
} // Else continue waiting.
}

if(!walking) {
if (!walking) {
return@runLoop
}

Expand All @@ -114,6 +124,146 @@ class Walk(val sortedPokestops: List<Pokestop>, val lootTimeouts: Map<String, Lo
}
}

fun walkRoute(bot: Bot, ctx: Context, settings: Settings, end: S2LatLng, speed: Double, sendDone: Boolean) {
if (speed.equals(0)) {
return
}
val timeout = 200L
val coordinatesList = getRouteCoordinates(S2LatLng.fromDegrees(ctx.lat.get(), ctx.lng.get()), end)
if (coordinatesList.size <= 0) {
walk(bot, ctx, settings, end, speed, sendDone)
} else {
var walking = true
bot.runLoop(timeout, "WalkingLoop") { cancel ->
if (walking) {
if (bot.api.map.getCatchablePokemon(ctx.blacklistedEncounters).size > 0 && settings.shouldCatchPokemons) {
// Stop walking
walking = false
Log.normal("Pausing to catch pokemon...")
} // Else continue walking.
} else {
if (bot.api.map.getCatchablePokemon(ctx.blacklistedEncounters).size <= 0) {
walking = true
Log.normal("Resuming walk.")
} // Else continue waiting.
}

if (!walking) {
return@runLoop
}


val start = S2LatLng.fromDegrees(ctx.lat.get(), ctx.lng.get())
val step = coordinatesList.first()
coordinatesList.removeAt(0)
val diff = step.sub(start)
val distance = start.getEarthDistance(step)
val timeRequired = distance / speed
val stepsRequired = timeRequired / (timeout.toDouble() / 1000.toDouble())
if (stepsRequired.equals(0)) {
cancel()
}
val deltaLat = diff.latDegrees() / stepsRequired
val deltaLng = diff.lngDegrees() / stepsRequired
var remainingSteps = stepsRequired
while (remainingSteps > 0) {
ctx.lat.addAndGet(deltaLat)
ctx.lng.addAndGet(deltaLng)
ctx.server.setLocation(ctx.lat.get(), ctx.lng.get())
remainingSteps--
Thread.sleep(timeout)
}

if (coordinatesList.size <= 0) {
if (ctx.lat.get()!= end.latDegrees() && ctx.lng.get() != end.lngDegrees()) {
ctx.walking.set(false)
walkAndComeBack(bot,ctx,settings,end,speed,sendDone)
cancel()
} else {
Log.normal("Destination reached.")
if (sendDone) {
ctx.server.sendGotoDone()
}
ctx.walking.set(false)
cancel()
}
}
}
}
}

fun walkAndComeBack(bot: Bot, ctx: Context, settings: Settings, end: S2LatLng, speed: Double, sendDone: Boolean) {
val start = S2LatLng.fromDegrees(ctx.lat.get(), ctx.lng.get())
val diff = end.sub(start)
val distance = start.getEarthDistance(end)
val timeout = 200L
// prevent division by 0
if (speed.equals(0)) {
return
}
val timeRequired = distance / speed
val stepsRequired = timeRequired / (timeout.toDouble() / 1000.toDouble())
// prevent division by 0
if (stepsRequired.equals(0)) {
return
}
val deltaLat = diff.latDegrees() / stepsRequired
val deltaLng = diff.lngDegrees() / stepsRequired
val deltaLat2 = -deltaLat
val deltaLng2 = -deltaLng

Log.normal("Walking to ${end.toStringDegrees()} in $stepsRequired steps.")
var remainingStepsGoing = stepsRequired
var remainingStepsComing = stepsRequired
var walking = true
bot.runLoop(timeout, "WalkingLoop") { cancel ->
// don't run away when there are still Pokemon around
if (walking) {
if (bot.api.map.getCatchablePokemon(ctx.blacklistedEncounters).size > 0 && settings.shouldCatchPokemons) {
// Stop walking
walking = false
Log.normal("Pausing to catch pokemon...")
} // Else continue walking.
} else {
if (bot.api.map.getCatchablePokemon(ctx.blacklistedEncounters).size <= 0) {
walking = true
Log.normal("Resuming walk.")
} // Else continue waiting.
}

if (!walking) {
return@runLoop
}

ctx.lat.addAndGet(deltaLat)
ctx.lng.addAndGet(deltaLng)

ctx.server.setLocation(ctx.lat.get(), ctx.lng.get())

remainingStepsGoing--

if (remainingStepsGoing <= 0) {
ctx.lat.addAndGet(deltaLat2)
ctx.lng.addAndGet(deltaLng2)

ctx.server.setLocation(ctx.lat.get(), ctx.lng.get())

remainingStepsComing--
}

if (remainingStepsComing <= 0) {
Log.normal("Destination reached.")

if (sendDone) {
ctx.server.sendGotoDone()
}

ctx.walking.set(false)
cancel()
}
}
}

private fun selectRandom(pokestops: List<Pokestop>, ctx: Context): Pokestop {
// Select random pokestop while taking the distance into account
// E.g. pokestop is closer to the user -> higher probabilty to be chosen
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package ink.abb.pogo.scraper.util.directions

import com.pokegoapi.google.common.geometry.S1Angle
import com.pokegoapi.google.common.geometry.S2LatLng
import org.json.JSONObject
import java.net.HttpURLConnection
import java.net.URL
import java.util.*
import java.util.regex.Pattern
import java.util.zip.GZIPInputStream

//var routeProvider = "http://yournavigation.org/api/dev/route.php"
var routeProvider = "http://router.project-osrm.org/viaroute"


fun getRoutefile(olat: Double, olng: Double, dlat: Double, dlng: Double): String {
val connection = URL(createURLString(olat, olng, dlat, dlng)).openConnection() as HttpURLConnection
connection.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
//connection.setRequestProperty("Accept-Encoding", "gzip")
connection.setRequestProperty("Accept-Language", "en")
connection.setRequestProperty("Cache-Control", "max=0")
connection.setRequestProperty("Connection", "keep-alive")
connection.setRequestProperty("DNT", "1")
connection.setRequestProperty("Host", "router.project-osrm.org")
connection.setRequestProperty("Upgrade-Insecure-Requests", "1")
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36")
var routeFile = String()
connection.inputStream.bufferedReader().lines().forEach {
routeFile += "$it\n"
}
return routeFile
}


fun createURLString(olat: Double, olng: Double, dlat: Double, dlng: Double): String {
//return "$routeProvider?flat=$olat&flon=$olng&tlat=$dlat&tlon=$dlng&v=foot&fast=1"
return "$routeProvider?loc=$olat,$olng&loc=$dlat,$dlng&compression=false"
}

fun getRouteCoordinates(olat: Double, olng: Double, dlat: Double, dlng: Double): ArrayList<S2LatLng> {
val routeJSONParsed = JSONObject(getRoutefile(olat, olng, dlat, dlng))
var coordinates = routeJSONParsed.get("route_geometry").toString()
coordinates = coordinates.replace("[", "")
coordinates = coordinates.replace("]", "")
val matcher = Pattern.compile("(|-)\\d+.(|-)\\d+,(|-)\\d+.(|-)\\d+").matcher(coordinates)
val coordinatesList = ArrayList<String>()
while (matcher.find()) {
coordinatesList.add(matcher.group())
}
val latlngList = ArrayList<S2LatLng>()
coordinatesList.forEach {
latlngList.add(S2LatLng(S1Angle.degrees(it.toString().split(",")[0].toDouble()), S1Angle.degrees(it.toString().split(",")[1].toDouble())))
}
return latlngList
}

fun getRouteCoordinates(start: S2LatLng, end: S2LatLng): ArrayList<S2LatLng> {
return getRouteCoordinates(start.latDegrees(),start.lngDegrees(),end.latDegrees(),end.lngDegrees())
}