init commit

This commit is contained in:
derek.holloway
2026-01-13 16:03:37 -08:00
parent 2841c55e14
commit 2301ac357e
9 changed files with 622 additions and 0 deletions
+138
View File
@@ -0,0 +1,138 @@
from DeliveryObject import DeliveryObject
# Names from the Data Tables provided
xyNames = [
"Western Governors University 4001 South 700 East, Salt Lake City, UT 84107",
"International Peace Gardens 1060 Dalton Ave S",
"Sugar House Park 1330 2100 S",
"Taylorsville-Bennion Heritage City Gov Off 1488 4800 S",
"Salt Lake City, Division of Health Services 177 W Price Ave",
"South Salt Lake Public Works 195 W Oakland Ave",
"Salt Lake City, Streets and Sanitation 2010 W 500 S",
"Deker Lake 2300 Parkway Blvd",
"Salt Lake City,Ottinger Hall 233 Canyon Rd",
"Columbus Library 2530 S 500 E",
"Taylorsville City Hall 2600 Taylorsville Blvd",
"South Salt Lake Police 2835 Main St",
"Council Hall 300 State St",
"Redwood Park 3060 Lester St",
"Salt Lake County Mental Health 3148 S 1100 W",
"Salt Lake County/United Police Dept 3365 S 900 W",
"West Valley Prosecutor 3575 W Valley Central Sta bus Loop",
"Housing Auth. of Salt Lake County 3595 Main St",
"Utah DMV Administrative Office 380 W 2880 S",
"Third District Juvenile Court 410 S State St",
"Cottonwood Regional Softball Complex 4300 S 1300 E",
"Holiday City Office 4580 S 2300 E",
"Murray City Museum 5025 State St",
"Valley Regional Softball Complex 5100 South 2700 West",
"City Center of Rock Springs 5383 South 900 East #104",
"Rice Terrace Pavilion Park 600 E 900 South",
"Wheeler Historic Farm 6351 South 900 East",
]
# Names from the Data Tables provided
xyNames2 = [
"HUB",
"1060 Dalton Ave S (84104)",
"1330 2100 S (84106)",
"1488 4800 S (84123)",
"177 W Price Ave (84115)",
"195 W Oakland Ave (84115)",
"2010 W 500 S (84104)",
"2300 Parkway Blvd (84119)",
"233 Canyon Rd (84103)",
"2530 S 500 E (84106)",
"2600 Taylorsville Blvd (84118)",
"2835 Main St (84115)",
"300 State St (84103)",
"3060 Lester St (84119)",
"3148 S 1100 W (84119)",
"3365 S 900 W (84119)",
"3575 W Valley Central Station bus Loop (84119)",
"3595 Main St (84115)",
"380 W 2880 S (84115)",
"410 S State St (84111)",
"4300 S 1300 E (84117)",
"4580 S 2300 E (84117)",
"5025 State St (84107)",
"5100 South 2700 West (84118)",
"5383 S 900 East #104 (84117)",
"600 E 900 South (84105)",
"6351 South 900 East (84121)"
]
# Data provided from assignment info
distanceChart = [
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[7.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[3.8, 7.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[11.0, 6.4, 9.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[2.2, 6.0, 4.4, 5.6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[3.5, 4.8, 2.8, 6.9, 1.9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[10.9, 1.6, 8.6, 8.6, 7.9, 6.3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[8.6, 2.8, 6.3, 4.0, 5.1, 4.3, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[7.6, 4.8, 5.3, 11.1, 7.5, 4.5, 4.2, 7.7, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[2.8, 6.3, 1.6, 7.3, 2.6, 1.5, 8.0, 9.3, 4.8, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[6.4, 7.3, 10.4, 1.0, 6.5, 8.7, 8.6, 4.6, 11.9, 9.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[3.2, 5.3, 3.0, 6.4, 1.5, 0.8, 6.9, 4.8, 4.7, 1.1, 7.3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[7.6, 4.8, 5.3, 11.1, 7.5, 4.5, 4.2, 7.7, 0.6, 5.1, 12.0, 4.7, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[5.2, 3.0, 6.5, 3.9, 3.2, 3.9, 4.2, 1.6, 7.6, 4.6, 4.9, 3.5, 7.3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[4.4, 4.6, 5.6, 4.3, 2.4, 3.0, 8.0, 3.3, 7.8, 3.7, 5.2, 2.6, 7.8, 1.3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[3.7, 4.5, 5.8, 4.4, 2.7, 3.8, 5.8, 3.4, 6.6, 4.0, 5.4, 2.9, 6.6, 1.5, 0.6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[7.6, 7.4, 5.7, 7.2, 1.4, 5.7, 7.2, 3.1, 7.2, 6.7, 8.1, 6.3, 7.2, 4.0, 6.4, 5.6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[2.0, 6.0, 4.1, 5.3, 0.5, 1.9, 7.7, 5.1, 5.9, 2.3, 6.2, 1.2, 5.9, 3.2, 2.4, 1.6, 7.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[3.6, 5.0, 3.6, 6.0, 1.7, 1.1, 6.6, 4.6, 5.4, 1.8, 6.9, 1.0, 5.4, 3.0, 2.2, 1.7, 6.1, 1.6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[6.5, 4.8, 4.3, 10.6, 6.5, 3.5, 3.2, 6.7, 1.0, 4.1, 11.5, 3.7, 1.0, 6.9, 6.8, 6.4, 7.2, 4.9, 4.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[1.9, 9.5, 3.3, 5.9, 3.2, 4.9, 11.2, 8.1, 8.5, 3.8, 6.9, 4.1, 8.5, 6.2, 5.3, 4.9, 10.6, 3.0, 4.6, 7.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[3.4, 10.9, 5.0, 7.4, 5.2, 6.9, 12.7, 10.4, 10.3, 5.8, 8.3, 6.2, 10.3, 8.2, 7.4, 6.9, 12.0, 5.0, 6.6, 9.3, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[2.4, 8.3, 6.1, 4.7, 2.5, 4.2, 10.0, 7.8, 7.8, 4.3, 4.1, 3.4, 7.8, 5.5, 4.6, 4.2, 9.4, 2.3, 3.9, 6.8, 2.9, 4.4, 0.0, 0.0, 0.0, 0.0],
[6.4, 6.9, 9.7, 0.6, 6.0, 9.0, 8.2, 4.2, 11.5, 7.8, 0.4, 6.9, 11.5, 4.4, 4.8, 5.6, 7.5, 5.5, 6.5, 11.4, 6.4, 7.9, 4.5, 0.0, 0.0, 0.0],
[2.4, 10.0, 6.1, 6.4, 4.2, 5.9, 11.7, 9.5, 9.5, 4.8, 4.9, 5.2, 9.5, 7.2, 6.3, 5.9, 11.1, 4.0, 5.6, 8.5, 2.8, 3.4, 1.7, 5.4, 0.0, 0.0],
[5.0, 4.4, 2.8, 10.1, 5.4, 3.5, 5.1, 6.2, 2.8, 3.2, 11.0, 3.7, 2.8, 6.4, 6.5, 5.7, 6.2, 5.1, 4.3, 1.8, 6.0, 7.9, 6.8, 10.6, 7.0, 0.0],
[3.6, 13.0, 7.4, 10.1, 5.5, 7.2, 14.2, 10.7, 14.1, 6.0, 6.8, 6.4, 14.1, 10.5, 8.8, 8.4, 13.6, 5.2, 6.9, 13.1, 4.1, 4.7, 3.1, 7.8, 1.3, 8.3]
]
# Data provided from assignment info [ wrapped in DeliveryObject's ]
packages = [
DeliveryObject(1, "195 W Oakland Ave", "Salt Lake City", "UT", 84115, "10:30 AM", 21),
DeliveryObject(2, "2530 S 500 E", "Salt Lake City", "UT", 84106, "EOD", 44),
DeliveryObject(3, "233 Canyon Rd", "Salt Lake City", "UT", 84103, "EOD", 2, "Can only be on truck 2"),
DeliveryObject(4, "380 W 2880 S", "Salt Lake City", "UT", 84115, "EOD", 4),
DeliveryObject(5, "410 S State St", "Salt Lake City", "UT", 84111, "EOD", 5),
DeliveryObject(6, "3060 Lester St", "West Valley City", "UT", 84119, "10:30 AM", 88, "Delayed on flight---will not arrive to depot until 9:05 am"),
DeliveryObject(7, "1330 2100 S", "Salt Lake City", "UT", 84106, "EOD", 8),
DeliveryObject(8, "300 State St", "Salt Lake City", "UT", 84103, "EOD", 9),
DeliveryObject(9, "300 State St", "Salt Lake City", "UT", 84103, "EOD", 2, "Wrong address listed"),
DeliveryObject(10, "600 E 900 South", "Salt Lake City", "UT", 84105, "EOD", 1),
DeliveryObject(11, "2600 Taylorsville Blvd", "Salt Lake City", "UT", 84118, "EOD", 1),
DeliveryObject(12, "3575 W Valley Central Station bus Loop", "West Valley City", "UT", 84119, "EOD", 1),
DeliveryObject(13, "2010 W 500 S", "Salt Lake City", "UT", 84104, "10:30 AM", 2),
DeliveryObject(14, "4300 S 1300 E", "Millcreek", "UT", 84117, "10:30 AM", 88, "Must be delivered with 15, 19"),
DeliveryObject(15, "4580 S 2300 E", "Holladay", "UT", 84117, "9:00 AM", 4),
DeliveryObject(16, "4580 S 2300 E", "Holladay", "UT", 84117, "10:30 AM", 88, "Must be delivered with 13, 19"),
DeliveryObject(17, "3148 S 1100 W", "Salt Lake City", "UT", 84119, "EOD", 2),
DeliveryObject(18, "1488 4800 S", "Salt Lake City", "UT", 84123, "EOD", 6, "Can only be on truck 2"),
DeliveryObject(19, "177 W Price Ave", "Salt Lake City", "UT", 84115, "EOD", 37),
DeliveryObject(20, "3595 Main St", "Salt Lake City", "UT", 84115, "10:30 AM", 37, "Must be delivered with 13, 15"),
DeliveryObject(21, "3595 Main St", "Salt Lake City", "UT", 84115, "EOD", 3),
DeliveryObject(22, "6351 South 900 East", "Murray", "UT", 84121, "EOD", 2),
DeliveryObject(23, "5100 South 2700 West", "Salt Lake City", "UT", 84118, "EOD", 5),
DeliveryObject(24, "5025 State St", "Murray", "UT", 84107, "EOD", 7),
DeliveryObject(25, "5383 South 900 East #104", "Salt Lake City", "UT", 84117, "10:30 AM", 7, "Delayed on flight---will not arrive to depot until 9:05 am"),
DeliveryObject(26, "5383 South 900 East #104", "Salt Lake City", "UT", 84117, "EOD", 25),
DeliveryObject(27, "1060 Dalton Ave S", "Salt Lake City", "UT", 84104, "EOD", 5),
DeliveryObject(28, "2835 Main St", "Salt Lake City", "UT", 84115, "EOD", 7, "Delayed on flight---will not arrive to depot until 9:05 am"),
DeliveryObject(29, "1330 2100 S", "Salt Lake City", "UT", 84106, "10:30 AM", 2),
DeliveryObject(30, "300 State St", "Salt Lake City", "UT", 84103, "10:30 AM", 1),
DeliveryObject(31, "3365 S 900 W", "Salt Lake City", "UT", 84119, "10:30 AM", 1),
DeliveryObject(32, "3365 S 900 W", "Salt Lake City", "UT", 84119, "EOD", 1, "Delayed on flight---will not arrive to depot until 9:05 am"),
DeliveryObject(33, "2530 S 500 E", "Salt Lake City", "UT", 84106, "EOD", 1),
DeliveryObject(34, "4580 S 2300 E", "Holladay", "UT", 84117, "10:30 AM", 2),
DeliveryObject(35, "1060 Dalton Ave S", "Salt Lake City", "UT", 84104, "EOD", 88),
DeliveryObject(36, "2300 Parkway Blvd", "West Valley City", "UT", 84119, "EOD", 88, "Can only be on truck 2"),
DeliveryObject(37, "410 S State St", "Salt Lake City", "UT", 84111, "10:30 AM", 2),
DeliveryObject(38, "410 S State St", "Salt Lake City", "UT", 84111, "EOD", 9, "Can only be on truck 2"),
DeliveryObject(39, "2010 W 500 S", "Salt Lake City", "UT", 84104, "EOD", 9),
DeliveryObject(40, "380 W 2880 S", "Salt Lake City", "UT", 84115, "10:30 AM", 45)
]
+31
View File
@@ -0,0 +1,31 @@
import datetime
from enum import Enum
# This is just a DataType to store the package info
class DeliveryObject:
def __init__(self, id: str, address: str, city: str, state: str, zip: str, deadline: str, weight: float, specialNotes=""):
self.id: str = id
self.truckId = -1
self.deliveryNumber = 0
self.address: str = address
self.city: str = city
self.state: str = state
self.zipCode: str = zip
self.packageWeight: float = weight
self.notes = specialNotes
self.status: deliveryStatus = deliveryStatus.NO_STATUS
self.deadline: datetime.time = deadline
self.timeStart: datetime.time = None
self.timeEnd: datetime.time = None
if deadline != "EOD":
parts = deadline.replace(':', ' ').split()
self.deadline = datetime.time(int(parts[0]), int(parts[1]))
else:
self.deadline = datetime.time(23,59)
class deliveryStatus(Enum):
NO_STATUS = 0
AT_THE_HUB = 1
IN_ROUTE = 2
DELIVERED = 3
+28
View File
@@ -0,0 +1,28 @@
from MyLinkedList import DerekLinkedList
class DerekHashTable:
def __init__(self) -> None:
self.backingStore = [DerekLinkedList() for _ in range(36)] # Create a linked list backing for the hash table
self.count = 0
def __iter__(self):
for linkedList in self.backingStore: # Look at each linked list
for data in linkedList: # Look at each item in the linked list
yield data # Return the data from each cell
def _calcHash(self, key) -> int: # Use the built in hash function
return hash(key) % 36 # Modulo the size of the hash array
def addItem(self, key, value):
i = self._calcHash(key) # Calculate a hash/modulo and put in the correct list
self.backingStore[i].append(key, value)
self.count += 1
def getItem(self, key):
i = self._calcHash(key) # Calculate the hash/modulo to get the data from the linked list
return self.backingStore[i].get(key)
def remItem(self, key):
i = self._calcHash(key) # Calculate the hash/modulo to get the list
self.backingStore[i].remove(key) # Remove the item from the list
self.count -= 1
+59
View File
@@ -0,0 +1,59 @@
# This is the node definition. Allowing each 'chain link' to hold data and point to the next
class DerekLinkedListNode:
def __init__(self, key, item) -> None:
self.key = key
self.data = item
self.next: DerekLinkedListNode | None = None
# This is the real meat of the linked list
class DerekLinkedList:
# On init point to the first node and hold the total length
def __init__(self) -> None:
self.head: DerekLinkedListNode | None = None
self.len: int = 0
def __iter__(self):
current = self.head # Set the head of the linked list
while current: # if the value exists
yield current.data # return the data
current = current.next # set the next link
# Appends an item to the end of the list
def append(self, key, item) -> None:
self.len += 1 # update the overall length
newNode = DerekLinkedListNode(key, item) # create the node to insert
if not self.head: # if the head doesnt exist
self.head = newNode # set the head as the node and return
return
cur = self.head # if the head exists set cur to the next node
while cur.next: # loop through the rest of the chain looking for the end
cur = cur.next
cur.next = newNode # add the new node to the end
# Removes an item at the index
def remove(self, key) -> None:
lastNode = self.head # previous node
curNode = self.head # current node
while key != curNode.key: # get to the node we are curious about
lastNode = curNode
if curNode.next == None:
return None
curNode = curNode.next
if curNode == self.head:
self.head = curNode.next
else:
lastNode.next = curNode.next # pull the current node out of the linked list
self.len -= 1 # remove the length by 1
# returns the data at the index
def get(self, key):
if self.head: # if the head exists
cur: DerekLinkedListNode | None = self.head # set the current searching for at the head
while key != cur.key: # go through the chain until we hit the index were looking for
if cur.next == None:
return
cur = cur.next
if isinstance(cur, DerekLinkedListNode): # since we made it here the data were on is a LinkedListNode
return cur.data # return the value out of the node
else:
return None
BIN
View File
Binary file not shown.
+91
View File
@@ -0,0 +1,91 @@
import sys
import AssignmentData
import datetime
from MyHashTable import DerekHashTable
from DeliveryObject import DeliveryObject
from DeliveryObject import deliveryStatus
class Truck:
def __init__(self, truckId: str):
self.id = truckId # This trucks ID
self.packages: list[DeliveryObject] = [] # The packages on the truck
self.hasLoad: bool = False # Status of if the truck was loaded
self.mileage = 0.0 # Mileage counter
self.deliveryCounter = 0 # Keep track of the delivery counter
self.current_location = "HUB" # Current location of the truck
self.speed = 18 # Set from the project instructions
self.startTime: datetime.time | None = None # The trucks start time
def addPkg(self, pkg: DeliveryObject): # Adds a package to the truck
if len(self.packages) < 16: # Check if we can fit another package
pkg.timeStart = self.getTimeOfDay() # Set the start time for the pkg
pkg.truckId = self.id # Set the truck ID for the end menu
pkg.status = deliveryStatus.IN_ROUTE # The package is on the truck
self.packages.append(pkg) # Add the package
else:
print("to many packages loaded on the truck") # If there are too many packages
sys.exit() # Crash the program
def getDriveTime(self) -> int: # Usefull outside this class
hourPercent = self.mileage / self.speed # Calculate the hour percentage
rounededMin = int(60 * hourPercent) # Average that into minutes
return rounededMin
def getTimeOfDay(self) -> datetime.time: # Get the time of day using offsets
truckStartMins = (self.startTime.hour * 60) + self.startTime.minute # Get the minutes past midnight when the truck left
offsetMinutes = truckStartMins + self.getDriveTime() # Add on the number of minutes the truck as been driving
pkgHours = (offsetMinutes // 60) % 24 # pull hours back out
pkgMins = offsetMinutes % 60 # pull minutes back out
return datetime.time(pkgHours, pkgMins) # return the time of day
def deliverPkgs(self): # Delivers any packeges that match the current location
for i in range(len(self.packages) - 1, -1, -1): # Loop backwards so removals dont index shift
if self.packages[i].address in self.current_location: # Check if the package is at the correct address
self.packages[i].status = deliveryStatus.DELIVERED # Mark as delivered
self.packages[i].timeEnd = self.getTimeOfDay() # Mark the delivered time
self.deliveryCounter += 1 # Incriment the delivery counter
self.packages[i].deliveryNumber = self.deliveryCounter # Mark the package number
self.packages.remove(self.packages[i]) # Remove the package from the truck
def getMatrixIndex(self, address_string: str) -> int: # Helper function to get x or y offset in the distance table
for i, full_name in enumerate(AssignmentData.xyNames2): # Loop through the names in the list
if address_string in full_name: # Check if the name matches
return i # Return the index
for i, full_name in enumerate(AssignmentData.xyNames): # Loop through the names in the list
if address_string in full_name: # Check if the name matches
return i # Return the index
print(f"Address {address_string} is not in Full List") # Report items not found in the list
return 0 # Default to Hub if not found
def driveNextClosest(self, priority: DerekHashTable) -> int: # The bread and butter of the algorithm
# deal with priority
priorityPkgs = [] # set the packages we currently want to look for
for cur in self.packages: # loop thorugh looking for priority matches
if priority.getItem(cur.id):
priorityPkgs.append(cur) # if a priority matches add it to the list
if not priorityPkgs: # if no priority matches just use the normal
priorityPkgs = self.packages
# get the next closest
shortest_dist = float('inf') # Start with a very long distance to work backwards from
next_pkg = None # Keep track of the closest pkg
current_idx = self.getMatrixIndex(self.current_location) # Get the trucks xyIndex
for pkg in priorityPkgs: # Loop through the packages
dest_idx = self.getMatrixIndex(pkg.address) # Get the package xyIndex
row = max(current_idx, dest_idx) # Becuase the distance table is a triange
col = min(current_idx, dest_idx) # Keep on the buttom left side
dist = AssignmentData.distanceChart[row][col] # Get the distance from the grid
if dist < shortest_dist: # If the distance is shorter than the one found
shortest_dist = dist # Update the distance and package
next_pkg = pkg
# drive
self.mileage += shortest_dist # Add this shortest distance to the truck
self.current_location = next_pkg.address # update the location
self.deliverPkgs() # deliver packages at this location
if len(self.packages) == 0: # if there is no packages
self.hasLoad = False # Set the load to empty
return len(self.packages) # return packages left to deliver
+147
View File
@@ -0,0 +1,147 @@
import datetime
class DataUI:
# Filter for selections that allow
def selectionChooser(self, fullList):
print("Please make a selection")
print(" [1]: Show all ")
print(" [2]: Show per truck")
print(" [3]: Show package")
SubMenuTreeSelection = input("")
selection = []
if "1" in SubMenuTreeSelection:
selection = fullList
if "2" in SubMenuTreeSelection:
print("Choose a truck ID [1,2,3]")
choice = input("")
for pkg in fullList:
if choice in pkg.truckId:
selection.append(pkg)
if "3" in SubMenuTreeSelection:
print("Choose a package ID [0..39]")
choice = input("")
for pkg in fullList:
if choice == str(pkg.id):
selection.append(pkg)
return selection
# Shows information about trucks
def Trucks(self, truck1, truck2, truck3):
trucks = [truck1, truck2, truck3]
print("")
for i,v in enumerate(trucks):
startTime = "Pending"
endTime = "Pending"
if v.startTime != None:
startTime = v.startTime.strftime('%H:%M')
if v.packages.count == 0:
endTime = v.getTimeOfDay().strftime('%H:%M')
print(f"Truck [{v.id}] | "
f"Mileage: {v.mileage:.2f} | "
f"Departure: {startTime} | "
f"Arrival: {endTime} | "
f"DriveTime: {v.getDriveTime()} mintes")
print("")
# Shows information about packages
def Packages(self, package_hash):
selection = self.selectionChooser(package_hash)
print("")
for i,v in enumerate(selection):
print(f"[{v.id:2}] | "
f"[DeliveryNumber]: {v.deliveryNumber:2} | "
f"[onTruck]: {v.truckId:2} | "
f"[Address]: {v.address[:20]:20} | "
f"[Status]: {v.status:10} | "
f"[Note]: {v.notes:10}")
print("")
# Shows information about delivery times and deadlines
def DeliveryTime(self, package_hash):
selection = self.selectionChooser(package_hash)
print("")
for i,v in enumerate(selection):
deadline = v.deadline
metDeadline = "False"
if isinstance(v.deadline, datetime.time):
deadline = v.deadline.strftime('%H:%M')
if v.timeStart != None and v.timeEnd != None:
if v.timeEnd <= v.deadline:
metDeadline = "True"
if v.deadline == datetime.time(23,59):
deadline = "EOD"
startTime = "Pending"
if v.timeStart != None:
startTime = v.timeStart.strftime('%H:%M')
endTime = "Pending"
if v.timeEnd != None:
endTime = v.timeEnd.strftime('%H:%M')
print(f"[{v.id:2}] | "
f"[StartTime]: {startTime:8} | "
f"[DelivedTime]: {endTime:8} | "
f"[Deadline]: {deadline:8} | "
f"[MetDeadline]: {metDeadline:5} | "
f"[Note]: {v.notes:10}")
print("")
# Main prompt for the UI
def MainSelectionPrompt(self, truck1, truck2, truck3, package_hash):
while True:
print("Please make a selection")
print(" [1]: Show stats for trucks")
print(" [2]: Show stats for packages")
print(" [3]: Check delivery times")
print(" [4]: Total Mileage for trucks")
print(" [5]: Cancel")
MenuTreeSelection = input("")
if "1" in MenuTreeSelection:
self.Trucks(truck1, truck2, truck3)
elif "2" in MenuTreeSelection:
self.Packages(package_hash)
elif "3" in MenuTreeSelection:
self.DeliveryTime(package_hash)
elif "4" in MenuTreeSelection:
totalDistance = truck1.mileage + truck2.mileage + truck3.mileage
print(f"\nTotal Mileage for all trucks: {totalDistance:.2f} miles\n")
elif "5" in MenuTreeSelection:
break
# Prompt for pausing the simulation and opening the menu for stats at specific times
def TimePrompt(self, currentTime: datetime.time, truck1, truck2, truck3, package_hash) -> datetime.time:
while True:
print(" [1]: Pause the simulation at a point in time")
print(" [2]: Run the simulation to the end")
print(" [3]: View stats for this moment in time")
selectionInput = input("")
if "1" in selectionInput:
while True:
print("Please input a time in format of [hh:mm] or type 'back' to return to menu")
timeInput = input("")
if timeInput.lower() == 'back':
break
try:
time = timeInput.split(":")
hour = int(time[0])
minute = int(time[1])
chosenTime = datetime.time(hour, minute)
if chosenTime > currentTime:
print(f"Simulation set to pause at {hour:02}:{minute:02}")
return chosenTime
else:
print(f"Error: Time must be later than {currentTime.strftime('%H:%M')}.")
except ValueError:
print(f"Invalid format '{timeInput}'. Use HH:MM (e.g., 10:30).")
elif "2" in selectionInput:
print("Running simulation until the end of the day.")
return datetime.time(23, 59)
elif "3" in selectionInput:
self.MainSelectionPrompt(truck1, truck2, truck3, package_hash)
else:
print("Invalid selection. Please choose 1, 2, or 3.")
Binary file not shown.
Executable
+128
View File
@@ -0,0 +1,128 @@
# Derek Holloway 010396173
import datetime
import AssignmentData
import UI
from MyHashTable import DerekHashTable
from Truck import Truck
from DeliveryObject import deliveryStatus
def main():
UserUI = UI.DataUI() # Load in the UI library
globalTime = datetime.time(8, 0)
package_hash = DerekHashTable() # Init the hash table
for pkg in AssignmentData.packages: # Load the packages into the hash table
pkg.status = deliveryStatus.AT_THE_HUB
package_hash.addItem(pkg.id, pkg)
truck1 = Truck("1") # Init truck 1 starting at 8:00
truck1.startTime = datetime.time(8, 0)
truck2 = Truck("2") # Init truck 2 starting at 9:15 -> Delayed for package 6
truck2.startTime = datetime.time(9, 5)
truck3 = Truck("3") # Init truck 3 starting as soon as the fastest truck returned
truck3.startTime = None
pauseTime = UserUI.TimePrompt(globalTime, truck1, truck2, truck3, package_hash) # start the first time prompt
noRequirements = [1,2,4,5,7,8,10,11,12,17,21,22,23,24,26,27,29,30,31,33,34,35,37,39,40]
truck2Only = [3,18,36,38]
delayed = [6,9,25,28,32]
sameTruck = [13,14,15,16,19,20]
# Package requirements
priority = [1,6,13,14,15,16,21,25,29,30,31,34,37,40]
priority_hash = DerekHashTable() # Load the priority packages into a hash for quick lookup
for cur in priority:
priority_hash.addItem(cur, cur)
# Sort the known packages
t1_load = sameTruck # Start with the packages that requrie the same truck
t2_load = truck2Only + delayed # add in the truck 2 only and the delays
t3_load = [] # create an empty list
# Sort the Unknown packages
for i, v in enumerate(noRequirements): # enumerate through all the packages not predefined
if priority_hash.getItem(v): # if the item is a priority item
if len(t1_load) < 16: # load priorities on truck 1 first as it leaves earlier
t1_load.append(v)
elif len(t2_load) < 16: # fallback to truck 2
t2_load.append(v)
else: # fallback again to truck 3
t3_load.append(v)
else:
if len(t3_load) < 16: # non priority pkgs default to truck 3 as it leaves last
t3_load.append(v)
elif len(t2_load) < 16: # fall back to truck 2
t2_load.append(v)
elif len(t1_load) < 16: # fallback again to truck 1
t1_load.append(v)
# Run the simulation until all the packages have been delivered
debounce1 = True
debounce2 = True
debounce3 = True
truck3LoadDebounce = True
while True:
# update global time
if globalTime >= datetime.time(10, 20): # If the time is correct for update
pkg9 = package_hash.getItem(9) # Handle the Package #9 address correction before Truck 3 starts
pkg9.address = "410 S State St"
# load packages for truck 1
if globalTime >= truck1.startTime and debounce1: # If its time to laod the packages, run once
debounce1 = False # set debounce for only one run
truck1.hasLoad = True # Set the truck load variable
for pkg_id in t1_load: # Load the packages in truck 1
truck1.addPkg(package_hash.getItem(pkg_id))
# load packages for truck 2
if globalTime >= truck2.startTime and debounce2: # If its time to laod the packages, run once
debounce2 = False # set debounce for only one run
truck2.hasLoad = True # Set the truck load variable
for pkg_id in t2_load: # Load the packages in truck 2
truck2.addPkg(package_hash.getItem(pkg_id))
# load packages for truck 3
if globalTime >= truck3.startTime and debounce3: # If its time to laod the packages, run once
debounce3 = False # set debounce for only one run
truck2.hasLoad = True # Set the truck load variable
for pkg_id in t2_load: # Load the packages in truck 2
truck2.addPkg(package_hash.getItem(pkg_id))
# deliver packages for truck 1
if truck1.hasLoad and globalTime >= truck1.getTimeOfDay(): # if there is more packages and the time has passed
packagesLeft = truck1.driveNextClosest(priority_hash) # deliver the next set of packages
if packagesLeft == 0 and truck3LoadDebounce: # if all the packages are delivered
truck3LoadDebounce = False # set the debouce
truck3.startTime = truck1.getTimeOfDay() # set truck3 start time
# deliver packages for truck 2
if truck2.hasLoad and globalTime >= truck2.getTimeOfDay(): # if there is more packages and the time has passed
packagesLeft = truck2.driveNextClosest(priority_hash) # deliver the next set of packages
if packagesLeft == 0 and truck3LoadDebounce: # if all the packages are delivered
truck3LoadDebounce = False # set the debouce
truck3.startTime = truck1.getTimeOfDay() # set truck3 start time
# deliver packages for truck 3
if truck3.hasLoad and globalTime >= truck3.getTimeOfDay(): # if there is more packages and the time has passed
truck3.driveNextClosest(priority_hash) # deliver the next set of packages
# Calculate new global time
totalMins = (globalTime.hour * 60) + globalTime.minute + 5 # Get total mins and add 30
offsetHour = (totalMins // 60) % 24 # pull hours back out
offsetMins = totalMins % 60 # pull minutes back out
globalTime = datetime.time(offsetHour, offsetMins) # return the time of day
# if we are done delivering
if (truck3.hasLoad == False and truck2.hasLoad == False and truck1.hasLoad == False):
break
UserUI.MainSelectionPrompt(truck1, truck2, truck3, package_hash) # Display the command prompt Menus
if __name__ == "__main__":
main()