1 use crate::{
2 CarID, DrivingGoal, OrigPersonID, ParkingSpot, PersonID, SidewalkPOI, SidewalkSpot, Sim,
3 TripEndpoint, TripMode, TripSpec, Vehicle, VehicleSpec, VehicleType, BIKE_LENGTH,
4 MAX_CAR_LENGTH, MIN_CAR_LENGTH, SPAWN_DIST,
5 };
6 use abstutil::{prettyprint_usize, Counter, Timer};
7 use geom::{Distance, Duration, LonLat, Speed, Time};
8 use map_model::{
9 BuildingID, BusRouteID, BusStopID, DirectedRoadID, Map, OffstreetParking, PathConstraints,
10 Position, RoadID,
11 };
12 use rand::seq::SliceRandom;
13 use rand::{Rng, SeedableRng};
14 use rand_xorshift::XorShiftRng;
15 use serde::{Deserialize, Serialize};
16 use std::collections::{BTreeMap, BTreeSet, HashSet, VecDeque};
17
18 // How to start a simulation.
19 #[derive(Clone, Serialize, Deserialize, Debug)]
20 pub struct Scenario {
21 pub scenario_name: String,
22 pub map_name: String,
23
24 pub people: Vec<PersonSpec>,
25 // None means seed all buses. Otherwise the route name must be present here.
26 pub only_seed_buses: Option<BTreeSet<String>>,
27 }
28
29 #[derive(Clone, Serialize, Deserialize, Debug)]
30 pub struct PersonSpec {
31 pub id: PersonID,
32 // Just used for debugging
33 pub orig_id: Option<OrigPersonID>,
34 pub trips: Vec<IndividTrip>,
35 }
36
37 #[derive(Clone, Serialize, Deserialize, Debug)]
38 pub struct IndividTrip {
39 pub depart: Time,
40 pub trip: SpawnTrip,
41 pub cancelled: bool,
42 // Did a ScenarioModifier affect this?
43 pub modified: bool,
44 }
45
46 impl IndividTrip {
new(depart: Time, trip: SpawnTrip) -> IndividTrip47 pub fn new(depart: Time, trip: SpawnTrip) -> IndividTrip {
48 IndividTrip {
49 depart,
50 trip,
51 cancelled: false,
52 modified: false,
53 }
54 }
55 }
56
57 #[derive(Clone, Debug, Serialize, Deserialize)]
58 pub enum SpawnTrip {
59 // Only for interactive / debug trips
60 VehicleAppearing {
61 start: Position,
62 goal: DrivingGoal,
63 is_bike: bool,
64 },
65 FromBorder {
66 dr: DirectedRoadID,
67 goal: DrivingGoal,
68 // For bikes starting at a border, use FromBorder. UsingBike implies a walk->bike trip.
69 is_bike: bool,
70 origin: Option<OffMapLocation>,
71 },
72 UsingParkedCar(BuildingID, DrivingGoal),
73 UsingBike(BuildingID, DrivingGoal),
74 JustWalking(SidewalkSpot, SidewalkSpot),
75 UsingTransit(
76 SidewalkSpot,
77 SidewalkSpot,
78 BusRouteID,
79 BusStopID,
80 Option<BusStopID>,
81 ),
82 // Completely off-map trip. Don't really simulate much of it.
83 Remote {
84 from: OffMapLocation,
85 to: OffMapLocation,
86 trip_time: Duration,
87 mode: TripMode,
88 },
89 }
90
91 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
92 pub struct OffMapLocation {
93 pub parcel_id: usize,
94 pub gps: LonLat,
95 }
96
97 impl Scenario {
98 // Any case where map edits could change the calls to the RNG, we have to fork.
instantiate(&self, sim: &mut Sim, map: &Map, rng: &mut XorShiftRng, timer: &mut Timer)99 pub fn instantiate(&self, sim: &mut Sim, map: &Map, rng: &mut XorShiftRng, timer: &mut Timer) {
100 sim.set_name(self.scenario_name.clone());
101
102 timer.start(format!("Instantiating {}", self.scenario_name));
103
104 if let Some(ref routes) = self.only_seed_buses {
105 for route in map.all_bus_routes() {
106 if routes.contains(&route.full_name) {
107 sim.seed_bus_route(route);
108 }
109 }
110 } else {
111 // All of them
112 for route in map.all_bus_routes() {
113 sim.seed_bus_route(route);
114 }
115 }
116
117 timer.start_iter("trips for People", self.people.len());
118 let mut spawner = sim.make_spawner();
119 let mut parked_cars: Vec<(Vehicle, BuildingID)> = Vec::new();
120 for p in &self.people {
121 timer.next();
122
123 if let Err(err) = p.check_schedule(map) {
124 panic!("{}", err);
125 }
126
127 let (vehicle_specs, cars_initially_parked_at, vehicle_foreach_trip) =
128 p.get_vehicles(rng);
129 sim.new_person(
130 p.id,
131 p.orig_id,
132 Scenario::rand_ped_speed(rng),
133 vehicle_specs,
134 );
135 let person = sim.get_person(p.id);
136 for (idx, b) in cars_initially_parked_at {
137 parked_cars.push((person.vehicles[idx].clone(), b));
138 }
139 for (t, maybe_idx) in p.trips.iter().zip(vehicle_foreach_trip) {
140 // The RNG call might change over edits for picking the spawning lane from a border
141 // with multiple choices for a vehicle type.
142 let mut tmp_rng = abstutil::fork_rng(rng);
143 let spec = t.trip.clone().to_trip_spec(
144 maybe_idx.map(|idx| person.vehicles[idx].id),
145 &mut tmp_rng,
146 map,
147 );
148 spawner.schedule_trip(
149 person,
150 t.depart,
151 spec,
152 t.trip.start(map),
153 t.cancelled,
154 t.modified,
155 map,
156 );
157 }
158 }
159
160 // parked_cars is stable over map edits, so don't fork.
161 parked_cars.shuffle(rng);
162 seed_parked_cars(parked_cars, sim, map, rng, timer);
163
164 sim.flush_spawner(spawner, map, timer);
165 timer.stop(format!("Instantiating {}", self.scenario_name));
166 }
167
save(&self)168 pub fn save(&self) {
169 abstutil::write_binary(
170 abstutil::path_scenario(&self.map_name, &self.scenario_name),
171 self,
172 );
173 }
174
empty(map: &Map, name: &str) -> Scenario175 pub fn empty(map: &Map, name: &str) -> Scenario {
176 Scenario {
177 scenario_name: name.to_string(),
178 map_name: map.get_name().to_string(),
179 people: Vec::new(),
180 only_seed_buses: Some(BTreeSet::new()),
181 }
182 }
183
rand_car(rng: &mut XorShiftRng) -> VehicleSpec184 pub fn rand_car(rng: &mut XorShiftRng) -> VehicleSpec {
185 let length = Scenario::rand_dist(rng, MIN_CAR_LENGTH, MAX_CAR_LENGTH);
186 VehicleSpec {
187 vehicle_type: VehicleType::Car,
188 length,
189 max_speed: None,
190 }
191 }
192
rand_bike(rng: &mut XorShiftRng) -> VehicleSpec193 pub fn rand_bike(rng: &mut XorShiftRng) -> VehicleSpec {
194 let max_speed = Some(Scenario::rand_speed(
195 rng,
196 Speed::miles_per_hour(8.0),
197 Speed::miles_per_hour(10.0),
198 ));
199 VehicleSpec {
200 vehicle_type: VehicleType::Bike,
201 length: BIKE_LENGTH,
202 max_speed,
203 }
204 }
205
rand_dist(rng: &mut XorShiftRng, low: Distance, high: Distance) -> Distance206 pub fn rand_dist(rng: &mut XorShiftRng, low: Distance, high: Distance) -> Distance {
207 assert!(high > low);
208 Distance::meters(rng.gen_range(low.inner_meters(), high.inner_meters()))
209 }
210
rand_speed(rng: &mut XorShiftRng, low: Speed, high: Speed) -> Speed211 fn rand_speed(rng: &mut XorShiftRng, low: Speed, high: Speed) -> Speed {
212 assert!(high > low);
213 Speed::meters_per_second(rng.gen_range(
214 low.inner_meters_per_second(),
215 high.inner_meters_per_second(),
216 ))
217 }
218
rand_ped_speed(rng: &mut XorShiftRng) -> Speed219 pub fn rand_ped_speed(rng: &mut XorShiftRng) -> Speed {
220 Scenario::rand_speed(rng, Speed::miles_per_hour(2.0), Speed::miles_per_hour(3.0))
221 }
222
count_parked_cars_per_bldg(&self) -> Counter<BuildingID>223 pub fn count_parked_cars_per_bldg(&self) -> Counter<BuildingID> {
224 let mut per_bldg = Counter::new();
225 // Pass in a dummy RNG
226 let mut rng = XorShiftRng::from_seed([0; 16]);
227 for p in &self.people {
228 let (_, cars_initially_parked_at, _) = p.get_vehicles(&mut rng);
229 for (_, b) in cars_initially_parked_at {
230 per_bldg.inc(b);
231 }
232 }
233 per_bldg
234 }
235
remove_weird_schedules(mut self, map: &Map) -> Scenario236 pub fn remove_weird_schedules(mut self, map: &Map) -> Scenario {
237 let orig = self.people.len();
238 self.people
239 .retain(|person| match person.check_schedule(map) {
240 Ok(()) => true,
241 Err(err) => {
242 println!("{}", err);
243 false
244 }
245 });
246 println!(
247 "{} of {} people have nonsense schedules",
248 prettyprint_usize(orig - self.people.len()),
249 prettyprint_usize(orig)
250 );
251 // Fix up IDs
252 for (idx, person) in self.people.iter_mut().enumerate() {
253 person.id = PersonID(idx);
254 }
255 self
256 }
257 }
258
seed_parked_cars( parked_cars: Vec<(Vehicle, BuildingID)>, sim: &mut Sim, map: &Map, base_rng: &mut XorShiftRng, timer: &mut Timer, )259 fn seed_parked_cars(
260 parked_cars: Vec<(Vehicle, BuildingID)>,
261 sim: &mut Sim,
262 map: &Map,
263 base_rng: &mut XorShiftRng,
264 timer: &mut Timer,
265 ) {
266 let mut open_spots_per_road: BTreeMap<RoadID, Vec<(ParkingSpot, Option<BuildingID>)>> =
267 BTreeMap::new();
268 for spot in sim.get_all_parking_spots().1 {
269 let (r, restriction) = match spot {
270 ParkingSpot::Onstreet(l, _) => (map.get_l(l).parent, None),
271 ParkingSpot::Offstreet(b, _) => (
272 map.get_l(map.get_b(b).sidewalk()).parent,
273 match map.get_b(b).parking {
274 OffstreetParking::PublicGarage(_, _) => None,
275 OffstreetParking::Private(_, _) => Some(b),
276 },
277 ),
278 ParkingSpot::Lot(pl, _) => (map.get_l(map.get_pl(pl).driving_pos.lane()).parent, None),
279 };
280 open_spots_per_road
281 .entry(r)
282 .or_insert_with(Vec::new)
283 .push((spot, restriction));
284 }
285 // Changing parking on one road shouldn't affect far-off roads. Fork carefully.
286 for r in map.all_roads() {
287 let mut tmp_rng = abstutil::fork_rng(base_rng);
288 if let Some(ref mut spots) = open_spots_per_road.get_mut(&r.id) {
289 spots.shuffle(&mut tmp_rng);
290 }
291 }
292
293 timer.start_iter("seed parked cars", parked_cars.len());
294 let mut ok = true;
295 let total_cars = parked_cars.len();
296 let mut seeded = 0;
297 for (vehicle, b) in parked_cars {
298 timer.next();
299 if !ok {
300 continue;
301 }
302 if let Some(spot) = find_spot_near_building(b, &mut open_spots_per_road, map) {
303 seeded += 1;
304 sim.seed_parked_car(vehicle, spot);
305 } else {
306 timer.warn(format!(
307 "Not enough room to seed parked cars. Only found spots for {} of {}",
308 prettyprint_usize(seeded),
309 prettyprint_usize(total_cars)
310 ));
311 ok = false;
312 }
313 }
314 }
315
316 // Pick a parking spot for this building. If the building's road has a free spot, use it. If not,
317 // start BFSing out from the road in a deterministic way until finding a nearby road with an open
318 // spot.
find_spot_near_building( b: BuildingID, open_spots_per_road: &mut BTreeMap<RoadID, Vec<(ParkingSpot, Option<BuildingID>)>>, map: &Map, ) -> Option<ParkingSpot>319 fn find_spot_near_building(
320 b: BuildingID,
321 open_spots_per_road: &mut BTreeMap<RoadID, Vec<(ParkingSpot, Option<BuildingID>)>>,
322 map: &Map,
323 ) -> Option<ParkingSpot> {
324 let mut roads_queue: VecDeque<RoadID> = VecDeque::new();
325 let mut visited: HashSet<RoadID> = HashSet::new();
326 {
327 let start = map.building_to_road(b).id;
328 roads_queue.push_back(start);
329 visited.insert(start);
330 }
331
332 loop {
333 let r = roads_queue.pop_front()?;
334 if let Some(spots) = open_spots_per_road.get_mut(&r) {
335 // Fill in all private parking first before
336 // TODO With some probability, skip this available spot and park farther away
337 if let Some(idx) = spots
338 .iter()
339 .position(|(_, restriction)| restriction == &Some(b))
340 {
341 return Some(spots.remove(idx).0);
342 }
343 if let Some(idx) = spots
344 .iter()
345 .position(|(_, restriction)| restriction.is_none())
346 {
347 return Some(spots.remove(idx).0);
348 }
349 }
350
351 for next_r in map.get_next_roads(r).into_iter() {
352 if !visited.contains(&next_r) {
353 roads_queue.push_back(next_r);
354 visited.insert(next_r);
355 }
356 }
357 }
358 }
359
360 impl SpawnTrip {
to_trip_spec( self, use_vehicle: Option<CarID>, rng: &mut XorShiftRng, map: &Map, ) -> TripSpec361 fn to_trip_spec(
362 self,
363 use_vehicle: Option<CarID>,
364 rng: &mut XorShiftRng,
365 map: &Map,
366 ) -> TripSpec {
367 match self {
368 SpawnTrip::VehicleAppearing { start, goal, .. } => TripSpec::VehicleAppearing {
369 start_pos: start,
370 goal,
371 use_vehicle: use_vehicle.unwrap(),
372 retry_if_no_room: true,
373 origin: None,
374 },
375 SpawnTrip::FromBorder {
376 dr,
377 goal,
378 is_bike,
379 origin,
380 } => {
381 let constraints = if is_bike {
382 PathConstraints::Bike
383 } else {
384 PathConstraints::Car
385 };
386 if let Some(l) = dr.lanes(constraints, map).choose(rng) {
387 TripSpec::VehicleAppearing {
388 start_pos: Position::new(*l, SPAWN_DIST),
389 goal,
390 use_vehicle: use_vehicle.unwrap(),
391 retry_if_no_room: true,
392 origin,
393 }
394 } else {
395 TripSpec::NoRoomToSpawn {
396 i: dr.src_i(map),
397 goal,
398 use_vehicle: use_vehicle.unwrap(),
399 origin,
400 error: format!("{} has no lanes to spawn a {:?}", dr.id, constraints),
401 }
402 }
403 }
404 SpawnTrip::UsingParkedCar(start_bldg, goal) => TripSpec::UsingParkedCar {
405 start_bldg,
406 goal,
407 car: use_vehicle.unwrap(),
408 },
409 SpawnTrip::UsingBike(start, goal) => TripSpec::UsingBike {
410 bike: use_vehicle.unwrap(),
411 start,
412 goal,
413 },
414 SpawnTrip::JustWalking(start, goal) => TripSpec::JustWalking { start, goal },
415 SpawnTrip::UsingTransit(start, goal, route, stop1, maybe_stop2) => {
416 TripSpec::UsingTransit {
417 start,
418 goal,
419 route,
420 stop1,
421 maybe_stop2,
422 }
423 }
424 SpawnTrip::Remote {
425 from,
426 to,
427 trip_time,
428 mode,
429 } => TripSpec::Remote {
430 from,
431 to,
432 trip_time,
433 mode,
434 },
435 }
436 }
437
438 // TODO Why do I feel like this code is sitting somewhere else already
mode(&self) -> TripMode439 pub fn mode(&self) -> TripMode {
440 match self {
441 SpawnTrip::VehicleAppearing { is_bike, .. } => {
442 if *is_bike {
443 TripMode::Bike
444 } else {
445 TripMode::Drive
446 }
447 }
448 SpawnTrip::FromBorder { is_bike, .. } => {
449 if *is_bike {
450 TripMode::Bike
451 } else {
452 TripMode::Drive
453 }
454 }
455 SpawnTrip::UsingParkedCar(_, _) => TripMode::Drive,
456 SpawnTrip::UsingBike(_, _) => TripMode::Bike,
457 SpawnTrip::JustWalking(_, _) => TripMode::Walk,
458 SpawnTrip::UsingTransit(_, _, _, _, _) => TripMode::Transit,
459 // TODO Uh...
460 SpawnTrip::Remote { .. } => TripMode::Drive,
461 }
462 }
463
start(&self, map: &Map) -> TripEndpoint464 pub fn start(&self, map: &Map) -> TripEndpoint {
465 match self {
466 SpawnTrip::VehicleAppearing { ref start, .. } => {
467 TripEndpoint::Border(map.get_l(start.lane()).src_i, None)
468 }
469 SpawnTrip::FromBorder { dr, ref origin, .. } => {
470 TripEndpoint::Border(dr.src_i(map), origin.clone())
471 }
472 SpawnTrip::UsingParkedCar(b, _) => TripEndpoint::Bldg(*b),
473 SpawnTrip::UsingBike(b, _) => TripEndpoint::Bldg(*b),
474 SpawnTrip::JustWalking(ref spot, _) | SpawnTrip::UsingTransit(ref spot, _, _, _, _) => {
475 match spot.connection {
476 SidewalkPOI::Building(b) => TripEndpoint::Bldg(b),
477 SidewalkPOI::Border(i, ref loc) => TripEndpoint::Border(i, loc.clone()),
478 SidewalkPOI::SuddenlyAppear => {
479 TripEndpoint::Border(map.get_l(spot.sidewalk_pos.lane()).src_i, None)
480 }
481 _ => unreachable!(),
482 }
483 }
484 // Pick an arbitrary border
485 SpawnTrip::Remote { ref from, .. } => {
486 TripEndpoint::Border(map.all_outgoing_borders()[0].id, Some(from.clone()))
487 }
488 }
489 }
490
end(&self, map: &Map) -> TripEndpoint491 pub fn end(&self, map: &Map) -> TripEndpoint {
492 match self {
493 SpawnTrip::VehicleAppearing { ref goal, .. }
494 | SpawnTrip::FromBorder { ref goal, .. }
495 | SpawnTrip::UsingParkedCar(_, ref goal)
496 | SpawnTrip::UsingBike(_, ref goal) => match goal {
497 DrivingGoal::ParkNear(b) => TripEndpoint::Bldg(*b),
498 DrivingGoal::Border(i, _, ref loc) => TripEndpoint::Border(*i, loc.clone()),
499 },
500 SpawnTrip::JustWalking(_, ref spot) | SpawnTrip::UsingTransit(_, ref spot, _, _, _) => {
501 match spot.connection {
502 SidewalkPOI::Building(b) => TripEndpoint::Bldg(b),
503 SidewalkPOI::Border(i, ref loc) => TripEndpoint::Border(i, loc.clone()),
504 _ => unreachable!(),
505 }
506 }
507 // Pick an arbitrary border
508 SpawnTrip::Remote { ref to, .. } => {
509 TripEndpoint::Border(map.all_incoming_borders()[0].id, Some(to.clone()))
510 }
511 }
512 }
513
new( from: TripEndpoint, to: TripEndpoint, mode: TripMode, map: &Map, ) -> Option<SpawnTrip>514 pub fn new(
515 from: TripEndpoint,
516 to: TripEndpoint,
517 mode: TripMode,
518 map: &Map,
519 ) -> Option<SpawnTrip> {
520 Some(match mode {
521 TripMode::Drive => match from {
522 TripEndpoint::Bldg(b) => {
523 SpawnTrip::UsingParkedCar(b, to.driving_goal(PathConstraints::Car, map)?)
524 }
525 TripEndpoint::Border(i, ref origin) => SpawnTrip::FromBorder {
526 dr: map.get_i(i).some_outgoing_road(map)?,
527 goal: to.driving_goal(PathConstraints::Car, map)?,
528 is_bike: false,
529 origin: origin.clone(),
530 },
531 },
532 TripMode::Bike => match from {
533 TripEndpoint::Bldg(b) => {
534 SpawnTrip::UsingBike(b, to.driving_goal(PathConstraints::Bike, map)?)
535 }
536 TripEndpoint::Border(i, ref origin) => SpawnTrip::FromBorder {
537 dr: map.get_i(i).some_outgoing_road(map)?,
538 goal: to.driving_goal(PathConstraints::Bike, map)?,
539 is_bike: true,
540 origin: origin.clone(),
541 },
542 },
543 TripMode::Walk => {
544 SpawnTrip::JustWalking(from.start_sidewalk_spot(map)?, to.end_sidewalk_spot(map)?)
545 }
546 TripMode::Transit => {
547 let start = from.start_sidewalk_spot(map)?;
548 let goal = to.end_sidewalk_spot(map)?;
549 if let Some((stop1, maybe_stop2, route)) =
550 map.should_use_transit(start.sidewalk_pos, goal.sidewalk_pos)
551 {
552 SpawnTrip::UsingTransit(start, goal, route, stop1, maybe_stop2)
553 } else {
554 //timer.warn(format!("{:?} not actually using transit, because pathfinding
555 // didn't find any useful route", trip));
556 SpawnTrip::JustWalking(start, goal)
557 }
558 }
559 })
560 }
561 }
562
563 impl PersonSpec {
564 // Verify that the trip start/endpoints of the person match up
check_schedule(&self, map: &Map) -> Result<(), String>565 fn check_schedule(&self, map: &Map) -> Result<(), String> {
566 for pair in self.trips.iter().zip(self.trips.iter().skip(1)) {
567 if pair.0.depart >= pair.1.depart {
568 return Err(format!(
569 "{} {:?} starts two trips in the wrong order: {} then {}",
570 self.id, self.orig_id, pair.0.depart, pair.1.depart
571 ));
572 }
573
574 // Once off-map, re-enter via any border node.
575 let end_bldg = match pair.0.trip.end(map) {
576 TripEndpoint::Bldg(b) => Some(b),
577 TripEndpoint::Border(_, _) => None,
578 };
579 let start_bldg = match pair.1.trip.start(map) {
580 TripEndpoint::Bldg(b) => Some(b),
581 TripEndpoint::Border(_, _) => None,
582 };
583
584 if end_bldg != start_bldg {
585 return Err(format!(
586 "At {}, {} {:?} warps between some trips, from {:?} to {:?}",
587 pair.1.depart, self.id, self.orig_id, end_bldg, start_bldg
588 ));
589 }
590
591 // But actually, make sure pairs of remote trips match up.
592 if let (SpawnTrip::Remote { ref to, .. }, SpawnTrip::Remote { ref from, .. }) =
593 (&pair.0.trip, &pair.1.trip)
594 {
595 if to != from {
596 return Err(format!(
597 "At {}, {} {:?} warps between some trips, from {:?} to {:?}",
598 pair.1.depart, self.id, self.orig_id, to, from
599 ));
600 }
601 }
602 }
603 Ok(())
604 }
605
get_vehicles( &self, rng: &mut XorShiftRng, ) -> ( Vec<VehicleSpec>, Vec<(usize, BuildingID)>, Vec<Option<usize>>, )606 fn get_vehicles(
607 &self,
608 rng: &mut XorShiftRng,
609 ) -> (
610 Vec<VehicleSpec>,
611 Vec<(usize, BuildingID)>,
612 Vec<Option<usize>>,
613 ) {
614 let mut vehicle_specs = Vec::new();
615 let mut cars_initially_parked_at = Vec::new();
616 let mut vehicle_foreach_trip = Vec::new();
617
618 let mut bike_idx = None;
619 // For each indexed car, is it parked somewhere, or off-map?
620 let mut car_locations: Vec<(usize, Option<BuildingID>)> = Vec::new();
621
622 // TODO If the trip is cancelled, this should be affected...
623 for trip in &self.trips {
624 let use_for_trip = match trip.trip {
625 SpawnTrip::VehicleAppearing {
626 is_bike, ref goal, ..
627 }
628 | SpawnTrip::FromBorder {
629 is_bike, ref goal, ..
630 } => {
631 if is_bike {
632 if bike_idx.is_none() {
633 bike_idx = Some(vehicle_specs.len());
634 vehicle_specs.push(Scenario::rand_bike(rng));
635 }
636 bike_idx
637 } else {
638 // Any available cars off-map?
639 let idx = if let Some(idx) = car_locations
640 .iter()
641 .find(|(_, parked_at)| parked_at.is_none())
642 .map(|(idx, _)| *idx)
643 {
644 idx
645 } else {
646 // Need a new car, starting off-map
647 let idx = vehicle_specs.len();
648 vehicle_specs.push(Scenario::rand_car(rng));
649 idx
650 };
651
652 // Where does this car wind up?
653 car_locations.retain(|(i, _)| idx != *i);
654 match goal {
655 DrivingGoal::ParkNear(b) => {
656 car_locations.push((idx, Some(*b)));
657 }
658 DrivingGoal::Border(_, _, _) => {
659 car_locations.push((idx, None));
660 }
661 }
662
663 Some(idx)
664 }
665 }
666 SpawnTrip::UsingParkedCar(b, ref goal) => {
667 // Is there already a car parked here?
668 let idx = if let Some(idx) = car_locations
669 .iter()
670 .find(|(_, parked_at)| *parked_at == Some(b))
671 .map(|(idx, _)| *idx)
672 {
673 idx
674 } else {
675 // Need a new car, starting at this building
676 let idx = vehicle_specs.len();
677 vehicle_specs.push(Scenario::rand_car(rng));
678 cars_initially_parked_at.push((idx, b));
679 idx
680 };
681
682 // Where does this car wind up?
683 car_locations.retain(|(i, _)| idx != *i);
684 match goal {
685 DrivingGoal::ParkNear(b) => {
686 car_locations.push((idx, Some(*b)));
687 }
688 DrivingGoal::Border(_, _, _) => {
689 car_locations.push((idx, None));
690 }
691 }
692
693 Some(idx)
694 }
695 SpawnTrip::UsingBike(_, _) => {
696 if bike_idx.is_none() {
697 bike_idx = Some(vehicle_specs.len());
698 vehicle_specs.push(Scenario::rand_bike(rng));
699 }
700 bike_idx
701 }
702 SpawnTrip::JustWalking(_, _) | SpawnTrip::UsingTransit(_, _, _, _, _) => None,
703 SpawnTrip::Remote { .. } => None,
704 };
705 vehicle_foreach_trip.push(use_for_trip);
706 }
707
708 // For debugging
709 if false {
710 let mut n = vehicle_specs.len();
711 if bike_idx.is_some() {
712 n -= 1;
713 }
714 if n > 1 {
715 println!("{} needs {} cars", self.id, n);
716 }
717 }
718
719 (
720 vehicle_specs,
721 cars_initially_parked_at,
722 vehicle_foreach_trip,
723 )
724 }
725 }
726