From 59193deb22954fa8e86708be1b90c78282e829dc Mon Sep 17 00:00:00 2001 From: Anson Bridges Date: Fri, 15 Nov 2024 18:54:26 -0500 Subject: final setup for hunt --- dashboard_website/router.py | 140 +++++++++++++++++++++++++++++++++----------- 1 file changed, 107 insertions(+), 33 deletions(-) (limited to 'dashboard_website/router.py') diff --git a/dashboard_website/router.py b/dashboard_website/router.py index 70af4c6..385cbae 100644 --- a/dashboard_website/router.py +++ b/dashboard_website/router.py @@ -101,33 +101,37 @@ def cluster_and_optimize( # assign pre-determined clues to bikes already_assigned_clues = [] routes = [[] for i in bikes] - for clue in clues: - if clue.assigned_team != 0 and ((clue.assigned_team-1) in active_indices): - routes[clue.assigned_team - 1].append(clue) - already_assigned_clues.append(clue) - # if we are not doing a hard reset, keep the current target clues if minimal: for i, bike in enumerate(bikes): - if (bike.status != "DISABLED") and (bike.target_clue != None) and (bike.target_clue not in routes[i]): - routes[i].append(bike.target_clue) - already_assigned_clues.append(clue) + if (bike.status != "DISABLED") and (bike.target_clue != None): + #routes[i].append(bike.target_clue) + print("Already assigned clue at: ", bike.target_clue) + already_assigned_clues.append(bike.target_clue) + for clue in clues: + if clue.assigned_team != 0 and ((clue.assigned_team-1) in active_indices) and (clue not in already_assigned_clues): + routes[clue.assigned_team - 1].append(clue) + already_assigned_clues.append(clue) # Split the remaining clues into clusters based on the cluster labels for i, label in enumerate(kmeans.labels_): if clues[i].assigned_team == 0 and (clues[i] not in already_assigned_clues): routes[active_indices[label]].append(clues[i]) - routes = __minimize_route_time_diff(routes, bikes, end, minimal, time_diff) + routes, times = __minimize_route_time_diff(routes, bikes, end, minimal, time_diff) if routes == -1: return -1, [], [], [] # Remove waypoints from the longest route until the trip time is less than the max time - for i in range(len(routes)): - resp = __remove_longest_waypoints(routes[i], bikes[i], end, max(bikes[i].timeTilDeadline()/3600, max_max_time)) - if resp == -1: - return -1, [], [], [] - routes[i] = resp + #for i in range(len(routes)): + # resp = __remove_longest_waypoints(routes[i], bikes[i], end, max(max_max_time, bikes[i].timeTilDeadline()/3600), minimal) + # if resp == -1: + # return -1, [], [], [] + # routes[i] = resp + clue_pool = [] + routes = __balance_waypoints(routes, bikes, end, times, clue_pool, max_max_time, minimal) + if routes == -1: + return -1, [], [], [] # Get the json of the routes route_waypoints = [] @@ -151,8 +155,10 @@ def cluster_and_optimize( for i, route in enumerate(routes): route2 = ["" for x in route] start_index = 0 if (minimal and (bikes[i].target_clue != None)) else 1 # if starting route with first clue, first index of trip must be included - for j, k in enumerate(route_waypoints[i][start_index:-1]): + for j, k in enumerate(route_waypoints[i][1:-1]): route2[k["waypoint_index"] - 1] = route[j] + if (minimal and (bikes[i].target_clue != None)): + route2.insert(0, bikes[i].target_clue) routes[i] = route2 return status, routes, geometries, times @@ -184,7 +190,7 @@ def __get_json(coordinate_string, start, end): + start + coordinate_string + end - + "?roundtrip=false&source=first&destination=last&geometries=geojson&overview=full" + + "?roundtrip=false&source=first&destination=last&geometries=geojson&overview=full&exclude=ferry" ) coordinates = coordinates.json() @@ -208,7 +214,7 @@ def __get_trip_time( + start + coordinate_string + end - + "?roundtrip=false&source=first&destination=last" + + "?roundtrip=false&source=first&destination=last&exclude=ferry" ) coordinates = coordinates.json() @@ -266,9 +272,9 @@ def __minimize_route_time_diff( if time_difference > time_diff: # Move a coordinate from the longest route to the shortest route for _ in range(max(1,int(time_difference/0.12))): # roughly estimate how many points must be moved to equalize - closest_coordinate = __find_closest_coordinate( + closest_coordinate = __find_closest_coordinates( routes[active_indices[sorted_indices[-1]]], __mean_center(routes[sorted_indices[0]]) - ) + )[0] routes[active_indices[sorted_indices[0]]].append(closest_coordinate) routes[active_indices[sorted_indices[-1]]].remove(closest_coordinate) @@ -276,11 +282,67 @@ def __minimize_route_time_diff( return __minimize_route_time_diff(routes, bikes, end, minimal, time_diff) # If the difference of the longest trip time from average is less than the time difference, return the routes - return routes + return routes, times + +def __balance_waypoints( + routes: [Clue], bikes: [Bike], end: Point, times, clue_pool: [Clue], max_time, minimal +): + """ + Takes a list of coordinates, a start point, an end point, and a maximum time and returns a list of coordinates + :param route_coordinates: the list of coordinates + :param start: the start point + :param end: the end point + :param max_time: the maximum time + :return: a list of coordinates + """ + + if db.should_cancel_routing(): return -1 + active_indices = [i for i in range(len(bikes)) if bikes[i].status != "DISABLED"] + starts = [ bike.target_clue if (minimal and (bike.target_clue != None)) else bike for bike in bikes ] # all bikes regardless of enabled + max_times = [ max(bike.timeTilDeadline()/3600, max_time) for bike in bikes ] + time_balances = [ (max_times[i] - times[i]) for i in range(len(times))] + + balanced = True + for balance in time_balances: + if balance < 0: + balanced = False + break + if balanced: + return routes + # shorten too-long routes + for i, time_balance in enumerate(time_balances): + print(i, time_balance) + if time_balance < 0: # route too long + route_mean = __mean_center(routes[active_indices[i]]) + farthest_coordinates = __find_farthest_coordinates(routes[active_indices[i]], route_mean, max(1,min(int(-1*time_balance/0.12),int(len(routes[active_indices[i]])*0.75)))) + for farthest_coordinate in farthest_coordinates: + routes[active_indices[i]].remove(farthest_coordinate) + clue_pool.append(farthest_coordinate) + # lengthen routes with spare room + for i, time_balance in enumerate(time_balances): + if time_balance > 0.15: # route too short + route_mean = __mean_center(routes[active_indices[i]]) + closest_coordinates = __find_closest_coordinates(clue_pool, route_mean, 1) + for closest_coordinate in closest_coordinates: + routes[active_indices[i]].append(closest_coordinate) + clue_pool.remove(closest_coordinate) + + for i, route in enumerate(routes): + if bikes[i].status == "DISABLED": continue + if db.should_cancel_routing(): return -1 + times[active_indices[i]] = __get_trip_time( + __clues_to_string(route), + len(route), + __clues_to_string([starts[i]]), + __clues_to_string([end])[:-1], + ) * bikes[i].time_modifier + + return __balance_waypoints(routes, bikes, end, times, clue_pool, max_time, minimal) + def __remove_longest_waypoints( - route_coordinates: [Clue], start: Bike, end: Point, max_time, start_already_set=False + route_coordinates: [Clue], start: Bike, end: Point, max_time, minimal, start_already_set=False ): """ Takes a list of coordinates, a start point, an end point, and a maximum time and returns a list of coordinates @@ -297,7 +359,7 @@ def __remove_longest_waypoints( print(max_time) - if start.status != "DISABLED" and start.target_clue != None: # already assigned a clue + if minimal and (start.status != "DISABLED") and (start.target_clue != None): # already assigned a clue start = start.target_clue # Find the trip time for the route route_time = __get_trip_time( @@ -315,11 +377,13 @@ def __remove_longest_waypoints( for farthest_coordinate in farthest_coordinates: route_coordinates.remove(farthest_coordinate) - return __remove_longest_waypoints(route_coordinates, start, end, max_time, start_already_set=True) + return __remove_longest_waypoints(route_coordinates, start, end, max_time, minimal, start_already_set=True) return route_coordinates + + def __normalize_points(clues: [Clue], bikes: [Bike]): """ Takes a list of coordinates and a list of centroids and returns a list of normalized coordinates and a list of @@ -372,23 +436,34 @@ def __min_max_normalize(value, min_value, max_value): return (value - min_value) / (max_value - min_value) -def __find_closest_coordinate(clues: [Clue], centroid: Point): +def __find_closest_coordinates(clues: [Clue], centroid: Point, n:int=1): """ Takes a list of coordinates and a centroid and returns the clue in the list that is closest to the centroid :param clues: the list of coordinates :param centroid: the centroid :return: the clue in the list that is closest to the centroid """ + # Convert the clues to a list of points - closest_coordinate = clues[0] - closest_coordinate_distance = __distance(closest_coordinate, centroid) + closest_coordinates = [] + for _ in range(min(n, len(clues))): + closest_coordinate = clues[0] + # remove only unrequired clues + i = 1 + while closest_coordinate in closest_coordinates: + closest_coordinate = clues[i] + i += 1 - for clue in clues: - if __distance(clue, centroid) < closest_coordinate_distance: - closest_coordinate = clue - closest_coordinate_distance = __distance(clue, centroid) + closest_coordinate_distance = __distance(closest_coordinate, centroid) - return closest_coordinate + for clue in clues: + if __distance(clue, centroid) < closest_coordinate_distance and (clue not in closest_coordinates): + closest_coordinate = clue + closest_coordinate_distance = __distance(clue, centroid) + + closest_coordinates.append(closest_coordinate) + + return closest_coordinates def __find_farthest_coordinates(clues: [Clue], centroid: Point, n:int=1): @@ -400,8 +475,7 @@ def __find_farthest_coordinates(clues: [Clue], centroid: Point, n:int=1): """ farthest_coordinates = [] - - for _ in range(n): + for _ in range(min(len(clues),n)): farthest_coordinate = clues[0] # remove only unrequired clues i = 1 -- cgit v1.2.3