Skip to content

Commit 080addd

Browse files
committed
taxi simulator using coroutines
1 parent d4ba3fe commit 080addd

File tree

1 file changed

+108
-70
lines changed

1 file changed

+108
-70
lines changed

control/simulation/taxi_sim.py

Lines changed: 108 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,56 @@
1+
2+
"""
3+
Taxi simulator
4+
5+
Sample run with two cars, random seed = 4::
6+
7+
>>> main(num_taxis=2, seed=7)
8+
taxi: 0 Event(time=2, actor_id=0, action='pick up passenger')
9+
taxi: 1 Event(time=11, actor_id=1, action='pick up passenger')
10+
taxi: 1 Event(time=12, actor_id=1, action='drop off passenger')
11+
taxi: 0 Event(time=13, actor_id=0, action='drop off passenger')
12+
taxi: 0 Event(time=15, actor_id=0, action='pick up passenger')
13+
taxi: 0 Event(time=16, actor_id=0, action='drop off passenger')
14+
taxi: 1 Event(time=16, actor_id=1, action='pick up passenger')
15+
taxi: 0 Event(time=17, actor_id=0, action='going home')
16+
taxi: 1 Event(time=24, actor_id=1, action='drop off passenger')
17+
taxi: 1 Event(time=25, actor_id=1, action='pick up passenger')
18+
taxi: 1 Event(time=31, actor_id=1, action='drop off passenger')
19+
taxi: 1 Event(time=32, actor_id=1, action='pick up passenger')
20+
taxi: 1 Event(time=33, actor_id=1, action='drop off passenger')
21+
taxi: 1 Event(time=34, actor_id=1, action='going home')
22+
*** end of events ***
23+
24+
"""
25+
126
import sys
227
import random
328
import collections
429
import queue
30+
import argparse
531

32+
DEFAULT_NUMBER_OF_TAXIS = 3
33+
DEFAULT_END_TIME = 80
634
FIND_PASSENGER_INTERVAL = 4
735
TRIP_DURATION = 10
836

9-
Event = collections.namedtuple('Event', 'time actor action')
37+
Event = collections.namedtuple('Event', 'time actor_id action')
1038

1139

1240
def compute_delay(interval):
1341
"""Compute action delay using exponential distribution"""
1442
return int(random.expovariate(1/interval)) + 1
1543

1644

17-
def taxi_process(ident, trips):
45+
def taxi_process(ident, trips, start_time=0):
1846
"""Yield to simulator issuing event at each state change"""
19-
trip_ends = 0
47+
time = start_time
2048
for i in range(trips):
21-
prowling_ends = trip_ends + compute_delay(FIND_PASSENGER_INTERVAL)
22-
yield Event(prowling_ends, ident, 'passenger picked up')
49+
prowling_ends = time + compute_delay(FIND_PASSENGER_INTERVAL)
50+
time = yield Event(prowling_ends, ident, 'pick up passenger')
2351

24-
trip_ends = prowling_ends + compute_delay(TRIP_DURATION)
25-
yield Event(trip_ends, ident, 'passenger dropped off')
52+
trip_ends = time + compute_delay(TRIP_DURATION)
53+
time = yield Event(trip_ends, ident, 'drop off passenger')
2654

2755
yield Event(trip_ends + 1, ident, 'going home')
2856

@@ -31,90 +59,100 @@ class Simulator:
3159

3260
def __init__(self, actors):
3361
self.events = queue.PriorityQueue()
34-
self.actors = list(actors)
35-
self.time = 0
62+
self.actors = dict(actors)
3663

37-
def schedule_events(self):
38-
"""Advance each actor to next state, scheduling events"""
39-
for actor in list(self.actors):
40-
try:
41-
future_event = next(actor)
42-
except StopIteration:
43-
self.actors.remove(actor) # remove exhausted actor
44-
else:
45-
self.events.put(future_event)
4664

