The "Route Optimization" problem is a classic application of optimization algorithms, particularly the Traveling Salesman Problem (TSP). In our case, we’ll call it the D*pe-- (sorry), Werk Runner Problem (WRP). In this problem, a werk runner needs to find the shortest possible route that visits a set of blocks; and returns to the starting block. While the werk game is likely not running algorithms for its werk runners (or maybe they are?); on scheduled deliveries, securing the bag, and promptly returning that bag to the connect or plug is a part of the job description of the werk runner. This means route optimization is a necessity for the runner, the connect, and/or plug.
Algorithmic Approach
An algorithm for the werk game cannot be too global; specifically in the case of a werk runner. The runner needs to be able to make decisions that optimize his chance for success in the moment: considering long-term benefits beyond running the werk is not a concern for the werk runner.
The correct approach for our werk runner would be the "greedy approach". A "greedy approach" is a problem-solving strategy that makes the locally optimal choice at each step with the hope of finding a global optimum. In other words, it focuses on immediate gains and makes decisions that seem best at the current moment without considering the potential long-term consequences.
Coding the Optimization of Routes
The above class is defined with the Manhattan Distance formula in mind. The Manhattan distance, also known as the "taxicab distance" or "L1 distance," is a metric used to measure the distance between two points in a grid-based system, such as a city grid. It is named after the grid-like layout of streets in Manhattan, New York City, where traveling from one point to another often involves moving along city blocks at right angles.
The Manhattan distance between two points (x1, y1) and (x2, y2) in a 2D grid is calculated as follows:
Below we define a variable, 'streetBlocks', as an array taking the StreetBlock object with defined arguments.
Sliding Out with the Werk
The first thing our Werk runner needs to know is the distance of the routes.
Then the runner starts on his route to deliver the werk. He needs to stay on the most optimized route, and be mindful of manipulated routes(detours, street signs that point to the wrong street, etc) or he could risk not only being late for the delivery but also not arriving at all. This is where the optimized route algorithm comes into play.
Explanation of Algorithm
The code starts with an initial route, continuously finds the nearest unvisited street block, and adds it to the optimized route. Under a specific condition, it allows the route to be manipulated by switching the order of the first two blocks. The result is an optimized route that should minimize the overall distance traveled.const initialRoute = [...blocks];
: It creates a copy of the original blocks
array, ensuring that the original data remains unaltered. The initialRoute
array will be used to track the initial state of the street blocks.
const optimizedRoute = [initialRoute[0]];
: It initializes theoptimizedRoute
array with the first street block from theinitialRoute
. The optimization process starts from this block.let routeManipulated = false;
: This variable is used to track whether the route has been manipulated. It is initially set tofalse
.The code enters a
while
loop that continues until theoptimizedRoute
includes all the street blocks in theblocks
array. In other words, it continues until the optimized route is complete.Inside the loop, it initializes variables
shortestDistance
to a very large value (usingNumber.MAX_VALUE
) andnearestBlock
tonull
. These variables will be used to keep track of the nearest unvisited street block.It then iterates through the
initialRoute
to find the nearest unvisited street block. For each block, it calculates the distance from the last block in theoptimizedRoute
using the Manhattan distance formula.It checks if the current street block is closer than the previously found nearest street block. If so, it updates
shortestDistance
and setsnearestBlock
to the current block.After finding the nearest unvisited street block, it checks whether the route should be manipulated. This manipulation occurs only if
routeManipulated
isfalse
, and theoptimizedRoute
contains exactly two blocks. In this case, it switches the order of the first two blocks in theoptimizedRoute
. This is a unique condition for route manipulation.Finally, the nearest street block (either the originally found one or the manipulated one) is added to the
optimizedRoute
.Once the
while
loop is complete, the function returns theoptimizedRoute
, which represents the optimized order in which to visit the street blocks.
Returning with the Bag: the Metrics
When our runner gets back to the connect/plug to drop off the bag, the optimizeRoute and calculateTotalDistance methods can be used to observe the routes the runner took and get the distance traveled.
The console would print the order of the blocks the runner took in delivering the werk, and the distance traveled. Talking about next level of keeping of tabs on your werkers!