adding Data Structures and Algorithms II C950
This commit is contained in:
+160
@@ -0,0 +1,160 @@
|
||||
# Student ID: 012498637
|
||||
# Student Name: Zakaria Benmoulay
|
||||
# Course: C950 - Data Structures and Algorithms II
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
||||
SPEED_MPH = 18 # all trucks travel at 18 mph
|
||||
HUB_ADDRESS = "Salt Lake City, UT 84107" # WGU hub — matches the distance table
|
||||
|
||||
|
||||
class Truck:
|
||||
# Let's initialize the truck with assigned packages and starting conditions.
|
||||
def __init__(self, truck_id, packages, depart_time, package_table,
|
||||
address_list, distance_matrix):
|
||||
|
||||
self.truck_id = truck_id
|
||||
self.package_ids = list(packages) # copy so original list is not modified
|
||||
self.depart_time = depart_time
|
||||
self.package_table = package_table
|
||||
self.address_list = address_list
|
||||
self.distance_matrix = distance_matrix
|
||||
|
||||
# Simulation state — updated as deliver_all() runs
|
||||
self.current_address = HUB_ADDRESS # truck starts at the hub
|
||||
self.current_time = depart_time # clock starts at departure time
|
||||
self.total_miles = 0.0 # accumulates mileage during delivery
|
||||
self.route = [] # list of (package_id, address, arrival_time)
|
||||
|
||||
|
||||
# Distance and time helpers
|
||||
def _distance(self, addr_a, addr_b):
|
||||
|
||||
def find_index(addr):
|
||||
addr_clean = addr.strip().lower()
|
||||
|
||||
# Pass 1: exact match
|
||||
for i, a in enumerate(self.address_list):
|
||||
if a.strip().lower() == addr_clean:
|
||||
return i
|
||||
|
||||
# Pass 2: substring match (handles abbreviations and extra words)
|
||||
for i, a in enumerate(self.address_list):
|
||||
if addr_clean in a.strip().lower() or a.strip().lower() in addr_clean:
|
||||
return i
|
||||
|
||||
# Pass 3: match on the first two tokens (street number + street name)
|
||||
tokens = addr_clean.split()
|
||||
if tokens:
|
||||
prefix = " ".join(tokens[:2])
|
||||
for i, a in enumerate(self.address_list):
|
||||
if a.strip().lower().startswith(prefix):
|
||||
return i
|
||||
|
||||
raise ValueError(f"Address not found in distance table: '{addr}'")
|
||||
|
||||
i = find_index(addr_a)
|
||||
j = find_index(addr_b)
|
||||
return self.distance_matrix[i][j]
|
||||
|
||||
def _travel_time(self, miles):
|
||||
|
||||
hours = miles / SPEED_MPH
|
||||
return timedelta(hours=hours)
|
||||
|
||||
|
||||
# Core nearest-neighbor delivery algorithm
|
||||
def deliver_all(self, return_to_hub=False):
|
||||
|
||||
# Step 1 — Mark all packages on this truck as En Route at departure time
|
||||
for pid in self.package_ids:
|
||||
pkg = self.package_table.lookup(pid)
|
||||
if pkg:
|
||||
pkg.mark_en_route(self.depart_time, self.truck_id)
|
||||
|
||||
# Work from a copy so the original package_ids list is preserved
|
||||
remaining = list(self.package_ids)
|
||||
|
||||
# Step 2 — Nearest-neighbor delivery loop
|
||||
while remaining:
|
||||
|
||||
# Inner scan: find the package with the shortest distance
|
||||
# from the truck's current location
|
||||
nearest_pid = None
|
||||
nearest_dist = float("inf") # start with infinity so any real distance wins
|
||||
|
||||
for pid in remaining:
|
||||
pkg = self.package_table.lookup(pid)
|
||||
if pkg is None:
|
||||
continue
|
||||
|
||||
# Get the correct address for this package at this time
|
||||
# (handles Package 9's address correction at 10:20 AM)
|
||||
address = self._resolve_address(pid, self.current_time)
|
||||
dist = self._distance(self.current_address, address)
|
||||
|
||||
# Update nearest if this package is closer than the current best
|
||||
if dist < nearest_dist:
|
||||
nearest_dist = dist
|
||||
nearest_pid = pid
|
||||
|
||||
if nearest_pid is None:
|
||||
break # safety check — should not happen with valid data
|
||||
|
||||
# Step 2b — Drive to the nearest package's address
|
||||
pkg = self.package_table.lookup(nearest_pid)
|
||||
dest_address = self._resolve_address(nearest_pid, self.current_time)
|
||||
travel = self._travel_time(nearest_dist)
|
||||
|
||||
self.current_time += travel # advance the simulated clock
|
||||
self.total_miles += nearest_dist # accumulate mileage
|
||||
self.current_address = dest_address # move truck to new location
|
||||
|
||||
# Step 2c — Mark the package as delivered at the current time
|
||||
pkg.mark_delivered(self.current_time)
|
||||
self.route.append((nearest_pid, dest_address, self.current_time))
|
||||
|
||||
# Step 2d — Remove from remaining list
|
||||
remaining.remove(nearest_pid)
|
||||
|
||||
# Step 3 — Return to hub if needed (for driver reassignment to Truck 3)
|
||||
if return_to_hub:
|
||||
dist_home = self._distance(self.current_address, HUB_ADDRESS)
|
||||
self.total_miles += dist_home
|
||||
self.current_time += self._travel_time(dist_home)
|
||||
self.current_address = HUB_ADDRESS
|
||||
|
||||
# Package 9 address correction
|
||||
def _resolve_address(self, package_id, current_time):
|
||||
|
||||
pkg = self.package_table.lookup(package_id)
|
||||
|
||||
if package_id == 9:
|
||||
# Define the correction time as 10:20 AM on the simulation date
|
||||
correction_time = self.depart_time.replace(
|
||||
hour=10, minute=20, second=0, microsecond=0
|
||||
)
|
||||
if current_time >= correction_time:
|
||||
return "410 S State St" # corrected address after 10:20 AM
|
||||
else:
|
||||
return pkg.address # wrong address before 10:20 AM
|
||||
|
||||
return pkg.address # all other packages return their address unchanged
|
||||
|
||||
|
||||
# Route summary display
|
||||
def print_route(self):
|
||||
|
||||
print(f"\n=== Truck {self.truck_id} Route ===")
|
||||
print(f"Departed hub : {self.depart_time.strftime('%I:%M %p')}")
|
||||
for pid, addr, arrival in self.route:
|
||||
pkg = self.package_table.lookup(pid)
|
||||
deadline = pkg.deadline if pkg else "?"
|
||||
print(
|
||||
f" Pkg {pid:>2} -> {addr:<40} "
|
||||
f"Arrived: {arrival.strftime('%I:%M %p')} "
|
||||
f"Deadline: {deadline}"
|
||||
)
|
||||
print(f"Total miles : {self.total_miles:.2f}")
|
||||
print(f"Finished at : {self.current_time.strftime('%I:%M %p')}")
|
||||
Reference in New Issue
Block a user