def test_priority_queue_is_empty_doctest() -> None: """Test the doctest provided for PriorityQueue.is_empty""" pq = PriorityQueue(str.__lt__) assert pq.is_empty() is True pq.add('fred') assert pq.is_empty() is False
class Simulation: """A simulation. This is the class which is responsible for setting up and running a simulation. The API is given to you: your main task is to implement the two methods according to their docstrings. Of course, you may add whatever private attributes and methods you want. But because you should not change the interface, you may not add any public attributes or methods. This is the entry point into your program, and in particular is used for auto-testing purposes. This makes it ESSENTIAL that you do not change the interface in any way! """ # === Private Attributes === # @type _events: PriorityQueue[Event] # A sequence of events arranged in priority determined by the event # sorting order. # @type _dispatcher: Dispatcher # The dispatcher associated with the simulation. def __init__(self): """Initialize a Simulation. @type self: Simulation @rtype: None """ self._events = PriorityQueue() self._dispatcher = Dispatcher() self._monitor = Monitor() def run(self, initial_events): """Run the simulation on the list of events in <initial_events>. Return a dictionary containing statistics of the simulation, according to the specifications in the assignment handout. @type self: Simulation @type initial_events: list[Event] An initial list of events. @rtype: dict[str, object] """ # Add all initial events to the event queue. for item in initial_events: self._events.add(item) # Until there are no more events, remove an event # from the event queue and do it. Add any returned # events to the event queue. while not (self._events.is_empty()): temp_event = self._events.remove() event_list = temp_event.do(self._dispatcher, self._monitor) for event in event_list: self._events.add(event) return self._monitor.report()
def schedule(self, parcels: List[Parcel], trucks: List[Truck], verbose: bool = False) -> List[Parcel]: rejects = [] parcels2 = PriorityQueue(self._func) for p in parcels: parcels2.add(p) while not parcels2.is_empty(): parcel = parcels2.remove() truckos = _greedy_capable_trucks(parcel, trucks) if self._t_priority == 'non-increasing': ordereds = PriorityQueue(_more_truck_space) else: ordereds = PriorityQueue(_less_truck_space) for i in truckos: ordereds.add(trucks[i]) if ordereds.is_empty(): rejects.append(parcel) continue picked = ordereds.remove() for t in trucks: if picked.id == t.id: t.pack(parcel) return rejects
class GroceryStoreSimulation: """A Grocery Store simulation. This is the class which is responsible for setting up and running a simulation. """ def __init__(self, store_file): """Initialize a GroceryStoreSimulation from a file. @type store_file: str A file containing the configuration of the grocery store. @rtype: None """ self._events = PriorityQueue() self._store = GroceryStore(store_file) def run(self, event_file): """Run the simulation on the events stored in <event_file>. Return a dictionary containing statistics of the simulation, according to the specifications in the assignment handout. @type self: GroceryStoreSimulation @type event_file: str A filename referring to a raw list of events. Precondition: the event file is a valid list of events. @rtype: dict[str, object] """ # Initialize statistics stats = { 'num_customers': 0, 'total_time': 0, 'max_wait': -1 } initial_events = create_event_list(event_file) for item in initial_events: self._events.add(item) while self._events.is_empty() == False: processEvent= self._events.remove() #change the stats acrroding to the eventbeing popped out from list if(processEvent.status=="Finish"): waitTime=processEvent.getWaitTime() if waitTime > stats['max_wait']: stats['max_wait'] = waitTime if(processEvent.status=="Begin"): stats['num_customers']+=1 if(self._events.is_empty() and processEvent.status=="Finish"): stats['total_time']=processEvent.timestamp newEvents=processEvent.do(self._store) if(newEvents!=None): for event in newEvents: self._events.add(event) return stats
def run(self, start: datetime, end: datetime) -> None: """Run the simulation from <start> to <end>. """ step = timedelta(minutes=1) # Each iteration spans one minute of time for ride in self.all_rides: PriorityQueue.add(RideStartEvent(ride)) while start <= end: # self._update_active_rides(start) self._update_active_rides_fast(start) if start < end: self.update_availability_and_unoccupied() self.visualizer.render_drawables( list(self.all_stations.values()) + self.active_rides, start) start += step if start == end: self.active_rides = [] # Leave this code at the very bottom of this method. # It will keep the visualization window open until you close # it by pressing the 'X'. while True: if self.visualizer.handle_window_events(): return # Stop the simulation
class GroceryStoreSimulation: """A Grocery Store simulation. This is the class which is responsible for setting up and running a simulation. The interface is given to you: your main task is to implement the two methods according to their docstrings. Of course, you may add whatever private attributes you want to this class. Because you should not change the interface in any way, you may not add any public attributes. === Private Attributes === _events: A sequence of events arranged in priority determined by the event sorting order. _store: The store being simulated. """ _events: PriorityQueue _store: GroceryStore def __init__(self, store_file: TextIO) -> None: """Initialize a GroceryStoreSimulation using configuration <store_file>. """ self._events = PriorityQueue() self._store = GroceryStore(store_file) def run(self, file: TextIO) -> Dict[str, Any]: """Run the simulation on the events stored in <initial_events>. Return a dictionary containing statistics of the simulation, according to the specifications in the assignment handout. """ # Initialize statistics stats = { 'num_customers': 0, 'total_time': 0, 'max_wait': -1 } max_waits = dict() initial_event_list = create_event_list(file) for event in initial_event_list: self._events.add(event) if isinstance(event, CustomerArrival): stats['num_customers'] += 1 max_waits[event.customer] = event.timestamp while not self._events.is_empty(): event = self._events.remove() if isinstance(event, CheckoutCompleted): max_waits[event.customer] = event.timestamp - \ max_waits[event.customer] spawns = event.do(self._store) for spawn in spawns: self._events.add(spawn) stats['total_time'] = event.timestamp stats['max_wait'] = max_waits[max(max_waits, key=max_waits.get)] return stats
class Simulation: """A simulation. This is the class that is responsible for setting up and running a simulation. The API is given to you: your main task is to implement the run method below according to its docstring. Of course, you may add whatever private attributes and methods you want. But because you should not change the interface, you may not add any public attributes or methods. This is the entry point into your program, and in particular is used for auto-testing purposes. This makes it ESSENTIAL that you do not change the interface in any way! """ # === Private Attributes === _events: PriorityQueue # A sequence of events arranged in priority determined by the event # sorting order. _dispatcher: Dispatcher # The dispatcher associated with the simulation. _monitor: Monitor # The monitor associated with the simulation. def __init__(self) -> None: """Initialize a Simulation. """ self._events = PriorityQueue() self._dispatcher = Dispatcher() self._monitor = Monitor() def run(self, initial_events: List[Event]) -> Dict[str, float]: """Run the simulation on the list of events in <initial_events>. Return a dictionary containing statistics of the simulation, according to the specifications in the assignment handout. initial_events: An initial list of events. """ # Add all initial events to the event queue. for event in initial_events: self._events.add(event) # Until there are no more events, remove an event # from the event queue and do it. Add any returned # events to the event queue. while not self._events.is_empty(): new = self._events.remove() future = new.do(self._dispatcher, self._monitor) for i in future: self._events.add(i) return self._monitor.report()
class Simulation: """A simulation. This is the class which is responsible for setting up and running a simulation. The API is given to you: your main task is to implement the run method below according to its docstring. Of course, you may add whatever private attributes and methods you want. But because you should not change the interface, you may not add any public attributes or methods. This is the entry point into your program, and in particular is used for auto-testing purposes. This makes it ESSENTIAL that you do not change the interface in any way! """ # === Private Attributes === # @type _events: PriorityQueue[Event] # A sequence of events arranged in priority determined by the event # sorting order. # @type _dispatcher: Dispatcher # The dispatcher associated with the simulation. def __init__(self): """Initialize a Simulation. @type self: Simulation @rtype: None """ self._events = PriorityQueue() self._dispatcher = Dispatcher() self._monitor = Monitor() def run(self, initial_events): """Run the simulation on the list of events in <initial_events>. Return a dictionary containing statistics of the simulation, according to the specifications in the assignment handout. @type self: Simulation @type initial_events: list[Event] An initial list of events. @rtype: dict[str, object] """ # TODO #Adding the events to the queue, UNSURE ABOUT THIS ???!?? for event in initial_events: self._events.add(event) while not self._events.is_empty(): currentEvent = self._events.remove() additionalEvents = currentEvent.do(self._dispatcher,self._monitor) if additionalEvents != []: for event in additionalEvents: self._events.add(event) # Add all initial events to the event queue. # Until there are no more events, remove an event # from the event queue and do it. Add any returned # events to the event queue. return self._monitor.report()
def schedule(self, parcels: List[Parcel], trucks: List[Truck], verbose: bool = False) -> List[Parcel]: rejects = [] parcels1 = PriorityQueue(_smaller_volume) parcels2 = PriorityQueue(_larger_volume) parcels3 = PriorityQueue(_smaller_city) parcels4 = PriorityQueue(_larger_city) for p in parcels: parcels1.add(p) parcels2.add(p) parcels3.add(p) parcels4.add(p) print("-----------------------") while not parcels1.is_empty(): p = parcels1.remove() print(p.id) print("-----------------------") while not parcels2.is_empty(): p = parcels2.remove() print(p.id) print("-----------------------") while not parcels3.is_empty(): p = parcels3.remove() print(p.id) print("-----------------------") while not parcels4.is_empty(): p = parcels4.remove() print(p.id) print("-----------------------") while not parcels2.is_empty(): parcel = parcels2.remove() truckos = _greedy_capable_trucks(parcel, trucks) if self._t_priority == 'non-increasing': ordereds = PriorityQueue(_more_truck_space) else: ordereds = PriorityQueue(_less_truck_space) for i in truckos: ordereds.add(trucks[i]) if ordereds.is_empty(): rejects.append(parcel) continue picked = ordereds.remove() for t in trucks: if picked.id == t.id: t.pack(parcel) pr return rejects
def schedule(self, parcels: List[Parcel], trucks: List[Truck], verbose: bool = False) -> List[Parcel]: """Returns a list of parcels that were not allocated to a truck due to lack of volume available""" # defining left over parcels and priority list for parcels leftovers = [] parcel_lst = self._parcel_lst_generator() # adding parcels to priority list for parcel in parcels: parcel_lst.add(parcel) # assigning parcels to trucks while not parcel_lst.is_empty(): # removing parcels from the remaining parcels to be # allocated parcel = parcel_lst.remove() # defining truck priority list with two separate lists indicating # routes that match-up vs not if self._truck_order == "non-decreasing": truck_lst = PriorityQueue(_truck_vol_least) truck_lst_high = PriorityQueue(_truck_vol_least) else: # self._truck_order == "non-increasing": truck_lst = PriorityQueue(_truck_vol_most) truck_lst_high = PriorityQueue(_truck_vol_most) # making available truck lists for current parcel for truck in trucks: # check if parcel will fit current_vol = truck.unused_space() valid = current_vol >= parcel.volume # if parcel fits and the truck's last current stop is the # parcel's destination prioritize this truck over any others if truck.route[-1] == parcel.destination and valid: truck_lst_high.add(truck) # if parcel fits add this truck to list elif valid: truck_lst.add(truck) # if the high_prio list not is empty (i.e. there is a truck # with the last route as its destination) add the parcel # to the most prioritized truck if not truck_lst_high.is_empty(): truck_lst_high.remove().pack(parcel) # if not same route but eligibble trucks add it to # the most prioritzed truck elif not truck_lst.is_empty(): truck_lst.remove().pack(parcel) # otherwise append to leftover parcels else: leftovers.append(parcel) return leftovers
def schedule(self, parcels, trucks, verbose=False): """ Schedule parcels greedily <trucks> are mutated. Do not reuse <trucks> for another scheduler/trial. === Local Variables === type queue: PriorityQueue Queue of parcels. type unused_parcel: [Parcel] If the parcel does not fit any truck, the parcel is appended to <unused_parcel> type one_parcel: Parcel An element of <parcels> or <queue> type trucks_with_space: [Truck] List of trucks with space """ queue = PriorityQueue(self._greater_priority) unused_parcel = [] for one_parcel in parcels: queue.add(one_parcel) while queue.is_empty() is False: one_parcel = queue.remove() trucks_with_space = [] for one_truck in trucks: if one_truck.get_unused_space() >= one_parcel.get_volume(): trucks_with_space.append(one_truck) trucks_with_destination = [] for one_truck in trucks_with_space: if one_truck.city_in_route(one_parcel.get_destination()): trucks_with_destination.append(one_truck) # if there is at least 1 suitable truck with the destination, # trucks without the destination will not be considered. if len(trucks_with_destination) > 0: trucks_with_space = trucks_with_destination if len(trucks_with_space) > 0: self._choose_load_truck(trucks_with_space, one_parcel, verbose) else: if verbose: print("Parcel #{} was not loaded.".format( one_parcel.get_id())) unused_parcel.append(one_parcel) return unused_parcel
def schedule(self, parcels, trucks, verbose=False): """Mutate the trucks so that they store information about which parcels they will deliver and what route they will take. The parcels will not be mutated. Return the parcels that do not get scheduled onto any truck, due to lack of capacity. @type self: Scheduler @type parcels: list[Parcel] The parcels to be scheduled for delivery. @type trucks: list[Truck] The trucks that can carry parcels for delivery. @type verbose: bool Whether or not to run in verbose mode. @rtype: list[Parcel] The parcels that did not get scheduled onto any truck, due to lack of capacity. """ parcel_queue = PriorityQueue(self._fun1) for each in parcels: parcel_queue.add(each) while not parcel_queue.is_empty(): target_parcel = parcel_queue.remove() parcel_data = target_parcel.get_parcel() truck_1st = [] truck_2nd = [] for each in trucks: if each.get_volume() >= parcel_data[-1]: truck_1st.append(each) for i in truck_1st: if parcel_data[-2] in i.get_destination(): truck_2nd.append(i) if not len(truck_2nd): truck_2nd = truck_1st truck_queue = PriorityQueue(self._fun2) for j in truck_2nd: truck_queue.add(j) if not truck_queue.is_empty(): truck_choice = truck_queue.remove() indx = trucks.index(truck_choice) trucks.remove(truck_choice) truck_choice.load_parcel(parcel_data[0], parcel_data[-2], parcel_data[-1]) parcels.remove(target_parcel) trucks.insert(indx, truck_choice) return parcels
def test_priority_truck_non_decreasing_destination() -> None: """Test the doctest provided for PriorityQueue.add and PriorityQueue.remove""" t1 = Truck(1, 25, 'York') t2 = Truck(2, 10, 'York') t3 = Truck(3, 8, 'York') t4 = Truck(4, 20, 'York') t5 = Truck(5, 15, 'York') t6 = Truck(6, 15, 'York') t7 = Truck(7, 20, 'York') p1 = Parcel(1, 15, 'York', 'Toronto') trucks = [t1, t2, t3, t4, t5, t6, t7] et = _capable_trucks(p1, trucks) assert len(et) == 5 pq = PriorityQueue(_less_truck_space) for t in et: pq.add(trucks[t]) truck = pq.remove() assert truck.id == 5
def _generate_queue(_parcel_priority: str, _parcel_order: str, parcels: List[Parcel]) -> PriorityQueue: """Return a PriorityQueue that follows the given <_parcel_priority> and given <_parcel_order>. """ q = 'Nothing Assigned' if _parcel_priority == "volume": if _parcel_order == "non-decreasing": q = PriorityQueue(_volume_non_decreasing) elif _parcel_order == "non-increasing": q = PriorityQueue(_volume_non_increasing) elif _parcel_priority == "destination": if _parcel_order == "non-decreasing": q = PriorityQueue(_destination_non_decreasing) elif _parcel_order == "non-increasing": q = PriorityQueue(_destination_non_increasing) for parcel in parcels: q.add(parcel) return q
def test_priority_queue_non_increasing_destination() -> None: """Test the doctest provided for PriorityQueue.add and PriorityQueue.remove""" p1 = Parcel(1, 25, 'York', 'a') p2 = Parcel(2, 10, 'York', 'aa') p3 = Parcel(3, 8, 'York', 'aaa') p4 = Parcel(4, 20, 'York', 'a') p5 = Parcel(5, 15, 'York', 'aaaa') p6 = Parcel(6, 15, 'York', 'aa') p7 = Parcel(7, 20, 'York', 'aaaaaa') parcels = [p1, p2, p3, p4, p5, p6, p7] pq = PriorityQueue(_smaller_city) for parcel in parcels: pq.add(parcel) assert pq.remove().id == 7 assert pq.remove().id == 5 assert pq.remove().id == 3 assert pq.remove().id == 2 assert pq.remove().id == 6 assert pq.remove().id == 1 assert pq.remove().id == 4
def test_priority_queue_non_decreasing_volume() -> None: """Test the doctest provided for PriorityQueue.add and PriorityQueue.remove""" p1 = Parcel(1, 25, 'York', 'Toronto') p2 = Parcel(2, 10, 'York', 'London') p3 = Parcel(3, 8, 'York', 'London') p4 = Parcel(4, 20, 'York', 'Toronto') p5 = Parcel(5, 15, 'York', 'Toronto') p6 = Parcel(6, 15, 'York', 'Hamilton') p7 = Parcel(7, 20, 'York', 'London') parcels = [p1, p2, p3, p4, p5, p6, p7] pq = PriorityQueue(_smaller_volume) for parcel in parcels: pq.add(parcel) assert pq.remove().volume == 8 assert pq.remove().volume == 10 assert pq.remove().volume == 15 assert pq.remove().volume == 15 assert pq.remove().volume == 20 assert pq.remove().volume == 20 assert pq.remove().volume == 25
def test_priority_queue_add_remove_doctest() -> None: """Test the doctest provided for PriorityQueue.add and PriorityQueue.remove""" pq = PriorityQueue(_shorter) pq.add('fred') pq.add('arju') pq.add('monalisa') pq.add('hat') assert pq.remove() == 'hat' assert pq.remove() == 'fred' assert pq.remove() == 'arju' assert pq.remove() == 'monalisa'
class TestPriorityQueue(unittest.TestCase): def setUp(self): self.queue = PriorityQueue(Helper.shorter) self.queue.add('fred') self.queue.add('arju') self.queue.add('monalisa') self.queue.add('hat') def test_pq_add(self): actual = self.queue._queue expected = ['monalisa', 'arju', 'fred', 'hat'] msg = "We expected {}, but found {}".format(str(expected), str(actual)) self.assertEqual(actual, expected, msg) def test_pq_remove(self): pq = self.queue._queue actual = self.queue.remove() expected = 'hat' msg = "Applied remove() to {}, expected {}, actual {}".format(pq, expected, actual) self.assertEqual(actual, expected, msg)
class GroceryStoreSimulation: """A Grocery Store simulation. This is the class which is responsible for setting up and running a simulation. The API is given to you: your main task is to implement the two methods according to their docstrings. Of course, you may add whatever private attributes and methods you want. But because you should not change the interface, you may not add any public attributes or methods. This is the entry point into your program, and in particular is used for autotesting purposes. This makes it ESSENTIAL that you do not change the interface in any way! """ # === Private Attributes === # @type _events: PriorityQueue[Event] # A sequence of events arranged in priority determined by the event # sorting order. # @type _store: GroceryStore # The grocery store associated with the simulation. def __init__(self, store_file): """Initialize a GroceryStoreSimulation from a file. @type store_file: str A file containing the configuration of the grocery store. @rtype: None """ self._events = PriorityQueue() self._store = GroceryStore(store_file) def run(self, event_file): """Run the simulation on the events stored in <event_file>. Return a dictionary containing statistics of the simulation, according to the specifications in the assignment handout. @type self: GroceryStoreSimulation @type event_file: str A filename referring to a raw list of events. Precondition: the event file is a valid list of events. @rtype: dict[str, object] """ # Initialize statistics stats = { 'num_customers': 0, 'total_time': 0, 'max_wait': -1 } initial_events = create_event_list(event_file) # TODO: Process all of the events, collecting statistics along the way. for event in initial_events: self._events.add(event) while not self._events.is_empty(): current_event = self._events.remove() new_events = current_event.do(self._store) stats['total_time'] = current_event.timestamp # collects the total time if new_events is None: pass elif len(new_events) > 0: for x in new_events: self._events.add(x) # find out how many Customers have checked out # through store's finished_customers dict stats['num_customers'] = len(self._store.finished_customers) # find out max_wait from store's finished_customers for customer in self._store.finished_customers: if self._store.finished_customers[customer].get_wait_time() > \ stats['max_wait']: stats['max_wait'] = self._store.finished_customers[ customer].get_wait_time() return stats
class Simulation: """Runs the core of the simulation through time. === Attributes === all_rides: A list of all the rides in this simulation. Note that not all rides might be used, depending on the timeframe when the simulation is run. all_stations: A dictionary containing all the stations in this simulation. visualizer: A helper class for visualizing the simulation. active rides: A list o all rides that are in progress at the current time in the simulation """ all_stations: Dict[str, Station] all_rides: List[Ride] visualizer: Visualizer active_rides: List[Ride] event_list: List['Event'] def __init__(self, station_file: str, ride_file: str) -> None: """Initialize this simulation with the given configuration settings. """ self.visualizer = Visualizer() self.all_stations = create_stations(station_file) self.all_rides = create_rides(ride_file, self.all_stations) self.active_rides = [] self.event_list = PriorityQueue() def run(self, start: datetime, end: datetime) -> None: """Run the simulation from <start> to <end>. """ step = timedelta(minutes=1) # Each iteration spans one minute of time for ride in self.all_rides: if ride.start_time > start or start <= ride.end_time <= end: start_event = RideStartEvent(self, ride.start_time) self.event_list.add(start_event) simulation_start = start while not start > end: self._update_active_rides_fast(start) if not start == simulation_start: self.low_avail_check() self.low_occ_check() drawables = self.active_rides + list(self.all_stations.values()) self.visualizer.render_drawables(drawables + self.active_rides, start) start += step # Leave this code at the very bottom of this method. # It will keep the visualization window open until you close # it by pressing the 'X'. while True: if self.visualizer.handle_window_events(): return # Stop the simulation def low_avail_check(self): """Check whether station's current state is "low available". If it is update station's low availability time """ for id_ in self.all_stations: station = self.all_stations[id_] if station.is_low_availability(): station.low_avail += 60.0 def low_occ_check(self): """Check whether station's current state is "low unoccupied." If it is update station's low availability time """ for id_ in self.all_stations: station = self.all_stations[id_] if station.is_low_unoccupied(): station.low_occ += 60.0 def _update_active_rides(self, time: datetime) -> None: """Update this simulation's list of active rides for the given time. REQUIRED IMPLEMENTATION NOTES: - Loop through `self.all_rides` and compare each Ride's start and end times with <time>. If <time> is between the ride's start and end times (inclusive), then add the ride to self.active_rides if it isn't already in that list. Otherwise, remove the ride from self.active_rides if it is in that list. - This means that if a ride started before the simulation's time period but ends during or after the simulation's time period, it should still be added to self.active_rides. """ for ride in self.all_rides: if ride.start_time <= time <= ride.end_time: if ride not in self.active_rides: if not ride.start.num_bikes == 0: self.active_rides.append(ride) ride.start.num_bikes -= 1 ride.start.rides_start += 1 else: self.all_rides.remove(ride) else: if ride in self.active_rides: if not ride.end.is_full(): ride.end.num_bikes += 1 ride.end.rides_end += 1 self.active_rides.remove(ride) def calculate_statistics(self) -> Dict[str, Tuple[str, float]]: """Return a dictionary containing statistics for this simulation. The returned dictionary has exactly four keys, corresponding to the four statistics tracked for each station: - 'max_start' - 'max_end' - 'max_time_low_availability' - 'max_time_low_unoccupied' The corresponding value of each key is a tuple of two elements, where the first element is the name (NOT id) of the station that has the maximum value of the quantity specified by that key, and the second element is the value of that quantity. For example, the value corresponding to key 'max_start' should be the name of the station with the most number of rides started at that station, and the number of rides that started at that station. """ return { 'max_start': (self.max_start()), 'max_end': (self.max_end()), 'max_time_low_availability': (self.lowest_avail()), 'max_time_low_unoccupied': (self.lowest_occ()) } def max_start(self): """Return the station and the amount with most rides that starts there """ max_ = -1 name = '' for id_ in self.all_stations: station = self.all_stations[id_] if station.rides_start > max_: max_ = station.rides_start name = station.name elif station.rides_start == max_: if station.name < name: name = station.name return name, max_ def max_end(self): """Return the station and the amount with most rides that starts there """ max_ = -1 name = '' for id_ in self.all_stations: station = self.all_stations[id_] if station.rides_end > max_: max_ = station.rides_end name = station.name elif station.rides_end == max_: if station.name < name: name = station.name return name, max_ def lowest_avail(self): """Return the station with longest time in the state of "low availability" and it's time """ avail = -1 name = '' for id_ in self.all_stations: station = self.all_stations[id_] if station.low_avail > avail: avail = station.low_avail name = station.name elif station.low_avail == avail: if station.name < name: name = station.name return name, avail def lowest_occ(self): """Return the station with longest time in the state of "low unoccupied" and it's time """ occ = -1 name = '' for id_ in self.all_stations: station = self.all_stations[id_] if station.low_occ > occ: occ = station.low_occ name = station.name elif station.low_occ == occ: if station.name < name: name = station.name return name, occ def _update_active_rides_fast(self, time: datetime) -> None: """Update this simulation's list of active rides for the given time. REQUIRED IMPLEMENTATION NOTES: - see Task 5 of the assignment handout """ if not self.event_list.is_empty(): event = self.event_list.remove() if event.time > time: self.event_list.add(event) else: while event is not None and event.time == time: new_events = event.process() for event in new_events: self.event_list.add(event) event = None if not self.event_list.is_empty(): event = self.event_list.remove() if event.time > time: self.event_list.add(event)
class Simulation: """Runs the core of the simulation through time. === Attributes === all_rides: A list of all the rides in this simulation. Note that not all rides might be used, depending on the timeframe when the simulation is run. all_stations: A dictionary containing all the stations in this simulation. visualizer: A helper class for visualizing the simulation. active_rides A list that contains the active rides that progess currently in the time of the simulation """ all_stations: Dict[str, Station] all_rides: List[Ride] visualizer: Visualizer active_rides: List[Ride] event_priority: PriorityQueue['Event'] def __init__(self, station_file: str, ride_file: str) -> None: """Initialize this simulation with the given configuration settings. """ self.visualizer = Visualizer() self.all_stations = create_stations(station_file) self.all_rides = create_rides(ride_file, self.all_stations) self.active_rides = [] self.event_priority = PriorityQueue() def run(self, start: datetime, end: datetime) -> None: """Run the simulation from <start> to <end>. """ for ride in self.all_rides: if ride.start_time >= start: self.event_priority.add( RideStartEvent(self, ride.start_time, ride)) step = timedelta(minutes=1) # Each iteration spans one minute of time while start <= end: #self._update_active_rides_fast(start) self._update_active_rides(start) if start < end: self.update_availability_and_unoccupied() self.visualizer.render_drawables( list(self.all_stations.values()) + self.active_rides, start) start += step # if start == end: # self.active_rides = [] # Leave this code at the very bottom of this method. # It will keep the visualization window open until you close # it by pressing the 'X'. while True: if self.visualizer.handle_window_events(): return # Stop the simulation def update_availability_and_unoccupied(self): """ updates the stations availability and unoccupied method per minute """ for station in self.all_stations.values(): station.low_availabilty() station.low_unoccupied() def update_giving_station(self, ride: Ride, time: datetime) -> None: """Decrease the num_bikes of a station if the ride is being added to the active rides list """ if ride.start_time == time: if ride.start.num_bikes > 0: ride.start.num_bikes -= 1 def update_taking_station(self, ride: Ride, time: datetime) -> None: """Increase the num_bikes of a station if the ride is being added to the active rides list only if we haven't exceeded the station capacity yet.""" if ride.end_time == time: if ride.end.capacity > ride.end.num_bikes: ride.end.num_bikes += 1 def _update_active_rides_fast(self, time: datetime) -> None: """Update this simulation's list of active rides for the given time. REQUIRED IMPLEMENTATION NOTES: - see Task 5 of the assignment handout """ while not self.event_priority.is_empty(): current_event = self.event_priority.remove() if current_event.ride.start_time >= time: self.event_priority.add(current_event) return else: self.event_priority.add(current_event.process) def _update_active_rides(self, time: datetime) -> None: """Update this simulation's list of active rides for the given time. REQUIRED IMPLEMENTATION NOTES: - Loop through `self.all_rides` and compare each Ride's start and end times with <time>. If <time> is between the ride's start and end times (inclusive), then add the ride to self.active_rides if it isn't already in that list. Otherwise, remove the ride from self.active_rides if it is in that list. - This means that if a ride started before the simulation's time period but ends during or after the simulation's time period, it should still be added to self.active_rides. """ for current_ride in self.all_rides: ride_station_start = current_ride.start ride_station_end = current_ride.end s = current_ride.start_time e = current_ride.end_time if time == s and time < e: if ride_station_start.num_bikes > 0 and ride_station_start. \ unocc_spots != ride_station_start.capacity: ride_station_start.num_bikes -= 1 ride_station_start.stats['starting rides'] += 1 ride_station_start.unocc_spots += 1 self.active_rides.append(current_ride) if time == e and current_ride in self.active_rides: if ride_station_end.num_bikes < ride_station_end.capacity and \ ride_station_end.unocc_spots > 0: ride_station_end.stats['ending rides'] += 1 ride_station_end.num_bikes += 1 ride_station_end.unocc_spots -= 1 self.active_rides.remove(current_ride) else: self.active_rides.remove(current_ride) def calculate_statistics(self) -> Dict[str, Tuple[str, float]]: """Return a dictionary containing statistics for this simulation. The returned dictionary has exactly four keys, corresponding to the four statistics tracked for each station: - 'max_start' - 'max_end' - 'max_time_low_availability' - 'max_time_low_unoccupied' The corresponding value of each key is a tuple of two elements, where the first element is the name (NOT id) of the station that has the maximum value of the quantity specified by that key, and the second element is the value of that quantity. For example, the value corresponding to key 'max_start' should be the name of the station with the most number of rides started at that station, and the number of rides that started at that station. """ max_start = -1 max_start_station_name = None max_end = -1 max_end_station_name = None max_avail = -1 max_avail_station_name = None max_unocc = -1 max_unocc_station_name = None for station in self.all_stations.values(): if station.stats['starting rides'] >= max_start: if station.stats['starting rides'] > max_start: max_start = station.stats['starting rides'] max_start_station_name = station.name elif station.stats['starting rides'] == max_start: if station.name < max_start_station_name: max_start_station_name = station.name if station.stats['ending rides'] >= max_end: if station.stats['ending rides'] > max_end: max_end = station.stats['ending rides'] max_end_station_name = station.name elif station.stats['ending rides'] == max_start: if station.name < max_end_station_name: max_end_station_name = station.name if station.stats['low availability'] >= max_avail: if station.stats['low availability'] > max_avail: max_avail_station_name = station.name max_avail = station.stats['low availability'] elif station.stats['low availability'] == max_avail: if station.name < max_avail_station_name: max_avail_station_name = station.name if station.stats['low unoccupied'] >= max_unocc: if station.stats['low unoccupied'] > max_unocc: max_unocc_station_name = station.name max_unocc = station.stats['low unoccupied'] elif station.stats['low unoccupied'] == max_unocc: if station.name < max_unocc_station_name: max_unocc_station_name = station.name return { 'max_start': (max_start_station_name, max_start), 'max_end': (max_end_station_name, max_end), 'max_time_low_availability': (max_avail_station_name, max_avail), 'max_time_low_unoccupied': (max_unocc_station_name, max_unocc) }
class Simulation: """Runs the core of the simulation through time. === Attributes === all_rides: A list of all the rides in this simulation. Note that not all rides might be used, depending on the timeframe when the simulation is run. all_stations: A dictionary containing all the stations in this simulation. visualizer: A helper class for visualizing the simulation. active_rides: A list to keep track of the rides that are in progress at the current time in the simulation. queue: A queue to keep track of of when rides become active or inactive. """ all_stations: Dict[str, Station] all_rides: List[Ride] visualizer: Visualizer active_rides: List[Ride] queue: PriorityQueue def __init__(self, station_file: str, ride_file: str) -> None: """Initialize this simulation with the given configuration settings. """ self.visualizer = Visualizer() self.all_stations = create_stations(station_file) self.all_rides = create_rides(ride_file, self.all_stations) self.active_rides = [] self.queue = PriorityQueue() def run(self, start: datetime, end: datetime) -> None: """Run the simulation from <start> to <end>. """ step = timedelta(minutes=1) # Each iteration spans one minute of time # Add the rides into the queue. for r in self.all_rides: if r.start_time <= end and r.end_time >= start: ride_start = RideStartEvent(self, r.start_time, r) self.queue.add(ride_start) # Main run simulation loop. current_time = start while current_time <= end: # Update the rides. self._update_active_rides_fast(current_time) # Draw the map, the rides, and the stations. everything_list = \ list(self.all_stations.values()) + self.active_rides self.visualizer.render_drawables(everything_list, current_time) # Add to the time spent with low number of bikes and/or low number # of vacant spot <= 5. for s in self.all_stations: if self.all_stations[s].check_num_bikes() \ and current_time < end: self.all_stations[s].time_low_bikes += step.total_seconds() if self.all_stations[s].check_vacancy() \ and current_time < end: self.all_stations[s].time_low_vacant += step.total_seconds( ) current_time += step # Leave this code at the very bottom of this method. # It will keep the visualization window open until you close # it by pressing the 'X'. while True: if self.visualizer.handle_window_events(): return # Stop the simulation def _update_active_rides(self, time: datetime) -> None: """Update this simulation's list of active rides for the given time. REQUIRED IMPLEMENTATION NOTES: - Loop through `self.all_rides` and compare each Ride's start and end times with <time>. If <time> is between the ride's start and end times (inclusive), then add the ride to self.active_rides if it isn't already in that list. Otherwise, remove the ride from self.active_rides if it is in that list. - This means that if a ride started before the simulation's time period but ends during or after the simulation's time period, it should still be added to self.active_rides. """ for r in self.all_rides: if r.start_time <= time <= r.end_time: if r not in self.active_rides and r.start.num_bikes > 0: r.add_leave(time) self.active_rides.append(r) elif r.end_time <= time: if r in self.active_rides: r.add_arrive() self.active_rides.remove(r) def calculate_statistics(self) -> Dict[str, Tuple[str, float]]: """Return a dictionary containing statistics for this simulation. The returned dictionary has exactly four keys, corresponding to the four statistics tracked for each station: - 'max_start' - 'max_end' - 'max_time_low_availability' - 'max_time_low_unoccupied' The corresponding value of each key is a tuple of two elements, where the first element is the name (NOT id) of the station that has the maximum value of the quantity specified by that key, and the second element is the value of that quantity. For example, the value corresponding to key 'max_start' should be the name of the station with the most number of rides started at that station, and the number of rides that started at that station. """ # Create empty dictionaries to store the neccessary values from the # stations. d_start = {} d_end = {} d_low_availability = {} d_low_unoccupied = {} # Use stations'name as the dictionaries' key. # The value of each dictionary will be the amount of bike leave, # the amount of bike arrive, the amount of time spent with low number of # bikes and the amount of unoccupied spot of each stations respectively. for s in self.all_stations: current_name = self.all_stations[s].name d_start[current_name] = self.all_stations[s].leave d_end[current_name] = self.all_stations[s].arrive d_low_availability[current_name] = \ self.all_stations[s].time_low_bikes d_low_unoccupied[current_name] = \ self.all_stations[s].time_low_vacant # Find the maximum values, store them into these varibles respectively: # - 'max_start' # - 'max_end' # - 'max_time_low_availability' # - 'max_time_low_unoccupied' # and return them in form of a Dict[str, Tuple[str, float]]. max_start = find_maximum(d_start) max_end = find_maximum(d_end) max_time_low_availability = find_maximum(d_low_availability) max_time_low_unoccupied = find_maximum(d_low_unoccupied) return { 'max_start': max_start, 'max_end': max_end, 'max_time_low_availability': max_time_low_availability, 'max_time_low_unoccupied': max_time_low_unoccupied } def _update_active_rides_fast(self, time: datetime) -> None: """Update this simulation's list of active rides for the given time. REQUIRED IMPLEMENTATION NOTES: - see Task 5 of the assignment handout """ # Loop through the queue while not self.queue.is_empty(): event = self.queue.remove() if event.time > time: self.queue.add(event) break else: event_num_bikes = event.ride.start.num_bikes if isinstance(event, RideStartEvent) and event_num_bikes > 0: event.ride.add_leave(time) end_event = event.process() self.queue.add(end_event[0]) if isinstance(event, RideEndEvent): event.process()
class Dispatcher: """A dispatcher fulfills requests from riders and drivers for a ride-sharing service. When a rider requests a driver, the dispatcher assigns a driver to the rider. If no driver is available, the rider is placed on a waiting list for the next available driver. A rider that has not yet been picked up by a driver may cancel their request. When a driver requests a rider, the dispatcher assigns a rider from the waiting list to the driver. If there is no rider on the waiting list the dispatcher does nothing. Once a driver requests a rider, the driver is registered with the dispatcher, and will be used to fulfill future rider requests. """ def __init__(self): """Initialize a Dispatcher. @type self: Dispatcher @rtype: None """ self.driver_list = [] self.rider_queue = PriorityQueue() def __str__(self): """Return a string representation. @type self: Dispatcher @rtype: str """ print("Drivers: ",end="") print(self.driver_list) def request_driver(self, rider): """Return a driver for the rider, or None if no driver is available. Add the rider to the waiting list if there is no available driver. @type self: Dispatcher @type rider: Rider @rtype: Driver | None """ if len(self.driver_list) == 0: self.rider_queue.add(rider) return None else: shortest_time = self.driver_list[0] for name in self.driver_list: if name.get_travel_time(rider.destination) < shortest_time.get_travel_time(rider.destination): if name.is_idle: shortest_time = name return shortest_time def request_rider(self, driver): """Return a rider for the driver, or None if no rider is available. If this is a new driver, register the driver for future rider requests. @type self: Dispatcher @type driver: Driver @rtype: Rider | None """ if not self.rider_queue.is_empty(): return self.rider_queue.remove() else: self.driver_list.append(driver) return None def cancel_ride(self, rider): """Cancel the ride for rider. @type self: Dispatcher @type rider: Rider @rtype: None """ if self.rider_queue.__contains__(rider): self.rider_queue.delete(rider) rider.status = CANCELLED
class GroceryStoreSimulation: """A Grocery Store simulation. This is the class which is responsible for setting up and running a simulation. The API is given to you: your main task is to implement the two methods according to their docstrings. Of course, you may add whatever private attributes and methods you want. But because you should not change the interface, you may not add any public attributes or methods. This is the entry point into your program, and in particular is used for autotesting purposes. This makes it ESSENTIAL that you do not change the interface in any way! """ # === Private Attributes === # @type _events: PriorityQueue[Event] # A sequence of events arranged in priority determined by the event # sorting order. # @type _store: GroceryStore # The grocery store associated with the simulation. def __init__(self, store_file): """Initialize a GroceryStoreSimulation from a file. @type store_file: str A file containing the configuration of the grocery store. @rtype: None """ self._events = PriorityQueue() self._store = GroceryStore(store_file) def run(self, event_file): """Run the simulation on the events stored in <event_file>. Return a dictionary containing statistics of the simulation, according to the specifications in the assignment handout. @type self: GroceryStoreSimulation @type event_file: str A filename referring to a raw list of events. Precondition: the event file is a valid list of events. @rtype: dict[str, object] """ # Initialize statistics stats = { 'num_customers': 0, 'total_time': 0, 'max_wait': -1 } initial_events = create_event_list(event_file) # TODO: Process all of the events, collecting statistics along the way. #add events to self._events for event in initial_events: self._events.add(event) while not self._events.is_empty(): event = self._events.remove() if isinstance(event,EventJoin): new_events = EventJoin.do() elif isinstance(event,EventClose): new_events = EventClose.do() elif isinstance(event,EventBegin): new_events = EventBegin.do() elif isinstance(event,EventFinish): new_events = EventFinish.do() for item in new_events: self._events.add(new_events) return stats
class Simulation: """Runs the core of the simulation through time. === Attributes === all_rides: A list of all the rides in this simulation. Note that not all rides might be used, depending on the timeframe when the simulation is run. all_stations: A dictionary containing all the stations in this simulation. visualizer: A helper class for visualizing the simulation. active_rides: A list of the active rides in the simulation event_queue: A priority queue of the events to be processed in the simulation """ all_stations: Dict[str, Station] all_rides: List[Ride] visualizer: Visualizer active_rides: List[Ride] event_queue: PriorityQueue def __init__(self, station_file: str, ride_file: str) -> None: """Initialize this simulation with the given configuration settings. """ self.all_stations = create_stations(station_file) self.all_rides = create_rides(ride_file, self.all_stations) self.active_rides = [] self.visualizer = Visualizer() self.event_queue = PriorityQueue() def run(self, start: datetime, end: datetime) -> None: """Run the simulation from <start> to <end>. """ current_time = start step = timedelta(minutes=1) # Each iteration spans one minute of time station_list = list(self.all_stations.values()) for ride in self.all_rides: if (ride.end_time > start and ride.end_time < end) or \ (ride.start_time < end and ride.start_time > start): self.event_queue.add(RideStartEvent(self, ride, start, end)) while current_time != end: self._update_status(current_time) self.visualizer.render_drawables(station_list + \ self.active_rides, current_time) current_time += step # Leave this code at the very bottom of this method. # It will keep the visualization window open until you close # it by pressing the 'X'. while True: if self.visualizer.handle_window_events(): return # Stop the simulation def _update_active_rides(self, time: datetime) -> None: """Update this simulation's list of active rides for the given time. REQUIRED IMPLEMENTATION NOTES: - Loop through `self.all_rides` and compare each Ride's start and end times with <time>. If <time> is between the ride's start and end times (inclusive), then add the ride to self.active_rides if it isn't already in that list. Otherwise, remove the ride from self.active_rides if it is in that list. - This means that if a ride started before the simulation's time period but ends during or after the simulation's time period, it should still be added to self.active_rides. """ for ride in self.active_rides: if ride.end_time == time: ride.end.update_bikes(1) self.active_rides.remove(ride) for ride in self.all_rides: if ride.start_time == time and ride not in self.active_rides: if ride.start.update_bikes(-1): self.active_rides.append(ride) def _update_status(self, time: datetime): """Wrapper method for updating this simulation's rides and station """ self._update_active_rides_fast(time) for station in list(self.all_stations.values()): station.update_time() def calculate_statistics(self) -> Dict[str, Tuple[str, float]]: """Return a dictionary containing statistics for this simulation. The returned dictionary has exactly four keys, corresponding to the four statistics tracked for each station: - 'max_start' - 'max_end' - 'max_time_low_availability' - 'max_time_low_unoccupied' The corresponding value of each key is a tuple of two elements, where the first element is the name (NOT id) of the station that has the maximum value of the quantity specified by that key, and the second element is the value of that quantity. For example, the value corresponding to key 'max_start' should be the name of the station with the most number of rides started at that station, and the number of rides that started at that station. """ stats = [] for station in list(self.all_stations.values()): stats.append(station.get_stats()) # sort list by station name stats = sorted(stats, key=lambda station: station[4]) sorted_lists = [] # a list of lists sorted by: max_start, max_end, # max_time_low_availability, max_time_low_unoccupied sorted_lists.append(sorted(stats, key=lambda station: station[0])) sorted_lists.append(sorted(stats, key=lambda station: station[1])) sorted_lists.append(sorted(stats, key=lambda station: station[2])) sorted_lists.append(sorted(stats, key=lambda station: station[3])) index = 0 for sorted_list in sorted_lists: # for each of the four statistics, remove the ones with the largest # but also equal values,leaving the station with the "smallest" name while len(sorted_list) > 1: if sorted_list[-1][index] == sorted_list[-2][index]: sorted_list.pop(-1) else: break index += 1 max_start = sorted_lists[0][-1] max_end = sorted_lists[1][-1] max_time_low_availability = sorted_lists[2][-1] max_time_low_unoccupied = sorted_lists[3][-1] return { 'max_start': (max_start[4], max_start[0]), 'max_end': (max_end[4], max_end[1]), 'max_time_low_availability': (max_time_low_availability[4] \ , max_time_low_availability[2]), 'max_time_low_unoccupied': (max_time_low_unoccupied[4] \ , max_time_low_unoccupied[3]) } def _update_active_rides_fast(self, time: datetime) -> None: """Update this simulation's list of active rides for the given time. REQUIRED IMPLEMENTATION NOTES: - see Task 5 of the assignment handout """ done = False while not done and not self.event_queue.is_empty(): event = self.event_queue.remove() if event.time > time: self.event_queue.add(event) done = True else: events = event.process() for event in events: self.event_queue.add(event)
def find_path(self, start_node, target_node): """ Implement the A-star path search algorithm If you will add a new node to the path, don't forget to set the parent. You can find an example in the docstring of Node class Please note the shortest path between two nodes may not be unique. However all of them have same length! @type self: Grid @type start_node: Node The starting node of the path @type target_node: Node The target node of the path @rtype: None >>> g = Grid("", ["B.++", ".+..", "...T"]) >>> g.find_path(g.boat, g.treasure) >>> n = g.treasure >>> print(n.grid_x, n.grid_y, ... n.gcost, n.hcost, n.fcost(), ... n.parent.grid_x, n.parent.grid_y) 3 2 38 0 38 2 1 >>> n = g.treasure.parent >>> print(n.grid_x, n.grid_y, ... n.gcost, n.hcost, n.fcost(), ... n.parent.grid_x, n.parent.grid_y) 2 1 24 14 38 1 0 >>> n = g.map[1][0] >>> print(n.grid_x, n.grid_y, ... n.gcost, n.hcost, n.fcost(), ... n.parent.grid_x, n.parent.grid_y) 1 0 10 28 38 0 0 >>> n = g.map[0][1].parent >>> n == g.boat True """ # TODO def less_than(x_node, y_node): """ Compare the priority of x over y @type x_node: Node @type y_node: Node @rtype: bool True if x has higher priority over y otherwise False """ return x_node < y_node # create an empty list as open set open_set = [] # loop the row on the map for row in self.map: # extend the row in the list open_set.extend(row) # remove the start node from the list open_set.remove(start_node) # define a closed set as the priority queue in less_than closed_set = PriorityQueue(less_than) # add the start node to the list of closed set closed_set.add(start_node) # set the gcost for start node start_node.set_gcost(0) # loop the closed set if it is not empty while not closed_set.is_empty(): # get the current node from the closed set curr_node = closed_set.remove() # if the current node is the target node if curr_node is target_node: # done break # loop the next node in the neighours for current node for next_node in self.get_neighours(curr_node): # if the next node is in the open set if next_node in open_set: # remove the next node open_set.remove(next_node) # add it to the closed set closed_set.add(next_node) # set the gcost for the next node next_node.set_gcost(curr_node.gcost + curr_node.distance(next_node)) # set the hcost for the next_node next_node.set_hcost(next_node.distance(target_node)) # set the parent for the next node next_node.set_parent(curr_node)
class Simulation: """A simulation. This is the class which is responsible for setting up and running a simulation. The API is given to you: your main task is to implement the run method below according to its docstring. Of course, you may add whatever private attributes and methods you want. But because you should not change the interface, you may not add any public attributes or methods. This is the entry point into your program, and in particular is used for auto-testing purposes. This makes it ESSENTIAL that you do not change the interface in any way! """ # === Private Attributes === # @type _events: PriorityQueue[Event] # A sequence of events arranged in priority determined by the event # sorting order. # @type _dispatcher: Dispatcher # The dispatcher associated with the simulation. # @type _monitor: monitor # The monitor which is used to record the events def __init__(self): """Initialize a Simulation. @type self: Simulation @rtype: None """ self._events = PriorityQueue() self._dispatcher = Dispatcher() self._monitor = Monitor() def run(self, initial_events): """Run the simulation on the list of events in <initial_events>. Return a dictionary containing statistics of the simulation, according to the specifications in the assignment handout. @type self: Simulation @type initial_events: list[Event] An initial list of events. @rtype: dict[str, object] >>> event_list_1 = [RiderRequest(1, Rider('Z', Location(1,1),\ Location(6,6), 15)), ] >>> event_list_1.append(DriverRequest(10, Driver('Y',\ Location(3,3), 2))) >>> simulation_1 = Simulation() >>> d1 = simulation_1.run(event_list_1) >>> d2 = {'rider_wait_time': 11.0, 'driver_ride_distance': 10.0,\ 'driver_total_distance': 14.0} >>> d1 == d2 True >>> event_list_2 = [DriverRequest(0, Driver('A', Location(1,1), 1)), \ DriverRequest(0, Driver('B', Location(1,2), 1)),RiderRequest(0, \ Rider('A2', Location(1,1),Location(5,5), 10)), RiderRequest(10,\ Rider('B2', Location(4,2), Location(1,5), 15)) ] >>> simulation_2 = Simulation() >>> d3 = simulation_2.run(event_list_2) >>> d4 = {'driver_ride_distance': 7.0, 'driver_total_distance': 8.5,\ 'rider_wait_time': 1.5} >>> d3 == d4 True """ # Add all initial events to the event queue. for event in initial_events: self._events.add(event) while not self._events.is_empty(): # Get the most nearest event, and remove it from the event queue removed_event = self._events.remove() # Do the event if isinstance(removed_event, Event): new_events = removed_event.do(self._dispatcher, self._monitor) # Append the result events back for event in new_events: self._events.add(event) return self._monitor.report()
class Simulation: """Runs the core of the simulation through time. === Attributes === all_rides: A list of all the rides in this simulation. Note that not all rides might be used, depending on the timeframe when the simulation is run. all_stations: A dictionary containing all the stations in this simulation. visualizer: A helper class for visualizing the simulation. active_rides: A list of all the rides in this simulation that are in progress. ride_pq: priorityquete of rides which are going to happen between simulation time. """ all_stations: Dict[str, Station] all_rides: List[Ride] visualizer: Visualizer active_rides: List[Ride] rides_pq: PriorityQueue def __init__(self, station_file: str, ride_file: str) -> None: """Initialize this simulation with the given configuration settings. """ self.all_stations = create_stations(station_file) self.all_rides = create_rides(ride_file, self.all_stations) self.active_rides = [] self.rides_pq = PriorityQueue() self.visualizer = Visualizer() def run(self, start: datetime, end: datetime) -> None: """Run the simulation from <start> to <end>. """ step = timedelta(minutes=1) # Each iteration spans one minute of time for ride in self.all_rides: if start <= ride.start_time <= end: self.rides_pq.add(RideStartEvent(self, ride.start_time, ride)) if (ride.start_time < start) and (ride.end_time >= start): ride.stats_decider = 0 self.rides_pq.add(RideStartEvent(self, ride.start_time, ride)) current = start # current = current time of the simulation for ride in self.all_rides: # to add the rides which has been started before the simulation's # time and hasn't finished yet if ride.start_time < start < ride.end_time: self.active_rides.append(ride) while current <= end: self._update_active_rides_fast(current) self.visualizer.render_drawables( list(self.all_stations.values()) + list(self.active_rides), current) if current != start: # because 8:00 to 8:00 doens't include any doration self.availibility_checker(int(step.total_seconds())) # to calculate the low_availibility and low_unoccupied of each # station current += step # Leave this code at the very bottom of this method. # It will keep the visualization window open until you close # it by pressing the 'X'. while True: if self.visualizer.handle_window_events(): return # Stop the simulation def availibility_checker(self, duration: int) -> None: """ It changes the amount of low_availibility and low occupied amount of each station by the amount of duration( which in this case duration is always 60 seconds)""" for station in self.all_stations.values(): if station.num_bikes <= 5: station.low_availability += duration if station.capacity - station.num_bikes <= 5: station.low_unoccupied += duration def _update_active_rides(self, time: datetime) -> None: """Update this simulation's list of active rides for the given time. REQUIRED IMPLEMENTATION NOTES: - Loop through `self.all_rides` and compare each Ride's start and end times with <time>. If <time> is between the ride's start and end times (inclusive), then add the ride to self.active_rides if it isn't already in that list. Otherwise, remove the ride from self.active_rides if it is in that list. - This means that if a ride started before the simulation's time period but ends during or after the simulation's time period, it should still be added to self.active_rides. - *** we use the attribiute'ride.allowance' to avoid adding the rides that couldn't happen at their start time due to the shortage of bikes in their station, at the other time between their start and end time. """ for ride in self.all_rides: if ride.start_time <= time <= ride.end_time and\ ride not in self.active_rides and\ ride.allowance == 1: if ride.start.num_bikes < 1: ride.allowance = 0 # therefor if the statoin that ride # start from it doesn't have enought bikes when the start # time arrives, the ride won't happen anymore( because of # the change of the ride.allowace) else: ride.start.num_bikes -= 1 ride.start.start_from += 1 ride.allowance = 0 self.active_rides.append(ride) if (not ride.start_time <= time <= ride.end_time) and\ (ride in self.active_rides): if ride.end.capacity >= ride.end.num_bikes + 1: ride.end.num_bikes += 1 ride.end.end_in += 1 # we're going to remove the bike at its endtime anyway but # if its station doesn't have enough spots it won't count for # the statistics. self.active_rides.remove(ride) def calculate_statistics(self) -> Dict[str, Tuple[str, float]]: """Return a dictionary containing statistics for this simulation. The returned dictionary has exactly four keys, corresponding to the four statistics tracked for each station: - 'max_start' - 'max_end' - 'max_time_low_availability' - 'max_time_low_unoccupied' The corresponding value of each key is a tuple of two elements, where the first element is the name (NOT id) of the station that has the maximum value of the quantity specified by that key, and the second element is the value of that quantity. For example, the value corresponding to key 'max_start' should be the name of the station with the most number of rides started at that station, and the number of rides that started at that station. """ max_start: Station # The station which has maximum rides that start from it. max_end: Station # The statoin which has maximun rides that end in it. mtla: Station # The station which has the maximum time of low availibilty. mtlu: Station # The station which has the maximun time of low unoccupied. first_loop = True # using first_loop variable to give value to the # {max_start, max_endd, mtla, mtlu} when the loop operates for the # first time, unless none of the if statements will occure.(cause the # variables don't have specific value) for station in self.all_stations.values(): if first_loop is True: max_start = max_end = mtla = mtlu = station first_loop = False max_start = bigger(max_start, station, max_start.start_from, station.start_from) max_end = bigger(max_end, station, max_end.end_in, station.end_in) mtla = bigger(mtla, station, mtla.low_availability, station.low_availability) mtlu = bigger(mtlu, station, mtlu.low_unoccupied, station.low_unoccupied) return { 'max_start': (max_start.name, max_start.start_from), 'max_end': (max_end.name, max_end.end_in), 'max_time_low_availability': (mtla.name, mtla.low_availability), 'max_time_low_unoccupied': (mtlu.name, mtlu.low_unoccupied) } def _update_active_rides_fast(self, time: datetime) -> None: """Update this simulation's list of active rides for the given time. REQUIRED IMPLEMENTATION NOTES: - see Task 5 of the assignment handout """ let = 0 # using let instead of syntax break, because when the first # event of our pq has not been occured because of the time, since our # pq has been sorted base on the event's time, therefore there is no # need to check other event so we do not need to check the others events # so we change the let to 1 to come out of the loop. while (self.rides_pq.is_empty() is False) and (let == 0): event = self.rides_pq.remove() if event.time > time: self.rides_pq.add(event) let = 1 if time >= event.time: endevent = event.process() while endevent != []: # because if our event is an EndRideEvent then its return's # list is empty and there is no consequence event. consequence_event = endevent.pop() self.rides_pq.add(consequence_event)
class Simulation: """Runs the core of the simulation through time. === Attributes === all_rides: A list of all the rides in this simulation. Note that not all rides might be used, depending on the timeframe when the simulation is run. all_stations: A dictionary containing all the stations in this simulation. visualizer: A helper class for visualizing the simulation. active_rides A list that contains the active rides that progess currently in the time of the simulation """ all_stations: Dict[str, Station] all_rides: List[Ride] visualizer: Visualizer active_rides: List[Ride] event_priority: PriorityQueue['Event'] def __init__(self, station_file: str, ride_file: str) -> None: """Initialize this simulation with the given configuration settings. """ self.visualizer = Visualizer() self.all_stations = create_stations(station_file) self.all_rides = create_rides(ride_file, self.all_stations) self.active_rides = [] self.event_priority = PriorityQueue() def run(self, start: datetime, end: datetime) -> None: """Run the simulation from <start> to <end>. """ for ride in self.all_rides: if ride.start_time >= start: self.event_priority.add(RideStartEvent(self, ride.start_time, ride)) step = timedelta(minutes=1) # Each iteration spans one minute of time while start <= end: #self._update_active_rides_fast(start) self._update_active_rides(start) if start < end: self.update_availability_and_unoccupied() self.visualizer.render_drawables( list(self.all_stations.values()) + self.active_rides, start) start += step # if start == end: # self.active_rides = [] # Leave this code at the very bottom of this method. # It will keep the visualization window open until you close # it by pressing the 'X'. while True: if self.visualizer.handle_window_events(): return # Stop the simulation
class GroceryStoreSimulation: """A Grocery Store simulation. This is the class which is responsible for setting up and running a simulation. The API is given to you: your main task is to implement the two methods according to their docstrings. Of course, you may add whatever private attributes and methods you want. But because you should not change the interface, you may not add any public attributes or methods. This is the entry point into your program, and in particular is used for auto testing purposes. This makes it ESSENTIAL that you do not change the interface in any way! """ # === Private Attributes === # @type _events: PriorityQueue[Event] # A sequence of events arranged in priority determined by the event # sorting order. # @type _store: GroceryStore # The grocery store associated with the simulation. def __init__(self, store_file): """Initialize a GroceryStoreSimulation from a file. @type store_file: str A file containing the configuration of the grocery store. @rtype: None """ self._events = PriorityQueue() self._store = GroceryStore(store_file) def run(self, event_file): """Run the simulation on the events stored in <event_file>. Return a dictionary containing statistics of the simulation, according to the specifications in the assignment handout. @type self: GroceryStoreSimulation @type event_file: str A filename referring to a raw list of events. Precondition: the event file is a valid list of events. @rtype: dict[str, int] """ # Initialize statistics stats = { 'num_customers': 0, 'total_time': 0, 'max_wait': -1 } initial_events = create_event_list(event_file) for i in range(len(initial_events)): self._events.add(initial_events[i]) if type(initial_events[i]) == JoinLine: stats['num_customers'] += 1 # THE FOLLOWING CODE MAY SHOW SOME WARNINGS. # (IF NOT, PLEASE IGNORE THIS COMMENT) # PROFESSOR LIU TOLD ME THAT THESE WARNINGS ARE PROBLEMS # WITH PyCharm ITSELF. SINCE WE ARE NOT ALLOWED TO CHANGE # THE INTERFACE, PROF SAID WE CAN JUST IGNORE THEM. THANK YOU. while not self._events.is_empty(): event = self._events.remove() spawned_events = event.do(self._store) stats['total_time'] = event.timestamp # Calculate max_wait if type(event) == FinishCheckOut: end_time = event.timestamp customer_name = event.name for u in range(len(initial_events)): if type(initial_events[u]) == JoinLine: temp = initial_events[u] if customer_name == temp.name: start_time = temp.timestamp waited_time = end_time - start_time if waited_time > stats['max_wait']: stats['max_wait'] = waited_time if spawned_events is not None: for j in range(len(spawned_events)): self._events.add(spawned_events[j]) return stats
def find_path(self, start_node, target_node): """ Implement the A-star path search algorithm If you will add a new node to the path, don't forget to set the parent. You can find an example in the docstring of Node class Please note the shortest path between two nodes may not be unique. However all of them have same length! @type self: Grid @type start_node: Node The starting node of the path @type target_node: Node The target node of the path @rtype: None """ #create a copy of the original map g = copy.copy(self.map) #make an open PriorityQueue to store children opens = PriorityQueue(Node.__lt__) #set the starting node's g and h costs to be 0 or else it starts around infinity start_node.gcost = 0 start_node.hcost = 0 #add the starting node to the open Queue opens.add(start_node) #loop while the open set is not empty while not opens.is_empty(): #remove the value with the lowest fcost built into the PriorityQueue class q = opens.remove() #create a list of successors suc = [] #add each new successor node in the 8 surrounding points to the list if the index is in range and the point is navigable if q.grid_y-1 >= 0 and q.grid_y - 1 < self.height and q.grid_x-1 >= 0 and q.grid_x - 1 < self.width and g[q.grid_x-1][q.grid_y-1].navigable == True: suc.append(Node(g[q.grid_x-1][q.grid_y-1].navigable,q.grid_x-1,q.grid_y-1)) if q.grid_y-1 >= 0 and q.grid_y - 1 < self.height and q.grid_x >= 0 and q.grid_x < self.width and g[q.grid_x][q.grid_y-1].navigable == True: suc.append(Node(g[q.grid_x][q.grid_y-1].navigable,q.grid_x,q.grid_y-1)) if q.grid_y-1 >= 0 and q.grid_y - 1 < self.height and q.grid_x + 1 >= 0 and q.grid_x + 1 < self.width and g[q.grid_x+1][q.grid_y-1].navigable == True: suc.append(Node(g[q.grid_x+1][q.grid_y-1].navigable,q.grid_x+1,q.grid_y-1)) if q.grid_y >= 0 and q.grid_y < self.height and q.grid_x-1 >= 0 and q.grid_x - 1 < self.width and g[q.grid_x-1][q.grid_y].navigable == True: suc.append(Node(g[q.grid_x-1][q.grid_y].navigable,q.grid_x-1,q.grid_y)) if q.grid_y >= 0 and q.grid_y < self.height and q.grid_x+1 >= 0 and q.grid_x + 1 < self.width and g[q.grid_x+1][q.grid_y].navigable == True: suc.append(Node(g[q.grid_x+1][q.grid_y].navigable,q.grid_x+1,q.grid_y)) if q.grid_y+1 >= 0 and q.grid_y + 1 < self.height and q.grid_x-1 >= 0 and q.grid_x - 1 < self.width and g[q.grid_x-1][q.grid_y+1].navigable == True: suc.append(Node(g[q.grid_x-1][q.grid_y+1].navigable,q.grid_x-1,q.grid_y+1)) if q.grid_y+1 >= 0 and q.grid_y + 1 < self.height and q.grid_x >= 0 and q.grid_x < self.width and g[q.grid_x][q.grid_y+1].navigable == True: suc.append(Node(g[q.grid_x][q.grid_y+1].navigable,q.grid_x,q.grid_y+1)) if q.grid_y+1 >= 0 and q.grid_y + 1 < self.height and q.grid_x+1 >= 0 and q.grid_x + 1 < self.width and g[q.grid_x+1][q.grid_y+1].navigable == True: suc.append(Node(g[q.grid_x+1][q.grid_y+1].navigable,q.grid_x+1,q.grid_y+1)) #for each successor for i in range(len(suc)): #set the parent to be the one that was just removed suc[i].set_parent(q) #stop the search if the target node is found if suc[i] == target_node: #also add the successor to the closed set self.path.add(suc[i]) return None #set the target's g, h, and f costs suc[i].set_gcost(q.gcost + q.distance(suc[i])) suc[i].set_hcost(target_node.distance(suc[i])) suc[i].fcost() #if the successor is in the open set and is lower f than the value in the successor then skip the successsor if opens.is_less_than(suc[i]): pass #if the successor is in the closed set and is lower f than the value in the successor then skip the successsor if self.path.is_less_than(suc[i]): pass #otherwise add the successor to the open set else: opens.add(suc[i]) #add the q value to the closed set self.path.add(q)
class GroceryStoreSimulation: """A Grocery Store simulation. This is the class which is responsible for setting up and running a simulation. The interface is given to you: your main task is to implement the two methods according to their docstrings. Of course, you may add whatever private attributes you want to this class. Because you should not change the interface in any way, you may not add any public attributes. === Private Attributes === _events: A sequence of events arranged in priority determined by the event sorting order. _store: The store being simulated. """ _events: PriorityQueue _store: GroceryStore def __init__(self, store_file: TextIO) -> None: """Initialize a GroceryStoreSimulation using configuration <store_file>. """ self._events = PriorityQueue() self._store = GroceryStore(store_file) def run(self, file: TextIO) -> Dict[str, Any]: """Run the simulation on the events stored in <initial_events>. Return a dictionary containing statistics of the simulation, according to the specifications in the assignment handout. """ # Initialize statistics stats = { 'num_customers': 0, 'total_time': 0, 'max_wait': -1 } # TODO: Calculate and return the correct statistics. file_list = create_event_list(file) for i in file_list: self._events.add(i) dict_name = {} max_wait = 0 while not self._events.is_empty(): i = self._events.remove() if isinstance(i, CustomerArrival): if i.customer.name not in dict_name: dict_name[i.customer.name] = i.timestamp stats['num_customers'] += 1 if isinstance(i, CheckoutCompleted): dict_name[i.customer.name] \ = i.timestamp - dict_name[i.customer.name] for event in i.do(self._store): self._events.add(event) for j in dict_name: if dict_name[j] > max_wait: max_wait = dict_name[j] time_last = i.timestamp stats['total_time'] = time_last stats["max_wait"] = max_wait return stats
class Simulation: """A simulation. This is the class which is responsible for setting up and running a simulation. The API is given to you: your main task is to implement the run method below according to its docstring. Of course, you may add whatever private attributes and methods you want. But because you should not change the interface, you may not add any public attributes or methods. This is the entry point into your program, and in particular is used for auto-testing purposes. This makes it ESSENTIAL that you do not change the interface in any way! """ # === Private Attributes === # @type _events: PriorityQueue[Event] # A sequence of events arranged in priority determined by the event # sorting order. # @type _dispatcher: Dispatcher # The dispatcher associated with the simulation. def __init__(self): """Initialize a Simulation. @type self: Simulation @rtype: None """ self._events = PriorityQueue() self._dispatcher = Dispatcher() self._monitor = Monitor() def run(self, initial_events): """Run the simulation on the list of events in <initial_events>. Return a dictionary containing statistics of the simulation, according to the specifications in the assignment handout. @type self: Simulation @type initial_events: list[Event] An initial list of events. @rtype: dict[str, object] >>> events = [] >>> loc = Location(0,0) >>> events.append(RiderRequest(2,Rider("r1", loc, Location(3,4), 4))) >>> events.append(DriverRequest(3, Driver("d1", Location(0,4), 10))) >>> sim = Simulation() >>> sim.run(events) {'rider_wait_time': 1.0, 'driver_ride_distance': 7.0, 'driver_total_distance': 11.0} # Above line couldn't be changed to go to next line because doing that # made the doctest compare the output so that it also flows to the next # line """ # Add all initial events to the event queue. # Until there are no more events, remove an event # from the event queue and do it. Add any returned # events to the event queue. for event in initial_events: self._events.add(event) new_events = [] while not self._events.is_empty(): new_events = self._events.remove().do(self._dispatcher, self._monitor) if new_events is not None: for event in new_events: self._events.add(event) return self._monitor.report()
class Grid: """ Represents the world where the action of the game takes place. You may define helper methods as you see fit. === Attributes: === @type width: int represents the width of the game map in characters the x-coordinate runs along width the leftmost node has x-coordinate zero @type height: int represents the height of the game map in lines the y-coordinate runs along height; the topmost line contains nodes with y-coordinate 0 @type map: List[List[Node]] map[x][y] is a Node with x-coordinate equal to x running from 0 to width-1 and y-coordinate running from 0 to height-1 @type treasure: Node a navigable node in the map, the location of the treasure @type boat: Node a navigable node in the map, the current location of the boat @type path: PriorityQueue a closed list of all the daughter Nodes in the A* algorithm === Representation invariants === - width and height are positive integers - map has dimensions width, height """ def __init__(self, file_path, text_grid=None): """ If text_grid is None, initialize a new Grid assuming file_path contains pathname to a text file with the following format: ..+..++ ++.B..+ .....++ ++..... .T....+ where a dot indicates a navigable Node, a plus indicates a non-navigable Node, B indicates the boat, and T the treasure. The width of this grid is 7 and height is 5. If text_grid is not None, it should be a list of strings representing a Grid. One string element of the list represents one row of the Grid. For example the grid above, should be stored in text_grid as follows: ["..+..++", "++.B..+", ".....++", "++.....", ".T....+"] @type file_path: str - a file pathname. See the above for the file format. - it should be ignored if text_grid is not None. - the file specified by file_path should exists, so there is no need for error handling Please call open_grid to open the file @type text_grid: List[str] @rtype: None """ self.state = "STARTED" self.file_path = file_path self.text_grid = text_grid #set up self.path = PriorityQueue(Node.__lt__) #set up map using open_grid() function and then splitting it if it is a file path if self.text_grid == None: self.map = self.open_grid(self.file_path).read().split("\n") #set up map if it is a text grid else: self.map = self.text_grid #y length of the map self.height = len(self.map) #break up the map into a 2 dimensional list of strings for i in range(len(self.map)): self.map[i] = list(self.map[i]) #set width to be the horizontal length of the map self.width = len(self.map[0][:]) #find the treasure located in the map g = copy.copy(self.map) for i, x in enumerate(g): if "T" in x: self.treasure = Node(True, x.index("T"),i) #change x and y values self.map = [list(i) for i in zip(*self.map)] self.boat = self.set_boat() for i,x in enumerate(self.map): for j in range(len(x)): self.map[i][j] = Node(self.map[i][j] != "+",i,j) @classmethod def open_grid(self, file_path): """ @rtype TextIOWrapper: """ return open(file_path) def __str__(self): """ Return a string representation. @type self: Grid @rtype: str >>> g = Grid("", ["B.++", ".+..", "...T"]) >>> print(g) B.++ .+.. ...T """ #join the 2d list twice, once with nothing and the other with linebreaks lst = [] g = self.convert() for i in g: lst.append("".join(i)) return"\n".join(lst) def move(self, direction): """ Move the boat in a specific direction, if the node corresponding to the direction is navigable Else do nothing @type self: Grid @type direction: str @rtype: None direction may be one of the following: N, S, E, W, NW, NE, SW, SE (north, south, ...) 123 4B5 678 1=NW, 2=N, 3=NE, 4=W, 5=E, 6=SW, 7=S, 8=SE >>> g = Grid("", ["B.++", ".+..", "...T"]) >>> g.move("S") >>> print(g) ..++ B+.. ...T """ #create moving direction vectors if the N S E W or a combination is entered if direction == "N": new = (0,-1) elif direction == "S": new = (0,1) elif direction == "E": new = (1,0) elif direction == "W": new = (-1,0) elif direction == "NW": new = (-1,-1) elif direction == "NE": new = (1,-1) elif direction == "SE": new = (1,1) elif direction == "SW": new = (-1,1) else: print("Invalid Command") #create a copy of the map so as not to disturb the original g = copy.copy(self.map) #find the boat's index B = (self.boat.grid_x,self.boat.grid_y) #if the boat (x,y) + the new direction(x,y) is in the bounds of the map if B[0]+new[0] >= 0 and B[0]+new[0] < self.height and B[1]+new[1] >= 0 and B[1]+new[1] < self.width: #if the new position of the boat is navigable if g[B[0]+new[0]][B[1]+new[1]].navigable == True or g[B[0]+new[0]][B[1]+new[1]] == self.treasure: #if the new position is the target position then you win if g[B[0]+new[0]][B[1]+new[1]] == self.treasure: self.state = "WON" print(self.state) #move change the map's position to an dot on the old one and B on the new self.boat = g[B[0]+new[0]][B[1]+new[1]] #join the list back together the list we have been working on #set the map equal to the new one set boat equal to new position self.map = g else: print("Cannot Move here") else: print("Cannot Move here") def find_path(self, start_node, target_node): """ Implement the A-star path search algorithm If you will add a new node to the path, don't forget to set the parent. You can find an example in the docstring of Node class Please note the shortest path between two nodes may not be unique. However all of them have same length! @type self: Grid @type start_node: Node The starting node of the path @type target_node: Node The target node of the path @rtype: None """ #create a copy of the original map g = copy.copy(self.map) #make an open PriorityQueue to store children opens = PriorityQueue(Node.__lt__) #set the starting node's g and h costs to be 0 or else it starts around infinity start_node.gcost = 0 start_node.hcost = 0 #add the starting node to the open Queue opens.add(start_node) #loop while the open set is not empty while not opens.is_empty(): #remove the value with the lowest fcost built into the PriorityQueue class q = opens.remove() #create a list of successors suc = [] #add each new successor node in the 8 surrounding points to the list if the index is in range and the point is navigable if q.grid_y-1 >= 0 and q.grid_y - 1 < self.height and q.grid_x-1 >= 0 and q.grid_x - 1 < self.width and g[q.grid_x-1][q.grid_y-1].navigable == True: suc.append(Node(g[q.grid_x-1][q.grid_y-1].navigable,q.grid_x-1,q.grid_y-1)) if q.grid_y-1 >= 0 and q.grid_y - 1 < self.height and q.grid_x >= 0 and q.grid_x < self.width and g[q.grid_x][q.grid_y-1].navigable == True: suc.append(Node(g[q.grid_x][q.grid_y-1].navigable,q.grid_x,q.grid_y-1)) if q.grid_y-1 >= 0 and q.grid_y - 1 < self.height and q.grid_x + 1 >= 0 and q.grid_x + 1 < self.width and g[q.grid_x+1][q.grid_y-1].navigable == True: suc.append(Node(g[q.grid_x+1][q.grid_y-1].navigable,q.grid_x+1,q.grid_y-1)) if q.grid_y >= 0 and q.grid_y < self.height and q.grid_x-1 >= 0 and q.grid_x - 1 < self.width and g[q.grid_x-1][q.grid_y].navigable == True: suc.append(Node(g[q.grid_x-1][q.grid_y].navigable,q.grid_x-1,q.grid_y)) if q.grid_y >= 0 and q.grid_y < self.height and q.grid_x+1 >= 0 and q.grid_x + 1 < self.width and g[q.grid_x+1][q.grid_y].navigable == True: suc.append(Node(g[q.grid_x+1][q.grid_y].navigable,q.grid_x+1,q.grid_y)) if q.grid_y+1 >= 0 and q.grid_y + 1 < self.height and q.grid_x-1 >= 0 and q.grid_x - 1 < self.width and g[q.grid_x-1][q.grid_y+1].navigable == True: suc.append(Node(g[q.grid_x-1][q.grid_y+1].navigable,q.grid_x-1,q.grid_y+1)) if q.grid_y+1 >= 0 and q.grid_y + 1 < self.height and q.grid_x >= 0 and q.grid_x < self.width and g[q.grid_x][q.grid_y+1].navigable == True: suc.append(Node(g[q.grid_x][q.grid_y+1].navigable,q.grid_x,q.grid_y+1)) if q.grid_y+1 >= 0 and q.grid_y + 1 < self.height and q.grid_x+1 >= 0 and q.grid_x + 1 < self.width and g[q.grid_x+1][q.grid_y+1].navigable == True: suc.append(Node(g[q.grid_x+1][q.grid_y+1].navigable,q.grid_x+1,q.grid_y+1)) #for each successor for i in range(len(suc)): #set the parent to be the one that was just removed suc[i].set_parent(q) #stop the search if the target node is found if suc[i] == target_node: #also add the successor to the closed set self.path.add(suc[i]) return None #set the target's g, h, and f costs suc[i].set_gcost(q.gcost + q.distance(suc[i])) suc[i].set_hcost(target_node.distance(suc[i])) suc[i].fcost() #if the successor is in the open set and is lower f than the value in the successor then skip the successsor if opens.is_less_than(suc[i]): pass #if the successor is in the closed set and is lower f than the value in the successor then skip the successsor if self.path.is_less_than(suc[i]): pass #otherwise add the successor to the open set else: opens.add(suc[i]) #add the q value to the closed set self.path.add(q) def convert(self): """converts the map of nodes into a list of strings @type self: Grid @rtype: list """ #create a copy of the map g = copy.copy(self.map) #invert the map back to y and x g = [list(i) for i in zip(*self.map)] #loop for each x y in map set the original value that was given as an input for i,x in enumerate(g): for j in range(len(x)): if g[i][j].navigable and g[i][j] != self.boat and g[i][j] != self.treasure: g[i][j] = "." elif g[i][j] == self.boat: g[i][j] = "B" elif g[i][j] == self.treasure: g[i][j] = "T" else: g[i][j] = "+" return g def retrace_path(self, start_node, target_node): """ Return a list of Nodes, starting from start_node, ending at target_node, tracing the parent Namely, start from target_node, and add its parent to the list. Keep going until you reach the start_node. If the chain breaks before reaching the start_node, return an empty list. @type self: Grid @type start_node: Node @type target_node: Node @rtype: list[Node] """ #run the find_path function self.find_path(start_node,target_node) #create an empty list lst = [] #copy the closed set nodel = copy.copy(self.path) #add the closed set into a list while not nodel.is_empty(): lst.append(nodel.remove()) #find the target node in the list for i in range(len(lst)): if lst[i] == target_node: tar = i #create the path from the target to the starting node by adding the all n parents to the list then return it lst2 = [] lst2.append(lst[tar]) node = lst[tar] while not node == start_node: lst2.append(node) node = node.parent return lst2 def set_boat(self): """set the position of the boat @type self: Grid @rtype: Node """ #create a copy of the map g = copy.copy(self.map) #search throught the values in the node for the boat for i, x in enumerate(g): if "B" in x: #return a node of the boat return Node(True, i, x.index("B")) def get_treasure(self, s_range): """ Return treasure node if it is located at a distance s_range or less from the boat, else return None @type s_range: int @rtype: Node, None """ #make a copy of the map g = copy.copy(self.map) #go through all of g to find the value T and set the treasure at that value = node at that point for i, x in enumerate(g): if "T" in x: Treasure = Node(True, i, x.index("T")) #return the value if it is <= s_range or else don't if self.boat.distance(Treasure) <= s_range: return Treasure else: return None def plot_path(self, start_node, target_node): """ Return a string representation of the grid map, plotting the shortest path from start_node to target_node computed by find_path using "*" characters to show the path @type self: Grid @type start_node: Node @type target_node: Node @rtype: str >>> g = Grid("", ["B.++", ".+..", "...T"]) >>> print(g.plot_path(g.boat, g.treasure)) B*++ .+*. ...T """ #create a copy of the map g = copy.copy(self.map) #make a path using A* paths = self.retrace_path(start_node,target_node) #write the path on the grid g = self.convert() for p in paths: if p != start_node and p!= target_node: g[p.grid_y][p.grid_x] = "*" new = [] for i, x in enumerate(g): new.append("".join(x)) return "\n".join(new)
class GroceryStoreSimulation: """A Grocery Store simulation. This is the class which is responsible for setting up and running a simulation. The interface is given to you: your main task is to implement the two methods according to their docstrings. Of course, you may add whatever private attributes you want to this class. Because you should not change the interface in any way, you may not add any public attributes. === Private Attributes === _events: A sequence of events arranged in priority determined by the event sorting order. _store: The store being simulated. """ _events: PriorityQueue _store: GroceryStore def __init__(self, store_file: TextIO) -> None: """Initialize a GroceryStoreSimulation using configuration <store_file>. """ self._events = PriorityQueue() self._store = GroceryStore(store_file) def run(self, file: TextIO) -> Dict[str, Any]: """Run the simulation on the events stored in <initial_events>. Return a dictionary containing statistics of the simulation, according to the specifications in the assignment handout. """ # This initializes statistics. stats = {'num_customers': 0, 'total_time': 0, 'max_wait': -1} # This fills self._events from file. event_list = create_event_list(file) while len(event_list) != 0: self._events.add(event_list.pop()) # This keeps track of customer start and end times. This is used to # calculate the maximum wait time. customers = {} while not self._events.is_empty(): event = self._events.remove() # This checks to see the event type. if isinstance(event, CustomerArrival): # This checks to see if the customer is in the customers # dictionary since the "max_wait" calculation is based on the # customer arriving at the wait area. if event.customer not in customers: customers[event.customer] = [event.timestamp, -1] stats['num_customers'] += 1 elif isinstance(event, CheckoutCompleted): customers[event.customer][1] = event.timestamp new_events = event.do(self._store) # This adds the new events. while not len(new_events) == 0: self._events.add(new_events.pop()) stats['total_time'] = event.timestamp # This calculates the maximum wait time. for key in customers: wait_time = customers[key][1] - customers[key][0] if wait_time > stats['max_wait']: stats['max_wait'] = wait_time return stats
class GroceryStoreSimulation: """A Grocery Store simulation. This is the class which is responsible for setting up and running a simulation. The API is given to you: your main task is to implement the two methods according to their docstrings. Of course, you may add whatever private attributes and methods you want. But because you should not change the interface, you may not add any public attributes or methods. This is the entry point into your program, and in particular is used for autotesting purposes. This makes it ESSENTIAL that you do not change the interface in any way! """ # === Private Attributes === # @type _events: PriorityQueue[Event] # A sequence of events arranged in priority determined by the event # sorting order. # @type _store: GroceryStore # The grocery store associated with the simulation. def __init__(self, store_file): """Initialize a GroceryStoreSimulation from a file. @type store_file: str A file containing the configuration of the grocery store. @rtype: None """ self._events = PriorityQueue() self._store = GroceryStore(store_file) def run(self, event_file): """Run the simulation on the events stored in <event_file>. Return a dictionary containing statistics of the simulation, according to the specifications in the assignment handout. @type self: GroceryStoreSimulation @type event_file: str A filename referring to a raw list of events. Precondition: the event file is a valid list of events. @type initial_events: list[event] @rtype: dict[str, object] """ # Initialize statistics stats = { 'num_customers': 0, #check 'total_time': 0, 'max_wait': -1 } initial_events = create_event_list(event_file) #counted number of customers from file directly # TODO: Process all of the events, collecting statistics along the way. #first, add event from initial_events to pq then sort using sort method for index in range (len(initial_events)): self._events.add(initial_events[index]) #second, pass sorted events which is inside PQ to store while not self._events.is_empty(): #trigger the do function which checks if new events spawn in the store #new events are returned #setup simulation clock, advance clock to first event current_event = self._events.remove() #when there is only one event left, equate the time if self._events.is_empty(): stats['total_time'] = current_event.timestamp #the event is triggered returned_tuple = current_event.do(self._store) if returned_tuple[1] == 'int': stats['max_wait'] = returned_tuple[0] elif returned_tuple[1] == 'one event': self._events.add(returned_tuple[0]) elif returned_tuple[1] == 'event list': for item in returned_tuple[0]: self._events.add(item) return stats def handle_new_event(self, item): self._events.add(item)