├── README.md ├── images └── simulation_1.gif └── route_simulation.py /README.md: -------------------------------------------------------------------------------- 1 | Multiple Vehicle Routing simulation based off a naive greedy algorithm. 2 | 3 | Don't use this or anything like this for your logistics company, you will lose all your money :) 4 | 5 | Red/Green Triangles: Vehicles 6 | 7 | Blue circles: Targets that have not yet been reached 8 | 9 | Pink circles: Targets that have already been reached 10 | 11 | 12 | ![alt tag](images/simulation_1.gif) 13 | -------------------------------------------------------------------------------- /images/simulation_1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbao/Multiple-Vehicle-Routing/71360c1bd6a43b9619e00f7d8a47071a983ea4ba/images/simulation_1.gif -------------------------------------------------------------------------------- /route_simulation.py: -------------------------------------------------------------------------------- 1 | import random 2 | from math import sqrt 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | 6 | class Truck(object): 7 | 8 | def __init__(self, name): 9 | self.name = name 10 | self.x = float(random.randint(0, 40)) 11 | self.y = float(random.randint(0, 40)) 12 | self.target = None 13 | 14 | def get_distance(self, target): 15 | """ 16 | Return distance between self and any target object 17 | """ 18 | x_squared = pow((self.x - target.x), 2) 19 | y_squared = pow((self.y - target.y), 2) 20 | 21 | return sqrt(x_squared + y_squared) 22 | 23 | def drive_to_target(self): 24 | """ 25 | Moves self closer to current target 26 | """ 27 | 28 | if self.target is None: 29 | return 30 | 31 | if self.get_distance(self.target) < .2: 32 | self.target.reached = True 33 | else: 34 | self.x += (self.target.x - self.x) * .2 35 | self.y += (self.target.y - self.y) * .2 36 | 37 | class Target(object): 38 | 39 | def __init__(self, reached=False): 40 | self.x = float(random.randint(0, 40)) 41 | self.y = float(random.randint(0, 40)) 42 | self.reached = reached 43 | 44 | class Dispatcher(object): 45 | """ 46 | Class responsible for moving trucks and tracking targets 47 | """ 48 | 49 | def __init__(self): 50 | self.trucks = [Truck("RED"), Truck("GREEN")] 51 | self.targets = list(set([Target() for i in xrange(20)])) 52 | self.job_complete = False 53 | 54 | def move_trucks(self): 55 | """ 56 | Brute force to find best targets for respective trucks 57 | """ 58 | 59 | # Check if all targets have been reached 60 | unreached_targets = [target for target in self.targets if target.reached is False] 61 | if len(unreached_targets) == 0: 62 | self.job_complete = True 63 | return 64 | 65 | # List of tuples: (truck object, target object, distance) 66 | truck_target_distance = [] 67 | 68 | for truck in self.trucks: 69 | for target in unreached_targets: 70 | truck_target_distance.append((truck, target, truck.get_distance(target))) 71 | 72 | # Sort by distance 73 | truck_target_distance.sort(key=lambda x: x[2]) 74 | 75 | next_moves = truck_target_distance[:1] 76 | 77 | for potential_move in truck_target_distance: 78 | if potential_move[0] != next_moves[0][0]: 79 | if potential_move[1] != next_moves[0][1]: 80 | next_moves.append(potential_move) 81 | break 82 | else: 83 | continue 84 | 85 | for move in next_moves: 86 | move[0].target = move[1] 87 | move[0].drive_to_target() 88 | 89 | class Plot(object): 90 | """ 91 | Class responsible for plotting the movement of Trucks and Targets 92 | """ 93 | 94 | def __init__(self, dispatch): 95 | """ 96 | Takes a Dispatcher object and plots its state. 97 | """ 98 | self.dispatch = dispatch 99 | 100 | # Initalize plot 101 | self.fig, self.ax = plt.subplots() 102 | self.ax.set_xlim(0, 40) 103 | self.ax.set_ylim(0, 40) 104 | 105 | # Trucks represented by points 106 | self.points_red, = self.ax.plot(self.dispatch.trucks[0].x, self.dispatch.trucks[0].y, color='red', marker='^', linestyle='None') 107 | 108 | self.points_green, = self.ax.plot(self.dispatch.trucks[1].x, self.dispatch.trucks[1].y, color='green', marker='^', linestyle='None') 109 | 110 | # Targets represented by points. 111 | targets_x_coordinates = [target.x for target in self.dispatch.targets] 112 | targets_y_coordinates = [target.y for target in self.dispatch.targets] 113 | self.points_targets_unreached, = self.ax.plot(targets_x_coordinates, targets_y_coordinates, color="blue", marker='o', linestyle='None') 114 | 115 | # No completed targets initially 116 | self.points_targets_reached, = self.ax.plot([], [], color="pink", marker='o', linestyle='None') 117 | 118 | def update(self): 119 | """ 120 | Updates plot as trucks move and targets are reached 121 | """ 122 | 123 | # Plot unreached targets 124 | targets_unreached_x_coordinates = [target.x for target in self.dispatch.targets if target.reached is False] 125 | targets_unreached_y_coordinates = [target.y for target in self.dispatch.targets if target.reached is False] 126 | self.points_targets_unreached.set_data(targets_unreached_x_coordinates, targets_unreached_y_coordinates) 127 | 128 | # Plot reached targets 129 | targets_reached_x_coordinates = [target.x for target in self.dispatch.targets if target.reached is True] 130 | targets_reached_y_coordinates = [target.y for target in self.dispatch.targets if target.reached is True] 131 | self.points_targets_reached.set_data(targets_reached_x_coordinates, targets_reached_y_coordinates) 132 | 133 | # Plot movement of trucks 134 | self.points_red.set_data(np.float(self.dispatch.trucks[0].x), np.float(self.dispatch.trucks[0].y)) 135 | self.points_green.set_data(np.float(self.dispatch.trucks[1].x), np.float(self.dispatch.trucks[1].y)) 136 | 137 | # Pause for capture animation 138 | plt.pause(0.01) 139 | 140 | def main(): 141 | """ 142 | 1. Creates an instance of the Dispatcher class. 143 | 2. Creates an instance of the Plot class. 144 | 3. Move trucks towards targets until all targets have been reached. 145 | """ 146 | 147 | random.seed(1) 148 | d = Dispatcher() 149 | p = Plot(d) 150 | 151 | while d.job_complete is False: 152 | d.move_trucks() 153 | p.update() 154 | 155 | main() 156 | --------------------------------------------------------------------------------