From 2301ac357eb0f21ac39e9c505e6acef6cd796776 Mon Sep 17 00:00:00 2001 From: "derek.holloway" Date: Tue, 13 Jan 2026 16:03:37 -0800 Subject: [PATCH] init commit --- AssignmentData.py | 138 +++++++++++++++++++++++++++++++++++++ DeliveryObject.py | 31 +++++++++ MyHashTable.py | 28 ++++++++ MyLinkedList.py | 59 ++++++++++++++++ Screenshots/.DS_Store | Bin 0 -> 6148 bytes Truck.py | 91 +++++++++++++++++++++++++ UI.py | 147 ++++++++++++++++++++++++++++++++++++++++ WGU_Truck_Delivery.docx | Bin 0 -> 19958 bytes main.py | 128 ++++++++++++++++++++++++++++++++++ 9 files changed, 622 insertions(+) create mode 100644 AssignmentData.py create mode 100644 DeliveryObject.py create mode 100644 MyHashTable.py create mode 100644 MyLinkedList.py create mode 100644 Screenshots/.DS_Store create mode 100644 Truck.py create mode 100644 UI.py create mode 100644 WGU_Truck_Delivery.docx create mode 100755 main.py diff --git a/AssignmentData.py b/AssignmentData.py new file mode 100644 index 0000000..077cada --- /dev/null +++ b/AssignmentData.py @@ -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) +] \ No newline at end of file diff --git a/DeliveryObject.py b/DeliveryObject.py new file mode 100644 index 0000000..0d5e110 --- /dev/null +++ b/DeliveryObject.py @@ -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 \ No newline at end of file diff --git a/MyHashTable.py b/MyHashTable.py new file mode 100644 index 0000000..90f9899 --- /dev/null +++ b/MyHashTable.py @@ -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 \ No newline at end of file diff --git a/MyLinkedList.py b/MyLinkedList.py new file mode 100644 index 0000000..32afedc --- /dev/null +++ b/MyLinkedList.py @@ -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 \ No newline at end of file diff --git a/Screenshots/.DS_Store b/Screenshots/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..87426b893174ced214a66bf35855c56dd4fbfb3b GIT binary patch literal 6148 zcmeHK%Sr<=6g?TcD7xvww<5v4#nx4KLunBeM5ykyEs9vIR0}TW4+OV@AK_Q%2lzYI zbCZbEcFI&2#Y!&RduH-Db90hOW&o&iw^;yk0J3z!%q&BKNxk@x1+GN;DZ|mlEi~FV zXY}S3@Cy8O1?1gbLk)L0qsQOhBOG9t?{P>kV4LxVEi{VA^XeKGd#!e(-fGv$!0kgl z67xM{X3&dcu_kf_va`gwHpbv8VhcOzc?O*U?v)>KCuWN>8J}Y#Ur1#vjnHs&Ah-2Y zrYdKmnL0NoKrkQ6aA{25#POqc|DObsC0Td((V2zAf9_x%|AYGSU( z=y0_*aAi&$S)*2o#EkL4jGq!XyIj%VCsusLm(n!F3p0<;x*5t;1!c3%&P`R;K3T7T zS6~tf$o`<|f}z96qS-naxLKR}jG!3Hya&4MJg%*AE3U~$j6_~NNIXVBUqyPW=JNY}WfLGvODWEcy zTBXb(xwCa>aB|ja`YByZ+C>&k7Y276n+rLLm+8h>r{#heI*cr`gyug4j0`?_1tzM% ECq)m((EtDd literal 0 HcmV?d00001 diff --git a/Truck.py b/Truck.py new file mode 100644 index 0000000..6b017f4 --- /dev/null +++ b/Truck.py @@ -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 \ No newline at end of file diff --git a/UI.py b/UI.py new file mode 100644 index 0000000..2297336 --- /dev/null +++ b/UI.py @@ -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.") \ No newline at end of file diff --git a/WGU_Truck_Delivery.docx b/WGU_Truck_Delivery.docx new file mode 100644 index 0000000000000000000000000000000000000000..a5783d82f3b483242a9b97c66ea8b0f506bce530 GIT binary patch literal 19958 zcma(119+rC(>4sp&PE&Cwr$(CHLWp91Y`~Tl_AMfnNLi(_H$z5h)$RIey3}vfNp{?H(@R zjh)O8jZDm4{sF8Y4GFdH_#krhW&RWh5Rk(EIzdQZ5+-)W3XXR6PV~lhj;3_(wl=m( zf`A?d#I{G&_{;FbJS7uSK`UxGQ6Z4Ih)iqxEj9-lFT$%=E?^~#=JT_t!B%7QU13!S zub*=BT;!796M=q}70+a5(V!>Vi-3gcNb4R6~9K~E1IQkNZ2ykZDTCw`W$G=PY8}Ah>70vcsN7Cnq&Nb*?7v=cyWtjj2 z0d@XY7a{&n7a7{y|MgI|;wYe(;LAhQG?j~3{KniwB8n9ip=HrpO}t`YY>dh9P!>sV zKP0QP_)=#l#xCG~x_ex-Zh;NJkfIwR_ZtEwP}1!=Ao;M!BdB6%h?ACPC;Wt>H|Qm8 z?$s`2!65H~*brP1UE(S|gwk9PlDFM6Jx&f};&s8xAXW+uG`K2Tl1G@yXNfU~n)pTn z`H?5yYj2;GyXGs~Y_#~gJb6&ij^7P`*HZXxXw;=X@{c307;z-_zQtk~o`jaWkBED* z3ps|%hue^r3+_6#uv=}DOcI)6mbhxtnqANv=cQU@)BPLU z5N)MWR59W3MOxM*PJV$K9yx{#M3igkKM%Uf2eJ_~*|Mu-36sos7G(fh$pq^YC|(S9 z37>xi&}Cl)lfjpF)c>n@Q2wWPj9r|Z?QH+ypouz|9)|D1+v}xJ%d0Ao@iV_7Ik;6G z<2D+Bo$Q8WBY$`i2VBI2;`7PP^1cgKAuc0XHPcg50@1>MBd4`{FXoW!9^Nv*3(DJv zplNko)FB2B8wQS~A+Y8w{ccsfG_eJr%4tkTENZ8ea$9xt2@2_rq0mv`)OY7^e*Z;mS;C!{Z**Tif|8x2L zS|eO+O##k-ZTsg^sxd-Ip``0N#)Tdw#u)7uuwd6 zHg0AaHeKD8lj}u3q-J@k z8k7Gpp*dloj6Nj+KcWH@bB(|}lf)dZ-Ln$BPMNF<`K_Ws|9rE$#Ep(mVaR$=v(W%iNx!S#mIhszgqzMJ$0Y+)VDZ_H`C5w@>}X2n<1$0 zu3orn+krAUm|~Ql96!$>{?3l8Nrmm)m+h(l)$4zk&40|!|D2b3t#trS6Kcn$GNRW! zNWEIZJ_#BTB*a_`NXAl`$zA~k*aVz;ir$31kmIinoqN_7&mRdshjCfy?+zy%7O=W~ zmDz*{lap=}&)?WLeIDyrcN95mZQa^A_<6OV=93#|be-DWeLqXhH)(

