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]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.4"
|
version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"rand_chacha",
|
"rand_chacha",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
"rand_hc",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3206,15 +3205,6 @@ dependencies = [
|
|||||||
"rand",
|
"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]]
|
[[package]]
|
||||||
name = "rand_xorshift"
|
name = "rand_xorshift"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
@ -3923,6 +3913,8 @@ dependencies = [
|
|||||||
"geom",
|
"geom",
|
||||||
"log",
|
"log",
|
||||||
"map_model",
|
"map_model",
|
||||||
|
"rand",
|
||||||
|
"rand_xorshift",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -259,12 +259,19 @@ impl EditScenarioModifiers {
|
|||||||
.build_def(ctx),
|
.build_def(ctx),
|
||||||
);
|
);
|
||||||
rows.push(Widget::row(vec![
|
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()
|
ctx.style()
|
||||||
.btn_outline
|
.btn_outline
|
||||||
.text("Repeat schedule multiple days")
|
.text("Repeat schedule multiple days")
|
||||||
.build_def(ctx),
|
.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::horiz_separator(ctx, 1.0));
|
||||||
rows.push(
|
rows.push(
|
||||||
Widget::row(vec![
|
Widget::row(vec![
|
||||||
@ -361,6 +368,17 @@ impl State<App> for EditScenarioModifiers {
|
|||||||
self.modifiers.clone(),
|
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 => {
|
x => {
|
||||||
if let Some(x) = x.strip_prefix("delete modifier ") {
|
if let Some(x) = x.strip_prefix("delete modifier ") {
|
||||||
self.modifiers.remove(x.parse::<usize>().unwrap() - 1);
|
self.modifiers.remove(x.parse::<usize>().unwrap() - 1);
|
||||||
|
@ -549,18 +549,22 @@ impl State<App> for SandboxLoader {
|
|||||||
ctx.loading_screen("instantiate scenario", |_, timer| {
|
ctx.loading_screen("instantiate scenario", |_, timer| {
|
||||||
app.primary.scenario = Some(scenario.clone());
|
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 {
|
if let GameplayMode::PlayScenario(_, _, ref modifiers) = self.mode {
|
||||||
for m in modifiers {
|
for m in modifiers {
|
||||||
scenario = m.apply(&app.primary.map, scenario);
|
scenario = m.apply(&app.primary.map, scenario, &mut rng);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
app.primary.sim.instantiate(
|
app.primary
|
||||||
&scenario,
|
.sim
|
||||||
&app.primary.map,
|
.instantiate(&scenario, &app.primary.map, &mut rng, timer);
|
||||||
&mut app.primary.current_flags.sim_flags.make_rng(),
|
|
||||||
timer,
|
|
||||||
);
|
|
||||||
app.primary
|
app.primary
|
||||||
.sim
|
.sim
|
||||||
.tiny_step(&app.primary.map, &mut app.primary.sim_cb);
|
.tiny_step(&app.primary.map, &mut app.primary.sim_cb);
|
||||||
@ -572,6 +576,8 @@ impl State<App> for SandboxLoader {
|
|||||||
secondary.sim.instantiate(
|
secondary.sim.instantiate(
|
||||||
&scenario,
|
&scenario,
|
||||||
&secondary.map,
|
&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(),
|
&mut secondary.current_flags.sim_flags.make_rng(),
|
||||||
timer,
|
timer,
|
||||||
);
|
);
|
||||||
|
@ -29,7 +29,7 @@ pub fn run(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for m in modifiers {
|
for m in modifiers {
|
||||||
scenario = m.apply(&map, scenario);
|
scenario = m.apply(&map, scenario, &mut rng);
|
||||||
}
|
}
|
||||||
|
|
||||||
if should_delete_cancelled_trips {
|
if should_delete_cancelled_trips {
|
||||||
|
@ -510,11 +510,11 @@ impl LoadSim {
|
|||||||
map.recalculate_pathfinding_after_edits(timer);
|
map.recalculate_pathfinding_after_edits(timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut rng = XorShiftRng::seed_from_u64(self.rng_seed);
|
||||||
for m in &self.modifiers {
|
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());
|
let mut sim = Sim::new(&map, self.opts.clone());
|
||||||
sim.instantiate(&scenario, &map, &mut rng, timer);
|
sim.instantiate(&scenario, &map, &mut rng, timer);
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ impl SimFlags {
|
|||||||
let map = Map::load_synchronously(scenario.map_name.path(), timer);
|
let map = Map::load_synchronously(scenario.map_name.path(), timer);
|
||||||
|
|
||||||
for m in &self.scenario_modifiers {
|
for m in &self.scenario_modifiers {
|
||||||
scenario = m.apply(&map, scenario);
|
scenario = m.apply(&map, scenario, &mut rng);
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.run_name == "unnamed" {
|
if opts.run_name == "unnamed" {
|
||||||
|
@ -10,4 +10,6 @@ anyhow = "1.0.38"
|
|||||||
geom = { path = "../geom" }
|
geom = { path = "../geom" }
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
map_model = { path = "../map_model" }
|
map_model = { path = "../map_model" }
|
||||||
|
rand = "0.8.5"
|
||||||
|
rand_xorshift = "0.3.0"
|
||||||
serde = "1.0.123"
|
serde = "1.0.123"
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
|
extern crate rand;
|
||||||
|
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
|
use rand::Rng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use abstutil::Timer;
|
use abstutil::Timer;
|
||||||
@ -12,6 +16,10 @@ use crate::{Scenario, TripMode};
|
|||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize)]
|
||||||
pub enum ScenarioModifier {
|
pub enum ScenarioModifier {
|
||||||
RepeatDays(usize),
|
RepeatDays(usize),
|
||||||
|
RepeatDaysNoise {
|
||||||
|
days: usize,
|
||||||
|
departure_time_noise: Duration,
|
||||||
|
},
|
||||||
ChangeMode {
|
ChangeMode {
|
||||||
pct_ppl: usize,
|
pct_ppl: usize,
|
||||||
departure_filter: (Time, Time),
|
departure_filter: (Time, Time),
|
||||||
@ -26,9 +34,13 @@ pub enum ScenarioModifier {
|
|||||||
impl ScenarioModifier {
|
impl ScenarioModifier {
|
||||||
/// If this modifies scenario_name, then that means prebaked results don't match up and
|
/// If this modifies scenario_name, then that means prebaked results don't match up and
|
||||||
/// shouldn't be used.
|
/// 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 {
|
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 {
|
ScenarioModifier::ChangeMode {
|
||||||
pct_ppl,
|
pct_ppl,
|
||||||
departure_filter,
|
departure_filter,
|
||||||
@ -90,6 +102,13 @@ impl ScenarioModifier {
|
|||||||
pub fn describe(&self) -> String {
|
pub fn describe(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
ScenarioModifier::RepeatDays(n) => format!("repeat the entire day {} times", n),
|
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 {
|
ScenarioModifier::ChangeMode {
|
||||||
pct_ppl,
|
pct_ppl,
|
||||||
to_mode,
|
to_mode,
|
||||||
@ -117,7 +136,12 @@ impl ScenarioModifier {
|
|||||||
//
|
//
|
||||||
// The bigger problem is that any people that seem to require multiple cars... will wind up
|
// The bigger problem is that any people that seem to require multiple cars... will wind up
|
||||||
// needing LOTS of cars.
|
// 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);
|
s.scenario_name = format!("{} (repeated {} days)", s.scenario_name, days);
|
||||||
for person in &mut s.people {
|
for person in &mut s.people {
|
||||||
let mut trips = Vec::new();
|
let mut trips = Vec::new();
|
||||||
@ -126,6 +150,13 @@ fn repeat_days(mut s: Scenario, days: usize) -> Scenario {
|
|||||||
for trip in &person.trips {
|
for trip in &person.trips {
|
||||||
let mut new = trip.clone();
|
let mut new = trip.clone();
|
||||||
new.depart += offset;
|
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;
|
new.modified = true;
|
||||||
trips.push(new);
|
trips.push(new);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user