mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-27 15:03:20 +03:00
Feature to repeat days with noise on departure (#926)
* Added repeat days with noise function & enum * Added widgets and controls * Modified description * Refactor to have an Option<usize> in signature instead of a whole new similar function * Added noise parameter + xor random shift + clamped substraction * Added ref to the XorShiftRng to ScenarioModifiers::apply calls * Added noise to pushed ScenarioModifier * Finish up the PR: - consistently reuse the RNG in the UI like tests/headless - slight style tweaks Co-authored-by: Dustin Carlino <dabreegster@gmail.com>
This commit is contained in:
parent
34487a3322
commit
d3333d813d
16
Cargo.lock
generated
16
Cargo.lock
generated
@ -3167,14 +3167,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_hc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3206,15 +3205,6 @@ dependencies = [
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_xorshift"
|
||||
version = "0.3.0"
|
||||
@ -3923,6 +3913,8 @@ dependencies = [
|
||||
"geom",
|
||||
"log",
|
||||
"map_model",
|
||||
"rand",
|
||||
"rand_xorshift",
|
||||
"serde",
|
||||
]
|
||||
|
||||
|
@ -259,12 +259,19 @@ impl EditScenarioModifiers {
|
||||
.build_def(ctx),
|
||||
);
|
||||
rows.push(Widget::row(vec![
|
||||
Spinner::widget(ctx, "repeat_days", (2, 14), 2_usize, 1),
|
||||
Spinner::widget(ctx, "repeat_days", (2, 14), 2, 1),
|
||||
ctx.style()
|
||||
.btn_outline
|
||||
.text("Repeat schedule multiple days")
|
||||
.build_def(ctx),
|
||||
]));
|
||||
rows.push(Widget::row(vec![
|
||||
Spinner::widget(ctx, "repeat_days_noise", (2, 14), 2_usize, 1),
|
||||
ctx.style()
|
||||
.btn_outline
|
||||
.text("Repeat schedule multiple days with +/- 10 minutes of noise")
|
||||
.build_def(ctx),
|
||||
]));
|
||||
rows.push(Widget::horiz_separator(ctx, 1.0));
|
||||
rows.push(
|
||||
Widget::row(vec![
|
||||
@ -361,6 +368,17 @@ impl State<App> for EditScenarioModifiers {
|
||||
self.modifiers.clone(),
|
||||
));
|
||||
}
|
||||
"Repeat schedule multiple days with +/- 10 minutes of noise" => {
|
||||
self.modifiers.push(ScenarioModifier::RepeatDaysNoise {
|
||||
days: self.panel.spinner("repeat_days_noise"),
|
||||
departure_time_noise: Duration::minutes(10),
|
||||
});
|
||||
return Transition::Replace(EditScenarioModifiers::new_state(
|
||||
ctx,
|
||||
self.scenario_name.clone(),
|
||||
self.modifiers.clone(),
|
||||
));
|
||||
}
|
||||
x => {
|
||||
if let Some(x) = x.strip_prefix("delete modifier ") {
|
||||
self.modifiers.remove(x.parse::<usize>().unwrap() - 1);
|
||||
|
@ -549,18 +549,22 @@ impl State<App> for SandboxLoader {
|
||||
ctx.loading_screen("instantiate scenario", |_, timer| {
|
||||
app.primary.scenario = Some(scenario.clone());
|
||||
|
||||
// Use the same RNG as we apply scenario modifiers and instantiate the
|
||||
// scenario. One unexpected effect will be that parked car seeding (during
|
||||
// scenario instantiation) may spuriously change if a scenario modifier
|
||||
// uses the RNG. This is at least consistent with the tests, headless mode,
|
||||
// and instantiating a scenario from CLI flags.
|
||||
let mut rng = app.primary.current_flags.sim_flags.make_rng();
|
||||
|
||||
if let GameplayMode::PlayScenario(_, _, ref modifiers) = self.mode {
|
||||
for m in modifiers {
|
||||
scenario = m.apply(&app.primary.map, scenario);
|
||||
scenario = m.apply(&app.primary.map, scenario, &mut rng);
|
||||
}
|
||||
}
|
||||
|
||||
app.primary.sim.instantiate(
|
||||
&scenario,
|
||||
&app.primary.map,
|
||||
&mut app.primary.current_flags.sim_flags.make_rng(),
|
||||
timer,
|
||||
);
|
||||
app.primary
|
||||
.sim
|
||||
.instantiate(&scenario, &app.primary.map, &mut rng, timer);
|
||||
app.primary
|
||||
.sim
|
||||
.tiny_step(&app.primary.map, &mut app.primary.sim_cb);
|
||||
@ -572,6 +576,8 @@ impl State<App> for SandboxLoader {
|
||||
secondary.sim.instantiate(
|
||||
&scenario,
|
||||
&secondary.map,
|
||||
// Start fresh here. This will match up with the primary sim,
|
||||
// unless modifiers used the RNG
|
||||
&mut secondary.current_flags.sim_flags.make_rng(),
|
||||
timer,
|
||||
);
|
||||
|
@ -29,7 +29,7 @@ pub fn run(
|
||||
}
|
||||
|
||||
for m in modifiers {
|
||||
scenario = m.apply(&map, scenario);
|
||||
scenario = m.apply(&map, scenario, &mut rng);
|
||||
}
|
||||
|
||||
if should_delete_cancelled_trips {
|
||||
|
@ -510,11 +510,11 @@ impl LoadSim {
|
||||
map.recalculate_pathfinding_after_edits(timer);
|
||||
}
|
||||
|
||||
let mut rng = XorShiftRng::seed_from_u64(self.rng_seed);
|
||||
for m in &self.modifiers {
|
||||
scenario = m.apply(&map, scenario);
|
||||
scenario = m.apply(&map, scenario, &mut rng);
|
||||
}
|
||||
|
||||
let mut rng = XorShiftRng::seed_from_u64(self.rng_seed);
|
||||
let mut sim = Sim::new(&map, self.opts.clone());
|
||||
sim.instantiate(&scenario, &map, &mut rng, timer);
|
||||
|
||||
|
@ -108,7 +108,7 @@ impl SimFlags {
|
||||
let map = Map::load_synchronously(scenario.map_name.path(), timer);
|
||||
|
||||
for m in &self.scenario_modifiers {
|
||||
scenario = m.apply(&map, scenario);
|
||||
scenario = m.apply(&map, scenario, &mut rng);
|
||||
}
|
||||
|
||||
if opts.run_name == "unnamed" {
|
||||
|
@ -10,4 +10,6 @@ anyhow = "1.0.38"
|
||||
geom = { path = "../geom" }
|
||||
log = "0.4.14"
|
||||
map_model = { path = "../map_model" }
|
||||
rand = "0.8.5"
|
||||
rand_xorshift = "0.3.0"
|
||||
serde = "1.0.123"
|
||||
|
@ -1,5 +1,9 @@
|
||||
extern crate rand;
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use rand::Rng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use abstutil::Timer;
|
||||
@ -12,6 +16,10 @@ use crate::{Scenario, TripMode};
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize)]
|
||||
pub enum ScenarioModifier {
|
||||
RepeatDays(usize),
|
||||
RepeatDaysNoise {
|
||||
days: usize,
|
||||
departure_time_noise: Duration,
|
||||
},
|
||||
ChangeMode {
|
||||
pct_ppl: usize,
|
||||
departure_filter: (Time, Time),
|
||||
@ -26,9 +34,13 @@ pub enum ScenarioModifier {
|
||||
impl ScenarioModifier {
|
||||
/// If this modifies scenario_name, then that means prebaked results don't match up and
|
||||
/// shouldn't be used.
|
||||
pub fn apply(&self, map: &Map, mut s: Scenario) -> Scenario {
|
||||
pub fn apply(&self, map: &Map, mut s: Scenario, rng: &mut XorShiftRng) -> Scenario {
|
||||
match self {
|
||||
ScenarioModifier::RepeatDays(n) => repeat_days(s, *n),
|
||||
ScenarioModifier::RepeatDays(n) => repeat_days(s, *n, None, rng),
|
||||
ScenarioModifier::RepeatDaysNoise {
|
||||
days,
|
||||
departure_time_noise,
|
||||
} => repeat_days(s, *days, Some(*departure_time_noise), rng),
|
||||
ScenarioModifier::ChangeMode {
|
||||
pct_ppl,
|
||||
departure_filter,
|
||||
@ -90,6 +102,13 @@ impl ScenarioModifier {
|
||||
pub fn describe(&self) -> String {
|
||||
match self {
|
||||
ScenarioModifier::RepeatDays(n) => format!("repeat the entire day {} times", n),
|
||||
ScenarioModifier::RepeatDaysNoise {
|
||||
days,
|
||||
departure_time_noise,
|
||||
} => format!(
|
||||
"repeat the entire day {} times with +/- {} noise on each departure",
|
||||
days, departure_time_noise
|
||||
),
|
||||
ScenarioModifier::ChangeMode {
|
||||
pct_ppl,
|
||||
to_mode,
|
||||
@ -117,7 +136,12 @@ impl ScenarioModifier {
|
||||
//
|
||||
// The bigger problem is that any people that seem to require multiple cars... will wind up
|
||||
// needing LOTS of cars.
|
||||
fn repeat_days(mut s: Scenario, days: usize) -> Scenario {
|
||||
fn repeat_days(
|
||||
mut s: Scenario,
|
||||
days: usize,
|
||||
noise: Option<Duration>,
|
||||
rng: &mut XorShiftRng,
|
||||
) -> Scenario {
|
||||
s.scenario_name = format!("{} (repeated {} days)", s.scenario_name, days);
|
||||
for person in &mut s.people {
|
||||
let mut trips = Vec::new();
|
||||
@ -126,6 +150,13 @@ fn repeat_days(mut s: Scenario, days: usize) -> Scenario {
|
||||
for trip in &person.trips {
|
||||
let mut new = trip.clone();
|
||||
new.depart += offset;
|
||||
if let Some(noise_v) = noise {
|
||||
// + or - noise_v
|
||||
let noise_rnd = Duration::seconds(
|
||||
rng.gen_range((0.0)..=(2.0 * noise_v.inner_seconds() as f64)),
|
||||
) - noise_v;
|
||||
new.depart = new.depart.clamped_sub(noise_rnd);
|
||||
}
|
||||
new.modified = true;
|
||||
trips.push(new);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user