4765
def run(self, end_time):
4866
"""Schedule and execute events until time is up"""
49-
while self.time < end_time:
50-
self.schedule_events()
67+
for ident, actor in sorted(self.actors.items()):
68+
first_event = next(actor) # prime each coroutine
69+
self.events.put(first_event)
70+
time = 0
71+
while time < end_time:
5172
if self.events.empty():
5273
print('*** end of events ***')
5374
break
54-
event = self.events.get()
55-
self.time = event.time
56-
print('taxi:', event.actor, event.actor * ' ', event)
75+
76+
# get and display current event
77+
current_event = self.events.get()
78+
print('taxi:', current_event.actor_id,
79+
current_event.actor_id * ' ', current_event)
80+
81+
# schedule next action for current actor
82+
actor = self.actors[current_event.actor_id]
83+
time = current_event.time
84+
try:
85+
next_event = actor.send(time)
86+
except StopIteration:
87+
del self.actors[current_event.actor_id]
88+
else:
89+
self.events.put(next_event)
5790
else:
5891
msg = '*** end of simulation time: {} events pending ***'
5992
print(msg.format(self.events.qsize()))
6093

6194

62-
def extract_seed(args):
63-
"""Set random seed if given in command line"""
64-
for index, arg in enumerate(list(args)):
65-
if arg.startswith('seed='): # for testing...
66-
seed = int(arg.split('=')[1])
67-
random.seed(seed) # get reproducible results
68-
del args[index]
69-
return
70-
71-
72-
def main(args):
73-
"""Parse command line, build actors and run simulation"""
74-
extract_seed(args)
75-
if args:
76-
end_time = int(args[0])
77-
else:
78-
end_time = 100
79-
taxis = [taxi_process(i, (i+1)*2) for i in range(3)]
95+
def main(end_time=DEFAULT_END_TIME, num_taxis=DEFAULT_NUMBER_OF_TAXIS,
96+
seed=None):
97+
"""Initialize random generator, build actors and run simulation"""
98+
if seed is not None:
99+
random.seed(seed) # get reproducible results
100+
101+
taxis = {i: taxi_process(i, (i+1)*2, i*10) for i in range(num_taxis)}
80102
sim = Simulator(taxis)
81103
sim.run(end_time)
82104

105+
83106
if __name__ == '__main__':
84-
main(sys.argv[1:])
107+
108+
parser = argparse.ArgumentParser(
109+
description='Taxi fleet simulator.')
110+
parser.add_argument('-e', '--end-time', type=int,
111+
default=DEFAULT_END_TIME,
112+
help='simulation end time; default = %s'
113+
% DEFAULT_END_TIME)
114+
parser.add_argument('-t', '--taxis', type=int,
115+
default=DEFAULT_NUMBER_OF_TAXIS,
116+
help='number of taxis running; default = %s'
117+
% DEFAULT_NUMBER_OF_TAXIS)
118+
parser.add_argument('-s', '--seed', type=int, default=None,
119+
help='random generator seed (for testing)')
120+
121+
args = parser.parse_args()
122+
main(args.end_time, args.taxis, args.seed)
85123

86124

