TroglodyteDerivations commited on
Commit
7ac91cb
·
verified ·
1 Parent(s): 7d8a5ab

Upload 18 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,18 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ Autonomous[[:space:]]Hot[[:space:]]Dog[[:space:]]Delivery[[:space:]]Visualizations/Screenshot[[:space:]]2025-10-29[[:space:]]at[[:space:]]12.18.05 PM.png filter=lfs diff=lfs merge=lfs -text
37
+ Autonomous[[:space:]]Hot[[:space:]]Dog[[:space:]]Delivery[[:space:]]Visualizations/Screenshot[[:space:]]2025-10-29[[:space:]]at[[:space:]]12.18.34 PM.png filter=lfs diff=lfs merge=lfs -text
38
+ Autonomous[[:space:]]Hot[[:space:]]Dog[[:space:]]Delivery[[:space:]]Visualizations/Screenshot[[:space:]]2025-10-29[[:space:]]at[[:space:]]12.19.18 PM.png filter=lfs diff=lfs merge=lfs -text
39
+ Autonomous[[:space:]]Hot[[:space:]]Dog[[:space:]]Delivery[[:space:]]Visualizations/Screenshot[[:space:]]2025-10-29[[:space:]]at[[:space:]]12.19.45 PM.png filter=lfs diff=lfs merge=lfs -text
40
+ Autonomous[[:space:]]Hot[[:space:]]Dog[[:space:]]Delivery[[:space:]]Visualizations/Screenshot[[:space:]]2025-10-29[[:space:]]at[[:space:]]12.20.07 PM.png filter=lfs diff=lfs merge=lfs -text
41
+ Autonomous[[:space:]]Hot[[:space:]]Dog[[:space:]]Delivery[[:space:]]Visualizations/Screenshot[[:space:]]2025-10-29[[:space:]]at[[:space:]]12.20.43 PM.png filter=lfs diff=lfs merge=lfs -text
42
+ Autonomous[[:space:]]Hot[[:space:]]Dog[[:space:]]Delivery[[:space:]]Visualizations/Screenshot[[:space:]]2025-10-29[[:space:]]at[[:space:]]12.21.07 PM.png filter=lfs diff=lfs merge=lfs -text
43
+ Autonomous[[:space:]]Hot[[:space:]]Dog[[:space:]]Delivery[[:space:]]Visualizations/Screenshot[[:space:]]2025-10-29[[:space:]]at[[:space:]]12.21.52 PM.png filter=lfs diff=lfs merge=lfs -text
44
+ Autonomous[[:space:]]Hot[[:space:]]Dog[[:space:]]Delivery[[:space:]]Visualizations/Screenshot[[:space:]]2025-10-29[[:space:]]at[[:space:]]12.22.08 PM.png filter=lfs diff=lfs merge=lfs -text
45
+ Autonomous[[:space:]]Hot[[:space:]]Dog[[:space:]]Delivery[[:space:]]Visualizations/Screenshot[[:space:]]2025-10-29[[:space:]]at[[:space:]]12.22.18 PM.png filter=lfs diff=lfs merge=lfs -text
46
+ Autonomous[[:space:]]Hot[[:space:]]Dog[[:space:]]Delivery[[:space:]]Visualizations/Screenshot[[:space:]]2025-10-29[[:space:]]at[[:space:]]12.22.53 PM.png filter=lfs diff=lfs merge=lfs -text
47
+ Autonomous[[:space:]]Hot[[:space:]]Dog[[:space:]]Delivery[[:space:]]Visualizations/Screenshot[[:space:]]2025-10-29[[:space:]]at[[:space:]]12.23.29 PM.png filter=lfs diff=lfs merge=lfs -text
48
+ Autonomous[[:space:]]Hot[[:space:]]Dog[[:space:]]Delivery[[:space:]]Visualizations/Screenshot[[:space:]]2025-10-29[[:space:]]at[[:space:]]12.23.46 PM.png filter=lfs diff=lfs merge=lfs -text
49
+ flux_krea_00470_.png filter=lfs diff=lfs merge=lfs -text
50
+ output.mp4 filter=lfs diff=lfs merge=lfs -text
Autonomous Hot Dog Delivery Visualizations/Screenshot 2025-10-29 at 12.18.05 PM.png ADDED

Git LFS Details

  • SHA256: 033a94c6e22521aff983729915148c5952c686d32f50a971312e655d859f24a3
  • Pointer size: 132 Bytes
  • Size of remote file: 1.53 MB
Autonomous Hot Dog Delivery Visualizations/Screenshot 2025-10-29 at 12.18.34 PM.png ADDED

Git LFS Details

  • SHA256: 0d252b98c0a5af5f9e99dabb372c6ad77bc1963041be3714994fa1f33acaf24e
  • Pointer size: 132 Bytes
  • Size of remote file: 1.72 MB
Autonomous Hot Dog Delivery Visualizations/Screenshot 2025-10-29 at 12.19.01/342/200/257PM.png ADDED
Autonomous Hot Dog Delivery Visualizations/Screenshot 2025-10-29 at 12.19.18 PM.png ADDED

Git LFS Details

  • SHA256: 47a16a9c4fb70c3d30f459a444f6e7249571681480c045d2b972b0c1db90c437
  • Pointer size: 131 Bytes
  • Size of remote file: 309 kB
Autonomous Hot Dog Delivery Visualizations/Screenshot 2025-10-29 at 12.19.45 PM.png ADDED

Git LFS Details

  • SHA256: 6b806e89154358c884d13f9bb975ae58c67699a9045d6b093b7a4df21b5c10c9
  • Pointer size: 132 Bytes
  • Size of remote file: 1.61 MB
Autonomous Hot Dog Delivery Visualizations/Screenshot 2025-10-29 at 12.20.07 PM.png ADDED

Git LFS Details

  • SHA256: 4dc207af96af64ffd5ac657c7752eb7ccc372327839b4590f8b3910ac87c9c66
  • Pointer size: 132 Bytes
  • Size of remote file: 1.72 MB
Autonomous Hot Dog Delivery Visualizations/Screenshot 2025-10-29 at 12.20.43 PM.png ADDED

Git LFS Details

  • SHA256: ef516d78a589ff8e8578704f05d1a338187ef5195e479187919e1493c213d39f
  • Pointer size: 131 Bytes
  • Size of remote file: 540 kB
