# Some are wrong. Not fully implemented. def run_simulation(initial_events: list[Event], system: FoodDeliverySystem) -> None: events = EventQueueList() # Initialize an empty priority queue of events for event in initial_events: events.enqueue(event) # Repeatedly remove and process the next event while not events.is_empty(): event = events.dequeue() new_events = event.handle_event(system) for new_event in new_events: events.enqueue(new_event) class FoodDeliverySimulation: """A simulation of the food delivery system. """ # Private Instance Attributes: # - _system: The FoodDeliverySystem instance that this simulation uses. # - _events: A collection of the events to process during the simulation. _system: FoodDeliverySystem _events: EventQueue def __init__(self, start_time: datetime.datetime, num_days: int, num_couriers: int, num_customers: int, num_restaurants: int) -> None: """Initialize a new simulation with the given simulation parameters. start_time: the starting time of the simulation num_days: the number of days that the simulation runs num_couriers: the number of couriers in the system num_customers: the number of customers in the system num_restaurants: the number of restaurants in the system """ self._events = EventQueueList() self._system = FoodDeliverySystem() self._populate_initial_events(start_time, num_days) self._generate_system(num_couriers, num_customers, num_restaurants) def _populate_initial_events(self, start_time: datetime.datetime, num_days: int) -> None: """Populate this simulation's Event priority queue with GenerateOrdersEvents. One new GenerateOrderEvent is generated per day, starting with start_time and repeating num_days times. """ def _generate_system(self, num_couriers: int, num_customers: int, num_restaurants: int) -> None: """Populate this simulation's FoodDeliverySystem with the specified number of entities. """ def run(self) -> None: """Run this simulation. """ while not self._events.is_empty(): event = self._events.dequeue() new_events = event.handle_event(self._system) for new_event in new_events: self._events.enqueue(new_event) # >>> simulation = FoodDeliverySimulation(datetime.datetime(2022, 11, 30), 7, 4, 100, 50) # >>> simulation.run() def _populate_initial_events(self, start_time: datetime.datetime, num_days: int) -> None: """Populate this simulation's Event priority queue with GenerateOrdersEvents. One new GenerateOrderEvent is generated per day, starting with start_time and repeating num_days times. Preconditions: - num_days >= 0 """ for day in range(0, num_days): # 1. Create a GenerateOrderEvent for the given day after the start time. # 2. Enqueue the new event. def _generate_system(self, num_couriers: int, num_customers: int, num_restaurants: int) -> None: """Populate this simulation's FoodDeliverySystem with the specified number of entities. """ for i in range(0, num_customers): location = _generate_location() customer = Customer(f'Customer {i}', location) self._system.add_customer(customer) # Couriers and Restaurants are similar ... # Outside the class: helper for generating random locations in Toronto TORONTO_COORDS = (43.747743, 43.691170, -79.633951, -79.176646) def _generate_location() -> tuple[float, float]: """Return a randomly-generated location (latitude, longitude) within the Toronto bounds. """ return (random.uniform(TORONTO_COORDS[0], TORONTO_COORDS[1]), random.uniform(TORONTO_COORDS[2], TORONTO_COORDS[3])) # >>> simulation = FoodDeliverySimulation(datetime.datetime(2022, 11, 30), 7, 4, 100, 50) # >>> simulation.run()