87125
"""
88126
Sample run:
89127
90-
$ clear; python3 taxi_sim.py seed=5 110
91-
taxi: 0 Event(time=4, actor=0, action='passenger picked up')
92-
taxi: 1 Event(time=6, actor=1, action='passenger picked up')
93-
taxi: 2 Event(time=7, actor=2, action='passenger picked up')
94-
taxi: 1 Event(time=20, actor=1, action='passenger dropped off')
95-
taxi: 1 Event(time=23, actor=1, action='passenger picked up')
96-
taxi: 0 Event(time=33, actor=0, action='passenger dropped off')
97-
taxi: 2 Event(time=33, actor=2, action='passenger dropped off')
98-
taxi: 0 Event(time=34, actor=0, action='passenger picked up')
99-
taxi: 0 Event(time=45, actor=0, action='passenger dropped off')
100-
taxi: 2 Event(time=45, actor=2, action='passenger picked up')
101-
taxi: 0 Event(time=46, actor=0, action='going home')
102-
taxi: 1 Event(time=47, actor=1, action='passenger dropped off')
103-
taxi: 2 Event(time=47, actor=2, action='passenger dropped off')
104-
taxi: 2 Event(time=49, actor=2, action='passenger picked up')
105-
taxi: 1 Event(time=50, actor=1, action='passenger picked up')
106-
taxi: 1 Event(time=58, actor=1, action='passenger dropped off')
107-
taxi: 2 Event(time=58, actor=2, action='passenger dropped off')
108-
taxi: 1 Event(time=59, actor=1, action='passenger picked up')
109-
taxi: 2 Event(time=59, actor=2, action='passenger picked up')
110-
taxi: 1 Event(time=63, actor=1, action='passenger dropped off')
111-
taxi: 1 Event(time=64, actor=1, action='going home')
112-
taxi: 2 Event(time=84, actor=2, action='passenger dropped off')
113-
taxi: 2 Event(time=90, actor=2, action='passenger picked up')
114-
taxi: 2 Event(time=92, actor=2, action='passenger dropped off')
115-
taxi: 2 Event(time=99, actor=2, action='passenger picked up')
116-
taxi: 2 Event(time=101, actor=2, action='passenger dropped off')
117-
taxi: 2 Event(time=102, actor=2, action='going home')
128+
$ clear; python3 taxi_sim.py -s 19
129+
taxi: 0 Event(time=5, actor_id=0, action='pick up passenger')
130+
taxi: 0 Event(time=13, actor_id=0, action='drop off passenger')
131+
taxi: 0 Event(time=16, actor_id=0, action='pick up passenger')
132+
taxi: 1 Event(time=17, actor_id=1, action='pick up passenger')
133+
taxi: 1 Event(time=21, actor_id=1, action='drop off passenger')
134+
taxi: 1 Event(time=22, actor_id=1, action='pick up passenger')
135+
taxi: 2 Event(time=23, actor_id=2, action='pick up passenger')
136+
taxi: 1 Event(time=26, actor_id=1, action='drop off passenger')
137+
taxi: 2 Event(time=27, actor_id=2, action='drop off passenger')
138+
taxi: 1 Event(time=28, actor_id=1, action='pick up passenger')
139+
taxi: 2 Event(time=29, actor_id=2, action='pick up passenger')
140+
taxi: 1 Event(time=30, actor_id=1, action='drop off passenger')
141+
taxi: 1 Event(time=32, actor_id=1, action='pick up passenger')
142+
taxi: 2 Event(time=33, actor_id=2, action='drop off passenger')
143+
taxi: 2 Event(time=34, actor_id=2, action='pick up passenger')
144+
taxi: 2 Event(time=35, actor_id=2, action='drop off passenger')
145+
taxi: 2 Event(time=36, actor_id=2, action='pick up passenger')
146+
taxi: 1 Event(time=41, actor_id=1, action='drop off passenger')
147+
taxi: 1 Event(time=42, actor_id=1, action='going home')
148+
taxi: 2 Event(time=44, actor_id=2, action='drop off passenger')
149+
taxi: 2 Event(time=46, actor_id=2, action='pick up passenger')
150+
taxi: 2 Event(time=60, actor_id=2, action='drop off passenger')
151+
taxi: 2 Event(time=67, actor_id=2, action='pick up passenger')
152+
taxi: 2 Event(time=73, actor_id=2, action='drop off passenger')
153+
taxi: 0 Event(time=74, actor_id=0, action='drop off passenger')
154+
taxi: 2 Event(time=74, actor_id=2, action='going home')
155+
taxi: 0 Event(time=75, actor_id=0, action='going home')
118156
*** end of events ***
119157
120158
"""

0 commit comments

Comments
 (0)