Autonomous Hot Dog Delivery Visualizations/Screenshot 2025-10-29 at 12.21.07 PM.png ADDED

Git LFS Details

  • SHA256: 358ee5bf7618d60ea67953566eed6c3ba9f81f462959b54c84739bdf0bc41566
  • Pointer size: 132 Bytes
  • Size of remote file: 1.62 MB
Autonomous Hot Dog Delivery Visualizations/Screenshot 2025-10-29 at 12.21.52 PM.png ADDED

Git LFS Details

  • SHA256: 43da60da208bdc827de83c4de44a8617154b1aee6275a1885e43c3063e5e3db6
  • Pointer size: 132 Bytes
  • Size of remote file: 1.62 MB
Autonomous Hot Dog Delivery Visualizations/Screenshot 2025-10-29 at 12.22.08 PM.png ADDED

Git LFS Details

  • SHA256: c09a9ed0c33fb55d11d3511ca4b4d3a6650f5b044f4b275577396ee5f7dc4980
  • Pointer size: 132 Bytes
  • Size of remote file: 1.16 MB
Autonomous Hot Dog Delivery Visualizations/Screenshot 2025-10-29 at 12.22.18 PM.png ADDED

Git LFS Details

  • SHA256: 176a0a074a1b7bfa7ca5e5d4978b47887a0df5a3b3d7373bce4c4cb94e77b7ae
  • Pointer size: 132 Bytes
  • Size of remote file: 1.73 MB
Autonomous Hot Dog Delivery Visualizations/Screenshot 2025-10-29 at 12.22.53 PM.png ADDED

Git LFS Details

  • SHA256: 69cb46b96609c0cdc528654ef3125ba842fee889183ce985efe56e6ccd5631d3
  • Pointer size: 132 Bytes
  • Size of remote file: 1.61 MB
Autonomous Hot Dog Delivery Visualizations/Screenshot 2025-10-29 at 12.23.29 PM.png ADDED

Git LFS Details

  • SHA256: a0c19fbeb2e0be86f4638ba5c3af07cae71b7545e638d4cc0c1257bb71ecf1ef
  • Pointer size: 132 Bytes
  • Size of remote file: 1.72 MB
Autonomous Hot Dog Delivery Visualizations/Screenshot 2025-10-29 at 12.23.46 PM.png ADDED

Git LFS Details

  • SHA256: 4a4373ce2ab1101a537e78fb6961fbab77f9a935e3faf9350b9f32bba5d857e7
  • Pointer size: 132 Bytes
  • Size of remote file: 1.61 MB
