diff --git a/docs/project/references.md b/docs/project/references.md index 8ce23958c6..ab53eaa74d 100644 --- a/docs/project/references.md +++ b/docs/project/references.md @@ -30,6 +30,8 @@ - https://blogs.uw.edu/ceadvice/2019/05/08/infrastructure-week-2019-welcome-uw-cee-students-and-faculty/ - https://escience.washington.edu/dssg/ - josie kresner from transport foundry +- https://www.citylab.com/transportation/2019/08/city-planning-transportation-oakland-community-engagement/596050/ + - tweeting small problems -> bug tracker ## Similar projects diff --git a/editor/src/abtest/setup.rs b/editor/src/abtest/setup.rs index e2153b25c4..b5543e768d 100644 --- a/editor/src/abtest/setup.rs +++ b/editor/src/abtest/setup.rs @@ -156,8 +156,9 @@ fn launch_test(test: &ABTest, ui: &mut UI, ctx: &mut EventCtx) -> ABTestMode { load, rng_seed: current_flags.sim_flags.rng_seed, run_name: Some(format!("{} with {}", test.test_name, test.edits2_name)), - freeform_policy: current_flags.sim_flags.freeform_policy, savestate_every: None, + freeform_policy: current_flags.sim_flags.freeform_policy, + disable_block_the_box: current_flags.sim_flags.disable_block_the_box, }, ..current_flags.clone() }, diff --git a/editor/src/ui.rs b/editor/src/ui.rs index 1cd5e5c793..efae334e32 100644 --- a/editor/src/ui.rs +++ b/editor/src/ui.rs @@ -516,6 +516,7 @@ impl PerMapUI { .unwrap_or_else(|| "unnamed".to_string()), savestate_every: flags.savestate_every, use_freeform_policy_everywhere: flags.freeform_policy, + disable_block_the_box: flags.disable_block_the_box, }, ); } diff --git a/sim/src/make/load.rs b/sim/src/make/load.rs index 23077a8e4b..c45ce10e27 100644 --- a/sim/src/make/load.rs +++ b/sim/src/make/load.rs @@ -26,13 +26,17 @@ pub struct SimFlags { #[structopt(long = "run_name")] pub run_name: Option, + /// Regularly save simulation state + #[structopt(long = "savestate_every")] + pub savestate_every: Option, + /// Use freeform intersection policy everywhere #[structopt(long = "freeform_policy")] pub freeform_policy: bool, - /// Regularly save simulation state - #[structopt(long = "savestate_every")] - pub savestate_every: Option, + /// Disable block-the-box prevention + #[structopt(long = "disable_block_the_box")] + pub disable_block_the_box: bool, } impl SimFlags { @@ -46,8 +50,9 @@ impl SimFlags { load: PathBuf::from(abstutil::path_map(map)), rng_seed: Some(42), run_name: Some(run_name.to_string()), - freeform_policy: false, savestate_every: None, + freeform_policy: false, + disable_block_the_box: false, } } @@ -70,6 +75,7 @@ impl SimFlags { .unwrap_or_else(|| "unnamed".to_string()), savestate_every: self.savestate_every, use_freeform_policy_everywhere: self.freeform_policy, + disable_block_the_box: self.disable_block_the_box, }; if self.load.starts_with(Path::new("../data/save/")) { diff --git a/sim/src/mechanics/intersection.rs b/sim/src/mechanics/intersection.rs index 12981cd2fc..df5958e599 100644 --- a/sim/src/mechanics/intersection.rs +++ b/sim/src/mechanics/intersection.rs @@ -16,6 +16,7 @@ const WAIT_AT_STOP_SIGN: Duration = Duration::const_seconds(0.5); pub struct IntersectionSimState { state: BTreeMap, use_freeform_policy_everywhere: bool, + force_queue_entry: bool, } #[derive(Serialize, Deserialize, PartialEq)] @@ -35,10 +36,12 @@ impl IntersectionSimState { map: &Map, scheduler: &mut Scheduler, use_freeform_policy_everywhere: bool, + disable_block_the_box: bool, ) -> IntersectionSimState { let mut sim = IntersectionSimState { state: BTreeMap::new(), use_freeform_policy_everywhere, + force_queue_entry: disable_block_the_box, }; for i in map.all_intersections() { sim.state.insert( @@ -140,15 +143,22 @@ impl IntersectionSimState { state.waiting.entry(req.clone()).or_insert(now); let allowed = if self.use_freeform_policy_everywhere { - state.freeform_policy(&req, map, maybe_car_and_target_queue) + state.freeform_policy(&req, map) } else if let Some(ref signal) = map.maybe_get_traffic_signal(state.id) { - state.traffic_signal_policy(signal, &req, speed, now, maybe_car_and_target_queue, map) + state.traffic_signal_policy(signal, &req, speed, now, map) } else if let Some(ref sign) = map.maybe_get_stop_sign(state.id) { - state.stop_sign_policy(sign, &req, now, map, scheduler, maybe_car_and_target_queue) + state.stop_sign_policy(sign, &req, now, map, scheduler) } else { unreachable!() }; + // Don't block the box + if let Some((queue, car)) = maybe_car_and_target_queue { + if !queue.try_to_reserve_entry(car, self.force_queue_entry) { + return false; + } + } + if allowed { assert!(!state.any_accepted_conflict_with(turn, map)); state.waiting.remove(&req).unwrap(); @@ -187,24 +197,12 @@ impl State { .any(|req| map.get_t(req.turn).conflicts_with(turn)) } - fn freeform_policy( - &self, - req: &Request, - map: &Map, - maybe_car_and_target_queue: Option<(&mut Queue, &Car)>, - ) -> bool { + fn freeform_policy(&self, req: &Request, map: &Map) -> bool { // Allow concurrent turns that don't conflict if self.any_accepted_conflict_with(req.turn, map) { return false; } - // Don't block the box - if let Some((queue, car)) = maybe_car_and_target_queue { - if !queue.try_to_reserve_entry(car) { - return false; - } - } - true } @@ -215,7 +213,6 @@ impl State { now: Duration, map: &Map, scheduler: &mut Scheduler, - maybe_car_and_target_queue: Option<(&mut Queue, &Car)>, ) -> bool { if self.any_accepted_conflict_with(req.turn, map) { return false; @@ -251,13 +248,6 @@ impl State { // TODO Make sure we can optimistically finish this turn before an approaching // higher-priority vehicle wants to begin. - // Don't block the box - if let Some((queue, car)) = maybe_car_and_target_queue { - if !queue.try_to_reserve_entry(car) { - return false; - } - } - true } @@ -267,7 +257,6 @@ impl State { new_req: &Request, speed: Speed, time: Duration, - maybe_car_and_target_queue: Option<(&mut Queue, &Car)>, map: &Map, ) -> bool { let turn = map.get_t(new_req.turn); @@ -315,13 +304,6 @@ impl State { } } - // Don't block the box - if let Some((queue, car)) = maybe_car_and_target_queue { - if !queue.try_to_reserve_entry(car) { - return false; - } - } - true } } diff --git a/sim/src/mechanics/queue.rs b/sim/src/mechanics/queue.rs index 03c0444c54..9d33e5dde8 100644 --- a/sim/src/mechanics/queue.rs +++ b/sim/src/mechanics/queue.rs @@ -163,12 +163,16 @@ impl Queue { // If true, there's room and the car must actually start the turn (because the space is // reserved). - pub fn try_to_reserve_entry(&mut self, car: &Car) -> bool { + pub fn try_to_reserve_entry(&mut self, car: &Car, force_entry: bool) -> bool { // Sometimes a car + FOLLOWING_DISTANCE might be longer than the geom_len entirely. In that // case, it just means the car won't totally fit on the queue at once, which is fine. // Reserve the normal amount of space; the next car trying to enter will get rejected. + // Also allow this don't-block-the-box prevention to be disabled. let dist = car.vehicle.length + FOLLOWING_DISTANCE; - if self.reserved_length + dist < self.geom_len || self.reserved_length == Distance::ZERO { + if self.reserved_length + dist < self.geom_len + || self.reserved_length == Distance::ZERO + || force_entry + { self.reserved_length += dist; return true; } diff --git a/sim/src/sim.rs b/sim/src/sim.rs index a81a379cec..755382fe3e 100644 --- a/sim/src/sim.rs +++ b/sim/src/sim.rs @@ -57,6 +57,7 @@ pub struct SimOptions { pub run_name: String, pub savestate_every: Option, pub use_freeform_policy_everywhere: bool, + pub disable_block_the_box: bool, } impl SimOptions { @@ -65,6 +66,7 @@ impl SimOptions { run_name: run_name.to_string(), savestate_every: None, use_freeform_policy_everywhere: false, + disable_block_the_box: false, } } } @@ -88,6 +90,7 @@ impl Sim { map, &mut scheduler, opts.use_freeform_policy_everywhere, + opts.disable_block_the_box, ), transit: TransitSimState::new(), trips: TripManager::new(),