MM)tvRm|Q zlJ&hGPuw;EHB{Q)s!sy^f+qBrPy4r8sP$`9sNWmZQYSpML(PkAYi)E-m5O6USGH@= zD-mBR_nBcW{t{;}fy#+m z9|A)bqm(w1ga)bv+iy1V<*mtQ<}(?aWarTh!nwu2b4~TB*FBGh&v^|LEed;6WV4gF zI^S%y&N+QfKyczmxB}}%qKtFoZcMJ z8^yxM=L=aDBk_k^%<78RJ5ou{dMbswixHo8PQg2dxhMwp-P4IVM~|6@T_7*n)VJrO z9j$0TY@+M1n~T*6>B6)>J4Xn=PwM-)eB9rDcIe@cv^9(DD_y|;dcPj`a<~j}qy;2R zm(6^Fy2)7;p0!?`{!Zyn9nl+q-zQ`{}*-PFRc+S8znk^kI{+GCQWM224=_XJx%_2Ckl?-%k5kVpX zi*JJ25lz`6cHZ_C);%MGklZg!OZHjcjKCp`k!2EG`Gl&K>X}-4)z!9D+Ddwot5NorsuvZXkDJ`sD{>(T{PLTCNu^8o6Pk{%e+mA|Ewr;otQ$Q7H*J;=*dVh_=ePwT5$$(F^-ufx zs+#BTyp9BGRoCQ!9x&|Rq>(neQ9>1y>La@ur1;EiwlDvvx?caXSsR9;O899^C=3^V z#u5_`zQ2;z{%RIaZqE=DnGXu&{*7a75Uj{7*fXZMj_9^N0OnfnVq;<}f{egKRWMP7 zvas$5vJl2ST5fxG2iaqdf~#7}{1WTQ@T$)B2#`mcBeNvIzkqtfwz~vVdH~(1shKEr zQ-t{AQ*&L#;}qmr)b9|_T-44)vV*xbxCV?(1=A0CeY`Ls!PuNOxUW1H;@k)dJ5m

YwHhf%uPchD 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 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() \ No newline at end of file