diff --git a/docs/design/sim.md b/docs/design/sim.md index 4f57c246a4..6a145ba7db 100644 --- a/docs/design/sim.md +++ b/docs/design/sim.md @@ -136,7 +136,7 @@ though... Can we fork RNG for that too? Problems: - CarIDs are different, could make them be original parking spot - Missing trip ID, different ped IDs -- gen_range on different inputs += gen_range on different inputs diff --git a/sim/src/lib.rs b/sim/src/lib.rs index b6fdf8ea42..514e0eea61 100644 --- a/sim/src/lib.rs +++ b/sim/src/lib.rs @@ -59,6 +59,7 @@ use geom::{Angle, Pt2D}; pub use helpers::{load, SimFlags}; pub use instrument::save_backtraces; use map_model::{LaneID, Trace, TurnID}; +use rand::{Rng, SeedableRng, XorShiftRng}; pub use scenario::{Neighborhood, Scenario, SeedParkedCars, SpawnOverTime}; pub use sim::{Benchmark, Sim}; use std::fmt; @@ -322,3 +323,7 @@ impl Cloneable for Scenario {} impl Cloneable for Tick {} impl Cloneable for MapEdits {} impl Cloneable for ABTest {} + +fn fork_rng(base_rng: &mut R) -> XorShiftRng { + XorShiftRng::from_seed([base_rng.next_u32() as u8; 16]) +} diff --git a/sim/src/scenario.rs b/sim/src/scenario.rs index 145d4bcc06..8c02067cc6 100644 --- a/sim/src/scenario.rs +++ b/sim/src/scenario.rs @@ -2,8 +2,8 @@ use abstutil; use geom::{Polygon, Pt2D}; use map_model::{BuildingID, LaneID, Map}; use rand::Rng; -use std::collections::HashMap; -use {ParkedCar, Sim, Tick}; +use std::collections::{BTreeMap, HashMap}; +use {fork_rng, ParkedCar, Sim, Tick}; #[derive(Clone, Serialize, Deserialize, Debug)] pub struct Scenario { @@ -101,7 +101,7 @@ impl Scenario { ); } - let mut parked_cars_per_neighborhood: HashMap> = HashMap::new(); + let mut parked_cars_per_neighborhood: BTreeMap> = BTreeMap::new(); for (name, neighborhood) in &neighborhoods { parked_cars_per_neighborhood.insert( name.to_string(), @@ -109,6 +109,11 @@ impl Scenario { .get_all_parked_cars(Some(&Polygon::new(&neighborhood.points))), ); } + // Shuffle the list of parked cars, but be sure to fork the RNG to be stable across map + // edits. + for cars in parked_cars_per_neighborhood.values_mut() { + fork_rng(&mut sim.rng).shuffle(cars); + } for s in &self.spawn_over_time { for _ in 0..s.num_agents { @@ -126,22 +131,16 @@ impl Scenario { .unwrap(); if sim.rng.gen_bool(s.percent_drive) { - if parked_cars_per_neighborhood[&s.start_from_neighborhood].is_empty() { - panic!( - "{} has no parked cars; can't instantiate {}", - s.start_from_neighborhood, self.scenario_name - ); - } // TODO Probably prefer parked cars close to from_bldg, unless the particular // area is tight on parking. :) - let idx = sim.rng.gen_range( - 0, - parked_cars_per_neighborhood[&s.start_from_neighborhood].len(), - ); let parked_car = parked_cars_per_neighborhood .get_mut(&s.start_from_neighborhood) .unwrap() - .remove(idx); + .pop() + .expect(&format!( + "{} has no parked cars; can't instantiate {}", + s.start_from_neighborhood, self.scenario_name + )); sim.spawner.start_trip_using_parked_car( spawn_time, diff --git a/sim/src/spawn.rs b/sim/src/spawn.rs index a64267fed2..609aed36b1 100644 --- a/sim/src/spawn.rs +++ b/sim/src/spawn.rs @@ -3,14 +3,16 @@ use driving::DrivingSimState; use kinematics::Vehicle; use map_model::{BuildingID, BusRoute, BusStopID, LaneID, Map, Pathfinder}; use parking::ParkingSimState; -use rand::{Rng, SeedableRng, XorShiftRng}; +use rand::Rng; use router::Router; use std::collections::VecDeque; use std::time::Instant; use transit::TransitSimState; use trips::{TripLeg, TripManager}; use walking::{SidewalkSpot, WalkingSimState}; -use {AgentID, CarID, Event, ParkedCar, ParkingSpot, PedestrianID, RouteID, Tick, TripID}; +use { + fork_rng, AgentID, CarID, Event, ParkedCar, ParkingSpot, PedestrianID, RouteID, Tick, TripID, +}; #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] enum Command { @@ -213,7 +215,7 @@ impl Spawner { // Fork a new RNG for each candidate lane. This keeps things more deterministic, invariant // of lane edits. for l in in_lanes.into_iter() { - let mut rng = XorShiftRng::from_seed([base_rng.next_u32() as u8; 16]); + let mut rng = fork_rng(base_rng); for spot in parking_sim.get_free_spots(l) { total_capacity += 1;