app.py ADDED
@@ -0,0 +1,908 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import time
3
+ import random
4
+ import math
5
+ import io
6
+ import numpy as np
7
+ import matplotlib.pyplot as plt
8
+ from PyQt5.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout,
9
+ QWidget, QPushButton, QComboBox, QLabel, QTextEdit,
10
+ QTabWidget, QGroupBox, QMessageBox)
11
+ from PyQt5.QtCore import Qt, QTimer
12
+ from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
13
+ from matplotlib.figure import Figure
14
+
15
+ # Try to import QtWebEngine, but provide fallback
16
+ try:
17
+ from PyQt5.QtWebEngineWidgets import QWebEngineView
18
+ HAS_WEBENGINE = True
19
+ except ImportError:
20
+ HAS_WEBENGINE = False
21
+ print("QtWebEngine not available. Map display will be disabled.")
22
+ print("To enable maps, install: pip install PyQtWebEngine")
23
+
24
+ # Try to import folium, but provide fallback
25
+ try:
26
+ import folium
27
+ HAS_FOLIUM = True
28
+ except ImportError:
29
+ HAS_FOLIUM = False
30
+ print("Folium not available. Install with: pip install folium")
31
+
32
+ # Define the beach area in South Miami (approximate coordinates)
33
+ SOUTH_MIAMI_BEACH = (25.7617, -80.1918)
34
+ BEACH_AREA_BOUNDS = {
35
+ 'min_lat': 25.755,
36
+ 'max_lat': 25.777,
37
+ 'min_lon': -80.198,
38
+ 'max_lon': -80.180
39
+ }
40
+
41
+ class Node:
42
+ def __init__(self, position, parent=None):
43
+ self.position = position
44
+ self.parent = parent
45
+ self.g = 0
46
+ self.h = 0
47
+ self.f = 0
48
+
49
+ def __eq__(self, other):
50
+ return self.position == other.position
51
+
52
+ def __hash__(self):
53
+ return hash(self.position)
54
+
55
+ class AStarPathPlanner:
56
+ def __init__(self, grid_size=50):
57
+ self.grid_size = grid_size
58
+ self.obstacles = self.generate_obstacles()
59
+
60
+ def generate_obstacles(self):
61
+ """Generate obstacles with guaranteed free paths"""
62
+ obstacles = []
63
+ num_obstacles = random.randint(8, 12)
64
+
65
+ for _ in range(num_obstacles):
66
+ cluster_center_lat = random.uniform(BEACH_AREA_BOUNDS['min_lat'] + 0.002,
67
+ BEACH_AREA_BOUNDS['max_lat'] - 0.002)
68
+ cluster_center_lon = random.uniform(BEACH_AREA_BOUNDS['min_lon'] + 0.002,
69
+ BEACH_AREA_BOUNDS['max_lon'] - 0.002)
70
+
71
+ for _ in range(random.randint(2, 3)):
72
+ lat = cluster_center_lat + random.uniform(-0.001, 0.001)
73
+ lon = cluster_center_lon + random.uniform(-0.001, 0.001)
74
+
75
+ lat = max(BEACH_AREA_BOUNDS['min_lat'], min(BEACH_AREA_BOUNDS['max_lat'], lat))
76
+ lon = max(BEACH_AREA_BOUNDS['min_lon'], min(BEACH_AREA_BOUNDS['max_lon'], lon))
77
+
78
+ obstacles.append((lat, lon))
79
+
80
+ return obstacles
81
+
82
+ def is_on_land(self, position):
83
+ lat, lon = position
84
+ return (BEACH_AREA_BOUNDS['min_lat'] <= lat <= BEACH_AREA_BOUNDS['max_lat'] and
85
+ BEACH_AREA_BOUNDS['min_lon'] <= lon <= BEACH_AREA_BOUNDS['max_lon'])
86
+
87
+ def heuristic(self, a, b, heuristic_type='manhattan'):
88
+ lat1, lon1 = a
89
+ lat2, lon2 = b
90
+
91
+ lat_dist = abs(lat1 - lat2) * 111000
92
+ lon_dist = abs(lon1 - lon2) * 111000 * math.cos(math.radians((lat1 + lat2) / 2))
93
+
94
+ if heuristic_type == 'manhattan':
95
+ return lat_dist + lon_dist
96
+ elif heuristic_type == 'euclidean':
97
+ return math.sqrt(lat_dist**2 + lon_dist**2)
98
+ elif heuristic_type == 'chebyshev':
99
+ return max(lat_dist, lon_dist)
100
+ else:
101
+ return 0
102
+
103
+ def is_valid_position(self, position):
104
+ lat, lon = position
105
+
106
+ if not self.is_on_land(position):
107
+ return False
108
+
109
+ for obstacle in self.obstacles:
110
+ obstacle_dist = self.heuristic(position, obstacle, 'euclidean')
111
+ if obstacle_dist < 50:
112
+ return False
113
+
114
+ return True
115
+
116
+ def get_neighbors(self, position):
117
+ lat, lon = position
118
+ neighbors = []
119
+
120
+ base_step = 0.0003
121
+ directions = [
122
+ (-base_step, 0), (base_step, 0), (0, -base_step), (0, base_step),
123
+ (-base_step, -base_step), (-base_step, base_step),
124
+ (base_step, -base_step), (base_step, base_step)
125
+ ]
126
+
127
+ for dlat, dlon in directions:
128
+ new_lat = lat + dlat
129
+ new_lon = lon + dlon
130
+ new_position = (new_lat, new_lon)
131
+
132
+ if self.is_valid_position(new_position):
133
+ neighbors.append(new_position)
134
+
135
+ return neighbors
136
+
137
+ def a_star(self, start, goal, heuristic_type='manhattan'):
138
+ start_time = time.time()
139
+
140
+ start_node = Node(start)
141
+ goal_node = Node(goal)
142
+
143
+ open_set = set([start_node])
144
+ open_list = [start_node]
145
+ closed_set = set()
146
+
147
+ nodes_expanded = 0
148
+ max_nodes = 2000
149
+
150
+ while open_list and nodes_expanded < max_nodes:
151
+ current_node = min(open_list, key=lambda x: x.f)
152
+ open_list.remove(current_node)
153
+ open_set.remove(current_node)
154
+ closed_set.add(current_node)
155
+
156
+ nodes_expanded += 1
157
+
158
+ current_dist = self.heuristic(current_node.position, goal, 'euclidean')
159
+ if current_dist < 100:
160
+ path = []
161
+ current = current_node
162
+ while current is not None:
163
+ path.append(current.position)
164
+ current = current.parent
165
+
166
+ computation_time = time.time() - start_time
167
+ return path[::-1], nodes_expanded, computation_time, len(closed_set)
168
+
169
+ for neighbor_pos in self.get_neighbors(current_node.position):
170
+ neighbor_node = Node(neighbor_pos, current_node)
171
+
172
+ if neighbor_node in closed_set:
173
+ continue
174
+
175
+ move_cost = self.heuristic(current_node.position, neighbor_pos, heuristic_type)
176
+ neighbor_node.g = current_node.g + move_cost
177
+ neighbor_node.h = self.heuristic(neighbor_pos, goal, heuristic_type)
178
+ neighbor_node.f = neighbor_node.g + neighbor_node.h
179
+
180
+ if neighbor_node in open_set:
181
+ existing_node = next((n for n in open_list if n == neighbor_node), None)
182
+ if existing_node and neighbor_node.g < existing_node.g:
183
+ existing_node.g = neighbor_node.g
184
+ existing_node.f = neighbor_node.f
185
+ existing_node.parent = current_node
186
+ else:
187
+ open_set.add(neighbor_node)
188
+ open_list.append(neighbor_node)
189
+
190
+ computation_time = time.time() - start_time
191
+ print(f"A* failed: {nodes_expanded} nodes expanded, no path found")
192
+ return None, nodes_expanded, computation_time, len(closed_set)
193
+
194
+ class DeliveryBot:
195
+ def __init__(self):
196
+ self.orders = []
197
+ self.path_planner = AStarPathPlanner()
198
+ self.current_path = []
199
+ self.current_path_index = 0
200
+ self.current_position = SOUTH_MIAMI_BEACH
201
+ self.is_moving = False
202
+ self.current_delivery_order = None
203
+ self.generate_initial_orders()
204
+
205
+ def generate_initial_orders(self):
206
+ num_orders = random.randint(5, 8)
207
+
208
+ for i in range(num_orders):
209
+ attempts = 0
210
+ while attempts < 50:
211
+ lat = random.uniform(BEACH_AREA_BOUNDS['min_lat'] + 0.001,
212
+ BEACH_AREA_BOUNDS['max_lat'] - 0.001)
213
+ lon = random.uniform(BEACH_AREA_BOUNDS['min_lon'] + 0.001,
214
+ BEACH_AREA_BOUNDS['max_lon'] - 0.001)
215
+
216
+ if self.path_planner.is_valid_position((lat, lon)):
217
+ self.orders.append({
218
+ 'id': i,
219
+ 'position': (lat, lon),
220
+ 'status': 'waiting',
221
+ 'order_time': time.time() - random.uniform(0, 3600)
222
+ })
223
+ break
224
+ attempts += 1
225
+
226
+ if attempts >= 50:
227
+ fallback_pos = (
228
+ random.uniform(BEACH_AREA_BOUNDS['min_lat'] + 0.005,
229
+ BEACH_AREA_BOUNDS['max_lat'] - 0.005),
230
+ random.uniform(BEACH_AREA_BOUNDS['min_lon'] + 0.005,
231
+ BEACH_AREA_BOUNDS['max_lon'] - 0.005)
232
+ )
233
+ self.orders.append({
234
+ 'id': i,
235
+ 'position': fallback_pos,
236
+ 'status': 'waiting',
237
+ 'order_time': time.time() - random.uniform(0, 3600)
238
+ })
239
+
240
+ if len(self.orders) > 0:
241
+ self.orders[0]['status'] = 'delivered'
242
+ if len(self.orders) > 1:
243
+ self.orders[1]['status'] = 'delivered'
244
+
245
+ def update_order_status(self, order_id, status):
246
+ for order in self.orders:
247
+ if order['id'] == order_id:
248
+ order['status'] = status
249
+ break
250
+
251
+ def get_orders_by_status(self, status):
252
+ return [order for order in self.orders if order['status'] == status]
253
+
254
+ def get_next_waiting_order(self):
255
+ waiting_orders = self.get_orders_by_status('waiting')
256
+ if waiting_orders:
257
+ return waiting_orders[0]
258
+ return None
259
+
260
+ def start_delivery(self, order, path):
261
+ if not path or len(path) < 2:
262
+ return False
263
+
264
+ self.current_delivery_order = order
265
+ self.current_path = path
266
+ self.current_path_index = 0
267
+ self.is_moving = True
268
+ self.current_position = path[0]
269
+ self.update_order_status(order['id'], 'in_progress')
270
+ return True
271
+
272
+ def move_to_next_position(self):
273
+ """Move to next position in path, return True if still moving, False if complete"""
274
+ if not self.is_moving or self.current_path_index >= len(self.current_path):
275
+ # Delivery complete
276
+ if self.current_delivery_order:
277
+ self.update_order_status(self.current_delivery_order['id'], 'delivered')
278
+ completed_order = self.current_delivery_order
279
+ self.current_delivery_order = None
280
+ self.is_moving = False
281
+ return False, completed_order
282
+ return False, None
283
+
284
+ self.current_position = self.current_path[self.current_path_index]
285
+ self.current_path_index += 1
286
+
287
+ # Check if this is the final position
288
+ if self.current_path_index >= len(self.current_path):
289
+ # Final position reached
290
+ if self.current_delivery_order:
291
+ self.update_order_status(self.current_delivery_order['id'], 'delivered')
292
+ completed_order = self.current_delivery_order
293
+ self.current_delivery_order = None
294
+ self.is_moving = False
295
+ return False, completed_order
296
+
297
+ return True, None
298
+
299
+ def get_bot_position(self):
300
+ return self.current_position
301
+
302
+ class MplCanvas(FigureCanvas):
303
+ def __init__(self, parent=None, width=5, height=4, dpi=100):
304
+ fig = Figure(figsize=(width, height), dpi=dpi)
305
+ self.axes = fig.add_subplot(111)
306
+ super().__init__(fig)
307
+
308
+ class MapWidget(QWidget):
309
+ def __init__(self, delivery_bot, main_window):
310
+ super().__init__()
311
+ self.delivery_bot = delivery_bot
312
+ self.main_window = main_window
313
+ self.layout = QVBoxLayout()
314
+ self.setLayout(self.layout)
315
+
316
+ if HAS_WEBENGINE and HAS_FOLIUM:
317
+ self.map_view = QWebEngineView()
318
+ self.layout.addWidget(self.map_view)
319
+ else:
320
+ self.fallback_label = QLabel("Map display not available.\n\n"
321
+ "To enable maps, install:\n"
322
+ "pip install PyQtWebEngine folium")
323
+ self.fallback_label.setAlignment(Qt.AlignCenter)
324
+ self.fallback_label.setStyleSheet("font-size: 14px; color: gray;")
325
+ self.layout.addWidget(self.fallback_label)
326
+
327
+ def update_map(self, show_traversal=False):
328
+ if not HAS_WEBENGINE or not HAS_FOLIUM:
329
+ return
330
+
331
+ m = folium.Map(location=SOUTH_MIAMI_BEACH, zoom_start=15)
332
+
333
+ folium.Rectangle(
334
+ bounds=[(BEACH_AREA_BOUNDS['min_lat'], BEACH_AREA_BOUNDS['min_lon']),
335
+ (BEACH_AREA_BOUNDS['max_lat'], BEACH_AREA_BOUNDS['max_lon'])],
336
+ color='green',
337
+ fill=True,
338
+ fill_color='lightgreen',
339
+ fill_opacity=0.2,
340
+ popup="Delivery Area"
341
+ ).add_to(m)
342
+
343
+ for obstacle in self.delivery_bot.path_planner.obstacles:
344
+ folium.CircleMarker(
345
+ location=obstacle,
346
+ radius=8,
347
+ color='black',
348
+ fill=True,
349
+ fill_color='black',
350
+ fill_opacity=0.7,
351
+ popup="Obstacle"
352
+ ).add_to(m)
353
+
354
+ bot_position = self.delivery_bot.get_bot_position()
355
+ folium.Marker(
356
+ bot_position,
357
+ popup=f"Delivery Bot - {self.delivery_bot.current_delivery_order['id'] if self.delivery_bot.current_delivery_order else 'IDLE'}",
358
+ icon=folium.Icon(color='purple', icon='truck', prefix='fa')
359
+ ).add_to(m)
360
+
361
+ for order in self.delivery_bot.orders:
362
+ color = 'blue'
363
+ icon = 'cutlery'
364
+ if order['status'] == 'in_progress':
365
+ color = 'orange'
366
+ icon = 'hourglass-half'
367
+ elif order['status'] == 'delivered':
368
+ color = 'red'
369
+ icon = 'check'
370
+
371
+ folium.Marker(
372
+ location=order['position'],
373
+ popup=f"Order #{order['id']} - {order['status'].replace('_', ' ').title()}",
374
+ icon=folium.Icon(color=color, icon=icon, prefix='fa')
375
+ ).add_to(m)
376
+
377
+ if self.delivery_bot.current_path and len(self.delivery_bot.current_path) > 1:
378
+ if self.delivery_bot.current_path_index > 0:
379
+ completed_path = self.delivery_bot.current_path[:self.delivery_bot.current_path_index]
380
+ if len(completed_path) > 1:
381
+ folium.PolyLine(
382
+ completed_path,
383
+ color='green',
384
+ weight=4,
385
+ opacity=0.8,
386
+ popup="Completed Path"
387
+ ).add_to(m)
388
+
389
+ remaining_path = self.delivery_bot.current_path[self.delivery_bot.current_path_index:]
390
+ if len(remaining_path) > 1:
391
+ folium.PolyLine(
392
+ remaining_path,
393
+ color='purple',
394
+ weight=4,
395
+ opacity=0.6,
396
+ popup="Remaining Path"
397
+ ).add_to(m)
398
+
399
+ folium.Marker(
400
+ self.delivery_bot.current_path[0],
401
+ popup="Start Position",
402
+ icon=folium.Icon(color='green', icon='play', prefix='fa')
403
+ ).add_to(m)
404
+
405
+ folium.Marker(
406
+ self.delivery_bot.current_path[-1],
407
+ popup="Delivery Destination",
408
+ icon=folium.Icon(color='darkred', icon='flag-checkered', prefix='fa')
409
+ ).add_to(m)
410
+
411
+ data = io.BytesIO()
412
+ m.save(data, close_file=False)
413
+ self.map_view.setHtml(data.getvalue().decode())
414
+
415
+ class MainWindow(QMainWindow):
416
+ def __init__(self):
417
+ super().__init__()
418
+ self.delivery_bot = DeliveryBot()
419
+ self.current_heuristic = 'euclidean'
420
+ self.analysis_data = {
421
+ 'manhattan': {'nodes_expanded': [], 'computation_time': [], 'path_length': []},
422
+ 'euclidean': {'nodes_expanded': [], 'computation_time': [], 'path_length': []},
423
+ 'chebyshev': {'nodes_expanded': [], 'computation_time': [], 'path_length': []}
424
+ }
425
+
426
+ self.animation_timer = QTimer()
427
+ self.animation_timer.timeout.connect(self.animate_traversal)
428
+ self.animation_speed = 500
429
+
430
+ self.init_ui()
431
+ self.update_map()
432
+ self.update_analysis()
433
+
434
+ def init_ui(self):
435
+ self.setWindowTitle("Autonomous Hot Dog Delivery Bot - South Miami Beach")
436
+ self.setGeometry(100, 100, 1400, 900)
437
+
438
+ central_widget = QWidget()
439
+ self.setCentralWidget(central_widget)
440
+ main_layout = QHBoxLayout(central_widget)
441
+
442
+ left_panel = QVBoxLayout()
443
+ left_panel.setContentsMargins(10, 10, 10, 10)
444
+
445
+ controls_group = QGroupBox("Path Planning Controls")
446
+ controls_layout = QVBoxLayout()
447
+
448
+ heuristic_layout = QHBoxLayout()
449
+ heuristic_layout.addWidget(QLabel("Heuristic:"))
450
+ self.heuristic_combo = QComboBox()
451
+ self.heuristic_combo.addItems(["Manhattan", "Euclidean", "Chebyshev"])
452
+ self.heuristic_combo.setCurrentText("Euclidean")
453
+ self.heuristic_combo.currentTextChanged.connect(self.on_heuristic_changed)
454
+ heuristic_layout.addWidget(self.heuristic_combo)
455
+ controls_layout.addLayout(heuristic_layout)
456
+
457
+ speed_layout = QHBoxLayout()
458
+ speed_layout.addWidget(QLabel("Animation Speed:"))
459
+ self.speed_combo = QComboBox()
460
+ self.speed_combo.addItems(["Slow", "Medium", "Fast"])
461
+ self.speed_combo.setCurrentText("Medium")
462
+ self.speed_combo.currentTextChanged.connect(self.on_speed_changed)
463
+ speed_layout.addWidget(self.speed_combo)
464
+ controls_layout.addLayout(speed_layout)
465
+
466
+ self.plan_path_btn = QPushButton("Plan New Delivery Path")
467
+ self.plan_path_btn.clicked.connect(self.plan_path)
468
+ controls_layout.addWidget(self.plan_path_btn)
469
+
470
+ self.start_traversal_btn = QPushButton("Start Traversal")
471
+ self.start_traversal_btn.clicked.connect(self.start_traversal)
472
+ self.start_traversal_btn.setEnabled(False)
473
+ controls_layout.addWidget(self.start_traversal_btn)
474
+
475
+ self.simulate_delivery_btn = QPushButton("Complete Delivery")
476
+ self.simulate_delivery_btn.clicked.connect(self.complete_delivery)
477
+ controls_layout.addWidget(self.simulate_delivery_btn)
478
+
479
+ self.run_comparison_btn = QPushButton("Run Heuristic Comparison")
480
+ self.run_comparison_btn.clicked.connect(self.run_heuristic_comparison)
481
+ controls_layout.addWidget(self.run_comparison_btn)
482
+
483
+ controls_group.setLayout(controls_layout)
484
+ left_panel.addWidget(controls_group)
485
+
486
+ status_group = QGroupBox("Bot Status")
487
+ status_layout = QVBoxLayout()
488
+ self.bot_status_label = QLabel("Status: Ready for delivery")
489
+ self.bot_status_label.setStyleSheet("font-weight: bold; color: blue;")
490
+ status_layout.addWidget(self.bot_status_label)
491
+ status_group.setLayout(status_layout)
492
+ left_panel.addWidget(status_group)
493
+
494
+ status_group = QGroupBox("Delivery Status")
495
+ status_layout = QVBoxLayout()
496
+
497
+ self.status_text = QTextEdit()
498
+ self.status_text.setReadOnly(True)
499
+ status_layout.addWidget(self.status_text)
500
+
501
+ status_group.setLayout(status_layout)
502
+ left_panel.addWidget(status_group)
503
+
504
+ analysis_tabs = QTabWidget()
505
+
506
+ efficiency_tab = QWidget()
507
+ efficiency_layout = QVBoxLayout()
508
+ self.efficiency_canvas = MplCanvas(self, width=5, height=4, dpi=100)
509
+ efficiency_layout.addWidget(self.efficiency_canvas)
510
+ efficiency_tab.setLayout(efficiency_layout)
511
+ analysis_tabs.addTab(efficiency_tab, "Search Efficiency")
512
+
513
+ performance_tab = QWidget()
514
+ performance_layout = QVBoxLayout()
515
+ self.performance_canvas = MplCanvas(self, width=5, height=4, dpi=100)
516
+ performance_layout.addWidget(self.performance_canvas)
517
+ performance_tab.setLayout(performance_layout)
518
+ analysis_tabs.addTab(performance_tab, "Computation Performance")
519
+
520
+ optimality_tab = QWidget()
521
+ optimality_layout = QVBoxLayout()
522
+ self.optimality_canvas = MplCanvas(self, width=5, height=4, dpi=100)
523
+ optimality_layout.addWidget(self.optimality_canvas)
524
+ optimality_tab.setLayout(optimality_layout)
525
+ analysis_tabs.addTab(optimality_tab, "Path Optimality")
526
+
527
+ comparison_tab = QWidget()
528
+ comparison_layout = QVBoxLayout()
529
+ self.comparison_text = QTextEdit()
530
+ self.comparison_text.setReadOnly(True)
531
+ comparison_layout.addWidget(self.comparison_text)
532
+ comparison_tab.setLayout(comparison_layout)
533
+ analysis_tabs.addTab(comparison_tab, "Comparison Results")
534
+
535
+ left_panel.addWidget(analysis_tabs)
536
+
537
+ right_panel = QVBoxLayout()
538
+
539
+ map_group = QGroupBox("South Miami Beach Delivery Map - Real-time Traversal")
540
+ map_layout = QVBoxLayout()
541
+
542
+ self.map_widget = MapWidget(self.delivery_bot, self)
543
+ map_layout.addWidget(self.map_widget)
544
+
545
+ legend_layout = QHBoxLayout()
546
+ legend_layout.addWidget(QLabel("Legend:"))
547
+
548
+ purple_label = QLabel("● Purple: Delivery Bot")
549
+ purple_label.setStyleSheet("color: purple; font-weight: bold;")
550
+ legend_layout.addWidget(purple_label)
551
+
552
+ red_label = QLabel("● Red: Delivered")
553
+ red_label.setStyleSheet("color: red; font-weight: bold;")
554
+ legend_layout.addWidget(red_label)
555
+
556
+ green_label = QLabel("● Green: Start/Completed Path")
557
+ green_label.setStyleSheet("color: green; font-weight: bold;")
558
+ legend_layout.addWidget(green_label)
559
+
560
+ blue_label = QLabel("● Blue: Waiting for Delivery")
561
+ blue_label.setStyleSheet("color: blue; font-weight: bold;")
562
+ legend_layout.addWidget(blue_label)
563
+
564
+ orange_label = QLabel("● Orange: In Progress")
565
+ orange_label.setStyleSheet("color: orange; font-weight: bold;")
566
+ legend_layout.addWidget(orange_label)
567
+
568
+ legend_layout.addStretch()
569
+ map_layout.addLayout(legend_layout)
570
+
571
+ map_group.setLayout(map_layout)
572
+ right_panel.addWidget(map_group)
573
+
574
+ main_layout.addLayout(left_panel, 1)
575
+ main_layout.addLayout(right_panel, 2)
576
+
577
+ self.update_status()
578
+
579
+ def on_heuristic_changed(self, text):
580
+ self.current_heuristic = text.lower()
581
+
582
+ def on_speed_changed(self, text):
583
+ speeds = {"Slow": 1000, "Medium": 500, "Fast": 200}
584
+ self.animation_speed = speeds.get(text, 500)
585
+ if self.animation_timer.isActive():
586
+ self.animation_timer.stop()
587
+ self.animation_timer.start(self.animation_speed)
588
+
589
+ def update_status(self):
590
+ status_text = "=== DELIVERY ORDERS ===\n\n"
591
+
592
+ waiting_orders = self.delivery_bot.get_orders_by_status('waiting')
593
+ in_progress_orders = self.delivery_bot.get_orders_by_status('in_progress')
594
+ delivered_orders = self.delivery_bot.get_orders_by_status('delivered')
595
+
596
+ status_text += f"WAITING FOR DELIVERY ({len(waiting_orders)}):\n"
597
+ for order in waiting_orders:
598
+ status_text += f" Order #{order['id']}: Lat {order['position'][0]:.4f}, Lon {order['position'][1]:.4f}\n"
599
+
600
+ status_text += f"\nIN PROGRESS ({len(in_progress_orders)}):\n"
601
+ for order in in_progress_orders:
602
+ status_text += f" Order #{order['id']}: Lat {order['position'][0]:.4f}, Lon {order['position'][1]:.4f}\n"
603
+
604
+ status_text += f"\nDELIVERED ({len(delivered_orders)}):\n"
605
+ for order in delivered_orders:
606
+ status_text += f" Order #{order['id']}: Lat {order['position'][0]:.4f}, Lon {order['position'][1]:.4f}\n"
607
+
608
+ self.status_text.setText(status_text)
609
+
610
+ def update_map(self):
611
+ self.map_widget.update_map()
612
+
613
+ def plan_path(self):
614
+ if self.animation_timer.isActive():
615
+ self.animation_timer.stop()
616
+ self.delivery_bot.is_moving = False
617
+
618
+ target_order = self.delivery_bot.get_next_waiting_order()
619
+ if not target_order:
620
+ QMessageBox.information(self, "No Orders", "No waiting orders to deliver!")
621
+ return
622
+
623
+ start_pos = SOUTH_MIAMI_BEACH
624
+
625
+ print(f"Planning path from {start_pos} to order #{target_order['id']} at {target_order['position']}")
626
+ print(f"Using {self.current_heuristic} heuristic")
627
+
628
+ path, nodes_expanded, computation_time, closed_count = self.delivery_bot.path_planner.a_star(
629
+ start_pos, target_order['position'], self.current_heuristic
630
+ )
631
+
632
+ if path:
633
+ print(f"✓ Path found! Length: {len(path)} segments, Nodes expanded: {nodes_expanded}")
634
+
635
+ self.analysis_data[self.current_heuristic]['nodes_expanded'].append(nodes_expanded)
636
+ self.analysis_data[self.current_heuristic]['computation_time'].append(computation_time)
637
+ self.analysis_data[self.current_heuristic]['path_length'].append(len(path))
638
+
639
+ self.delivery_bot.current_path = path
640
+ self.delivery_bot.current_delivery_order = target_order
641
+ self.delivery_bot.current_path_index = 0
642
+ self.delivery_bot.is_moving = False
643
+
644
+ self.start_traversal_btn.setEnabled(True)
645
+
646
+ self.update_map()
647
+ self.update_status()
648
+ self.update_analysis()
649
+
650
+ QMessageBox.information(self, "Path Found",
651
+ f"✓ Path planned using {self.current_heuristic} heuristic!\n"
652
+ f"• Path length: {len(path)} segments\n"
653
+ f"• Nodes expanded: {nodes_expanded}\n"
654
+ f"• Computation time: {computation_time:.3f}s\n\n"
655
+ f"Click 'Start Traversal' to begin delivery to Order #{target_order['id']}.")
656
+ else:
657
+ print(f"✗ No path found to order #{target_order['id']}")
658
+ QMessageBox.warning(self, "Path Planning Failed",
659
+ f"Could not find a valid path to Order #{target_order['id']}.\n"
660
+ f"Try a different order or heuristic.\n"
661
+ f"Nodes expanded: {nodes_expanded}")
662
+
663
+ def start_traversal(self):
664
+ if not self.delivery_bot.current_path or not self.delivery_bot.current_delivery_order:
665
+ QMessageBox.warning(self, "No Path", "No path planned! Plan a path first.")
666
+ return
667
+
668
+ print(f"Starting traversal for order #{self.delivery_bot.current_delivery_order['id']}")
669
+
670
+ success = self.delivery_bot.start_delivery(
671
+ self.delivery_bot.current_delivery_order,
672
+ self.delivery_bot.current_path
673
+ )
674
+
675
+ if success:
676
+ self.bot_status_label.setText("Status: Starting delivery...")
677
+ self.bot_status_label.setStyleSheet("font-weight: bold; color: orange;")
678
+
679
+ self.plan_path_btn.setEnabled(False)
680
+ self.start_traversal_btn.setEnabled(False)
681
+ self.simulate_delivery_btn.setEnabled(False)
682
+ self.run_comparison_btn.setEnabled(False)
683
+
684
+ self.animation_timer.start(self.animation_speed)
685
+
686
+ self.update_status()
687
+ self.update_map()
688
+
689
+ print("✓ Traversal started successfully")
690
+ else:
691
+ QMessageBox.warning(self, "Error", "Failed to start delivery - invalid path!")
692
+
693
+ def animate_traversal(self):
694
+ if not self.delivery_bot.is_moving:
695
+ self.animation_timer.stop()
696
+ return
697
+
698
+ still_moving, completed_order = self.delivery_bot.move_to_next_position()
699
+
700
+ if still_moving:
701
+ # Still moving - update progress
702
+ progress = (self.delivery_bot.current_path_index / len(self.delivery_bot.current_path)) * 100
703
+ self.bot_status_label.setText(f"Status: Delivering order #{self.delivery_bot.current_delivery_order['id']}... {progress:.1f}%")
704
+
705
+ self.update_map()
706
+
707
+ if self.delivery_bot.current_path_index % 5 == 0:
708
+ print(f"Bot position: {self.delivery_bot.current_path_index}/{len(self.delivery_bot.current_path)}")
709
+ else:
710
+ # Delivery complete
711
+ self.animation_timer.stop()
712
+ self.bot_status_label.setText("Status: Delivery Complete!")
713
+ self.bot_status_label.setStyleSheet("font-weight: bold; color: green;")
714
+
715
+ # Re-enable buttons
716
+ self.plan_path_btn.setEnabled(True)
717
+ self.start_traversal_btn.setEnabled(False)
718
+ self.simulate_delivery_btn.setEnabled(True)
719
+ self.run_comparison_btn.setEnabled(True)
720
+
721
+ # Update final status
722
+ self.update_status()
723
+ self.update_map()
724
+
725
+ print("✓ Delivery completed successfully")
726
+
727
+ if completed_order:
728
+ QMessageBox.information(self, "Delivery Complete",
729
+ f"✓ Order #{completed_order['id']} delivered successfully!")
730
+
731
+ def complete_delivery(self):
732
+ in_progress_orders = self.delivery_bot.get_orders_by_status('in_progress')
733
+ if not in_progress_orders:
734
+ QMessageBox.information(self, "No Deliveries", "No orders in progress!")
735
+ return
736
+
737
+ target_order = in_progress_orders[0]
738
+ self.delivery_bot.update_order_status(target_order['id'], 'delivered')
739
+
740
+ if self.animation_timer.isActive():
741
+ self.animation_timer.stop()
742
+ self.delivery_bot.is_moving = False
743
+
744
+ self.bot_status_label.setText("Status: Ready for delivery")
745
+ self.bot_status_label.setStyleSheet("font-weight: bold; color: blue;")
746
+
747
+ self.update_status()
748
+ self.update_map()
749
+
750
+ QMessageBox.information(self, "Delivery Complete", f"Order #{target_order['id']} delivered!")
751
+
752
+ def run_heuristic_comparison(self):
753
+ target_order = self.delivery_bot.get_next_waiting_order()
754
+ if not target_order:
755
+ QMessageBox.information(self, "No Orders", "No waiting orders to compare!")
756
+ return
757
+
758
+ start_pos = SOUTH_MIAMI_BEACH
759
+
760
+ comparison_results = {}
761
+
762
+ for heuristic in ['manhattan', 'euclidean', 'chebyshev']:
763
+ print(f"Running comparison with {heuristic} heuristic")
764
+ path, nodes_expanded, computation_time, closed_count = self.delivery_bot.path_planner.a_star(
765
+ start_pos, target_order['position'], heuristic
766
+ )
767
+
768
+ if path:
769
+ comparison_results[heuristic] = {
770
+ 'nodes_expanded': nodes_expanded,
771
+ 'computation_time': computation_time,
772
+ 'path_length': len(path),
773
+ 'efficiency': len(path) / nodes_expanded if nodes_expanded > 0 else 0
774
+ }
775
+
776
+ self.analysis_data[heuristic]['nodes_expanded'].append(nodes_expanded)
777
+ self.analysis_data[heuristic]['computation_time'].append(computation_time)
778
+ self.analysis_data[heuristic]['path_length'].append(len(path))
779
+
780
+ comparison_text = "=== HEURISTIC COMPARISON RESULTS ===\n\n"
781
+
782
+ for heuristic, results in comparison_results.items():
783
+ comparison_text += f"{heuristic.upper()} HEURISTIC:\n"
784
+ comparison_text += f" Nodes Expanded: {results['nodes_expanded']}\n"
785
+ comparison_text += f" Computation Time: {results['computation_time']:.4f} seconds\n"
786
+ comparison_text += f" Path Length: {results['path_length']} segments\n"
787
+ comparison_text += f" Exploration Efficiency: {results['efficiency']:.4f}\n\n"
788
+
789
+ if len(comparison_results) == 3:
790
+ best_nodes = min(comparison_results.items(), key=lambda x: x[1]['nodes_expanded'])
791
+ best_time = min(comparison_results.items(), key=lambda x: x[1]['computation_time'])
792
+ best_efficiency = max(comparison_results.items(), key=lambda x: x[1]['efficiency'])
793
+
794
+ comparison_text += "=== PERFORMANCE ANALYSIS ===\n\n"
795
+ comparison_text += f"Best for Nodes Expanded: {best_nodes[0].title()} ({best_nodes[1]['nodes_expanded']} nodes)\n"
796
+ comparison_text += f"Best for Computation Time: {best_time[0].title()} ({best_time[1]['computation_time']:.4f}s)\n"
797
+ comparison_text += f"Best for Exploration Efficiency: {best_efficiency[0].title()} ({best_efficiency[1]['efficiency']:.4f})\n\n"
798
+
799
+ euclidean_result = comparison_results.get('euclidean', {})
800
+ if euclidean_result:
801
+ comparison_text += "=== EUCLIDEAN HEURISTIC FOR REAL-WORLD APPLICATIONS ===\n\n"
802
+ comparison_text += "✅ Use Euclidean When:\n"
803
+ comparison_text += "• Working in continuous spaces\n"
804
+ comparison_text += "• Any-angle movement is allowed\n"
805
+ comparison_text += "• Accuracy is more important than speed\n"
806
+ comparison_text += "• Real-world applications\n"
807
+ comparison_text += "Examples: Robotics, GPS navigation, drone pathfinding\n\n"
808
+
809
+ self.comparison_text.setText(comparison_text)
810
+ self.update_analysis()
811
+
812
+ QMessageBox.information(self, "Comparison Complete", "Heuristic comparison completed!")
813
+
814
+ def update_analysis(self):
815
+ heuristics = ['manhattan', 'euclidean', 'chebyshev']
816
+ colors = ['blue', 'green', 'red']
817
+
818
+ self.efficiency_canvas.axes.clear()
819
+ lines = []
820
+
821
+ for i, heuristic in enumerate(heuristics):
822
+ if self.analysis_data[heuristic]['nodes_expanded']:
823
+ line, = self.efficiency_canvas.axes.plot(
824
+ range(len(self.analysis_data[heuristic]['nodes_expanded'])),
825
+ self.analysis_data[heuristic]['nodes_expanded'],
826
+ label=heuristic.title(),
827
+ color=colors[i],
828
+ marker='o'
829
+ )
830
+ lines.append(line)
831
+
832
+ self.efficiency_canvas.axes.set_title('Search Efficiency (Nodes Expanded - Lower is Better)')
833
+ self.efficiency_canvas.axes.set_xlabel('Path Planning Attempt')
834
+ self.efficiency_canvas.axes.set_ylabel('Nodes Expanded')
835
+ if lines:
836
+ self.efficiency_canvas.axes.legend()
837
+ self.efficiency_canvas.axes.grid(True, alpha=0.3)
838
+ self.efficiency_canvas.draw()
839
+
840
+ self.performance_canvas.axes.clear()
841
+ lines = []
842
+
843
+ for i, heuristic in enumerate(heuristics):
844
+ if self.analysis_data[heuristic]['computation_time']:
845
+ line, = self.performance_canvas.axes.plot(
846
+ range(len(self.analysis_data[heuristic]['computation_time'])),
847
+ self.analysis_data[heuristic]['computation_time'],
848
+ label=heuristic.title(),
849
+ color=colors[i],
850
+ marker='o'
851
+ )
852
+ lines.append(line)
853
+
854
+ self.performance_canvas.axes.set_title('Computation Performance (Time - Lower is Better)')
855
+ self.performance_canvas.axes.set_xlabel('Path Planning Attempt')
856
+ self.performance_canvas.axes.set_ylabel('Computation Time (seconds)')
857
+ if lines:
858
+ self.performance_canvas.axes.legend()
859
+ self.performance_canvas.axes.grid(True, alpha=0.3)
860
+ self.performance_canvas.draw()
861
+
862
+ self.optimality_canvas.axes.clear()
863
+ lines = []
864
+
865
+ for i, heuristic in enumerate(heuristics):
866
+ if self.analysis_data[heuristic]['path_length']:
867
+ line, = self.optimality_canvas.axes.plot(
868
+ range(len(self.analysis_data[heuristic]['path_length'])),
869
+ self.analysis_data[heuristic]['path_length'],
870
+ label=heuristic.title(),
871
+ color=colors[i],
872
+ marker='o'
873
+ )
874
+ lines.append(line)
875
+
876
+ self.optimality_canvas.axes.set_title('Path Optimality (All Should Be Equal)')
877
+ self.optimality_canvas.axes.set_xlabel('Path Planning Attempt')
878
+ self.optimality_canvas.axes.set_ylabel('Path Length (segments)')
879
+ if lines:
880
+ self.optimality_canvas.axes.legend()
881
+ self.optimality_canvas.axes.grid(True, alpha=0.3)
882
+ self.optimality_canvas.draw()
883
+
884
+ def main():
885
+ app = QApplication(sys.argv)
886
+
887
+ if not HAS_WEBENGINE:
888
+ msg = QMessageBox()
889
+ msg.setIcon(QMessageBox.Warning)
890
+ msg.setText("PyQtWebEngine not available")
891
+ msg.setInformativeText("Map display will be disabled. To enable maps, install:\n\npip install PyQtWebEngine")
892
+ msg.setWindowTitle("Module Missing")
893
+ msg.exec_()
894
+
895
+ if not HAS_FOLIUM:
896
+ msg = QMessageBox()
897
+ msg.setIcon(QMessageBox.Warning)
898
+ msg.setText("Folium not available")
899
+ msg.setInformativeText("Map display will be disabled. To enable maps, install:\n\npip install folium")
900
+ msg.setWindowTitle("Module Missing")
901
+ msg.exec_()
902
+
903
+ window = MainWindow()
904
+ window.show()
905
+ sys.exit(app.exec_())
906
+
907
+ if __name__ == "__main__":
908
+ main()
flux_krea_00470_.png ADDED

Git LFS Details

  • SHA256: 4db95317dbda51d8f45aae58aee4ef3114dd58f48ff7cac143ac928f07c2fdd4
  • Pointer size: 132 Bytes
  • Size of remote file: 1.45 MB
output.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:45fb3183ed49c2173367017daa72ff7aef46558b66a03d91c47da80fb94eaaf4
3
+ size 51576532
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ PyQt5
2
+ folium
3
+ matplotlib
4
+ numpy
5
+ PyQtWebEngine