astray.to

the loop problem · 7 min read

The loop problem: ending where you started.

Some of the best drives go nowhere. You have an hour, a full tank, no errand, and you want a good loop that ends in your own driveway. It sounds like an easier request than a real trip. For a router, it is the harder one.

in plain words

A drive with no destination.

In astray you ask for a loop by making the start and the end the same place. The app notices (anything within about eleven meters counts as "the same place") and quietly changes the question. There is no longer a "how much extra time" slider, because there is no baseline trip to be extra to. Instead you say how long the whole tour should be: "about sixty minutes." That number becomes the entire budget.

What you want back is specific, even if you have never said it out loud: a tour that actually uses the hour, goes out on one road and comes back on a different one, and passes something worth seeing at the far end. What you do not want is a lap around your own block, or the same pretty road driven out and then driven straight back in reverse.

why it breaks

The fastest route is standing still.

Our point-to-point engine leans on a few load-bearing facts, and a loop knocks every one of them over. The fastest route from home to home takes zero seconds, because you are already there. That zero was the anchor for everything: the budget was defined as "fastest plus spare minutes," and the whole price search works by trading time against the fastest baseline. With a zero baseline there is nothing to trade against.

Worse, the answer that scores "technically correct" is garbage. A two-minute circuit around the neighborhood starts at home, ends at home, and stays under any budget you name. Nothing in the original objective says it is wrong. The hard part here is specification as much as search: the router has to be told what a good loop even means.

And there is a subtler failure underneath both: the engine could barely see any map. astray does not load a whole state to plan a trip; it loads the map tiles along a corridor between origin and destination. When origin and destination are the same point, that corridor collapses to almost nothing, and the engine wakes up inside a single tile with a few streets in it. The first loop routes were bad partly because the router was working nearly blind.

the map problem

Loading a trip that goes nowhere.

The map fix comes first, because nothing else works without it. For a loop, instead of tracing a corridor between two points, the engine samples a ring of sixteen points roughly 25 to 40 kilometers around home, about the reach of a 45-to-90-minute tour at free-flow speeds, and loads every tile the ring touches. That set is then grown outward a few hops along the tile adjacency graph, so sparse rural quads still reach a useful radius.

One detail matters more than it looks: this is a pure function from "home coordinate" to "list of tile names," and the exact same function runs on our server and inside the browser engine. Loops work identically offline on your phone and against the public API because there is only one implementation to get right.

the algorithm

Out on one road, back on another.

With a real map loaded, the engine builds tours the way you might with a paper atlas: pick a promising far point, drive out to it, come home a different way. The machinery is borrowed from the point-to-point engine's waypoint search, with four loop-specific changes.

  • Band the candidates by distance. A far point is only considered if its straight-line distance from home lands between 20% and 95% of half the tour budget. Closer than that and you get driveway circuits; farther and there is no way to make it home inside the hour. The straight-line distance is a lower bound on real driving time, so nothing feasible is ever excluded.
  • Pick far points where the scenery is. Surviving candidates are bucketed into small map cells, and each cell is scored by the time-weighted scenic value of its roads under your current mood. The best node from each of the top eight cells becomes a candidate turning point, twice as many as the point-to-point search uses, because loops need the variety.
  • Charge four-fold for going back the way you came. The outbound leg is a normal scenic search from home to the turning point. On the return search, every road that appeared on the outbound leg has its travel time multiplied by four, but only during the search. The route the search picks around those poisoned roads is then reported with honest times. This is a penalty, not a ban: on a peninsula with one road in and out, doubling back on the scenic road is still allowed to win.
  • Reject hairpins that survive anyway. If, despite the penalty, the outbound and return legs still share 55% or more of their underlying roads, the tour is discarded. That test uses stable road identities rather than directed edges, which is what catches "same road, opposite direction," the classic disguised U-turn.

Each surviving out-and-back pair becomes one candidate tour, capped at six per request, filtered to the time ceiling. The scenic search on each leg runs at a fixed moderate scenery price rather than sweeping the full price range, because a loop request needs breadth across turning points more than depth on any single one.

picking the winner

Spend the hour you asked for.

Selection is where the "two-minute circuit" failure gets closed for good. Among candidates inside the budget, the primary route maximizes total accumulated scenic value, the sum over the whole tour rather than the average. A short loop past one nice view cannot beat a full hour that keeps collecting. And one explicit rule backs that up: if any candidate spends at least 35% of the asked budget, shorter tours are excluded from winning outright. When you ask for an hour, an eight-minute answer is treated as the bug it is.

Alternatives work the same way as in the point-to-point engine: candidates that overlap the primary too heavily are dropped, so each option you see is a genuinely different loop rather than the same tour with a detour bolted on.

The scenery price, the negative-cost trap, and the way alternatives are kept distinct all live in the main routing article. The routing engine

The best way to evaluate a router is to drive it.

There is no account and no tracking. Open it, pick somewhere you've been meaning to go, and give it twenty spare minutes.