very simple modifier to cancel all trips for some percentage of people

This commit is contained in:
Dustin Carlino 2020-06-27 14:20:25 -07:00
parent 455b17faef
commit a6b90d00d8
13 changed files with 115 additions and 44 deletions

View File

@ -190,18 +190,14 @@ impl<'a, 'b> WrappedWizard<'a, 'b> {
)
}
pub fn input_percent(&mut self, query: &str) -> Option<f64> {
pub fn input_percent(&mut self, query: &str) -> Option<usize> {
self.input_something(
query,
None,
Box::new(|line| {
line.parse::<f64>().ok().and_then(|num| {
if num >= 0.0 && num <= 1.0 {
Some(num)
} else {
None
}
})
line.parse::<usize>()
.ok()
.and_then(|num| if num <= 100 { Some(num) } else { None })
}),
)
}

View File

@ -333,14 +333,22 @@ fn make(ctx: &mut EventCtx, app: &App, opts: &Options) -> Composite {
])
.margin_below(5),
);
let (_, unfinished, _) = app.primary.sim.num_trips();
col.push(
format!(
"{} trips aborted due to simulation glitch",
prettyprint_usize(aborted)
)
.draw_text(ctx)
.margin_below(5),
Text::from_multiline(vec![
Line(format!(
"{} trips aborted due to simulation glitch",
prettyprint_usize(aborted)
)),
Line(format!(
"{} unfinished trips remaining",
prettyprint_usize(unfinished)
)),
])
.draw(ctx)
.margin_below(10),
);
col.push(
Widget::row(vec![
if opts.skip > 0 {

View File

@ -263,6 +263,7 @@ impl State for AgentSpawner {
self.composite.dropdown_value("mode"),
map,
),
cancelled: false,
}],
});
}
@ -529,6 +530,7 @@ pub fn spawn_agents_around(i: IntersectionID, app: &mut App) {
origin: None,
},
TripEndpoint::Border(lane.src_i, None),
false,
map,
);
}
@ -549,6 +551,7 @@ pub fn spawn_agents_around(i: IntersectionID, app: &mut App) {
),
},
TripEndpoint::Border(lane.src_i, None),
false,
map,
);
}

View File

@ -126,7 +126,7 @@ impl GameplayMode {
};
if let GameplayMode::PlayScenario(_, _, ref modifiers) = self {
for m in modifiers {
scenario = m.apply(scenario);
scenario = m.apply(scenario, &mut rng);
}
}
scenario

View File

@ -258,19 +258,27 @@ impl State for EditScenarioModifiers {
fn new_modifier(scenario_name: String, modifiers: Vec<ScenarioModifier>) -> Box<dyn State> {
WizardState::new(Box::new(move |wiz, ctx, app| {
let mut wizard = wiz.wrap(ctx);
match wizard.choose_string("", || vec!["repeat days"])?.as_str() {
x if x == "repeat days" => {
let n = wizard.input_usize("Repeat everyone's schedule how many days?")?;
let mut mods = modifiers.clone();
mods.push(ScenarioModifier::RepeatDays(n));
Some(Transition::PopThenReplace(EditScenarioModifiers::new(
ctx,
app,
scenario_name.clone(),
mods,
)))
}
let new_mod = match wizard
.choose_string("", || {
vec!["repeat days", "cancel all trips for some people"]
})?
.as_str()
{
x if x == "repeat days" => ScenarioModifier::RepeatDays(
wizard.input_usize("Repeat everyone's schedule how many days?")?,
),
x if x == "cancel all trips for some people" => ScenarioModifier::CancelPeople(
wizard.input_percent("What percent of people should cancel trips? (0 to 100)")?,
),
_ => unreachable!(),
}
};
let mut mods = modifiers.clone();
mods.push(new_mod);
Some(Transition::PopThenReplace(EditScenarioModifiers::new(
ctx,
app,
scenario_name.clone(),
mods,
)))
}))
}

View File

@ -1097,6 +1097,7 @@ impl TutorialState {
goal: DrivingGoal::ParkNear(goal_bldg),
is_bike: false,
},
cancelled: false,
}],
});
// Will definitely get there first
@ -1114,6 +1115,7 @@ impl TutorialState {
goal: DrivingGoal::ParkNear(goal_bldg),
is_bike: false,
},
cancelled: false,
}],
});
}

View File

@ -741,8 +741,8 @@ fn compare_count(after: usize, before: usize) -> String {
if after == before {
"+0".to_string()
} else if after > before {
format!("+{}", after - before)
format!("+{}", prettyprint_usize(after - before))
} else {
format!("-{}", before - after)
format!("-{}", prettyprint_usize(before - after))
}
}

View File

@ -258,7 +258,11 @@ pub fn make_weekday_scenario(
})
{
let idx = individ_trips.len();
individ_trips.push(Some(IndividTrip { depart, trip }));
individ_trips.push(Some(IndividTrip {
depart,
trip,
cancelled: false,
}));
trips_per_person.insert(person, (seq, idx));
}
timer.note(format!(
@ -328,6 +332,7 @@ pub fn make_weekday_scenario_with_everyone(
individ_trips.push(Some(IndividTrip {
depart: orig_trip.depart_at,
trip,
cancelled: false,
}));
trips_per_person.insert(orig_trip.person, (orig_trip.seq, idx));
}

View File

@ -185,6 +185,7 @@ impl SpawnOverTime {
trips: vec![IndividTrip {
depart,
trip: SpawnTrip::UsingParkedCar(from_bldg, goal),
cancelled: false,
}],
});
return;
@ -204,6 +205,7 @@ impl SpawnOverTime {
trips: vec![IndividTrip {
depart,
trip: SpawnTrip::UsingBike(start_spot, goal),
cancelled: false,
}],
});
return;
@ -228,6 +230,7 @@ impl SpawnOverTime {
trips: vec![IndividTrip {
depart,
trip: SpawnTrip::UsingTransit(start_spot, goal, route, stop1, stop2),
cancelled: false,
}],
});
return;
@ -240,6 +243,7 @@ impl SpawnOverTime {
trips: vec![IndividTrip {
depart,
trip: SpawnTrip::JustWalking(start_spot, goal),
cancelled: false,
}],
});
return;
@ -295,6 +299,7 @@ impl BorderSpawnOverTime {
stop1,
stop2,
),
cancelled: false,
}],
});
continue;
@ -307,6 +312,7 @@ impl BorderSpawnOverTime {
trips: vec![IndividTrip {
depart,
trip: SpawnTrip::JustWalking(start.clone(), goal),
cancelled: false,
}],
});
}
@ -337,6 +343,7 @@ impl BorderSpawnOverTime {
is_bike: constraints == PathConstraints::Bike,
origin: None,
},
cancelled: false,
}],
});
}

View File

@ -1,23 +1,30 @@
use crate::{IndividTrip, Scenario};
use geom::Duration;
use rand::Rng;
use rand_xorshift::XorShiftRng;
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)]
pub enum ScenarioModifier {
RepeatDays(usize),
CancelPeople(usize),
}
impl ScenarioModifier {
pub fn apply(&self, s: Scenario) -> Scenario {
let mut s = match self {
// If this modifies scenario_name, then that means prebaked results don't match up and
// shouldn't be used.
pub fn apply(&self, s: Scenario, rng: &mut XorShiftRng) -> Scenario {
match self {
ScenarioModifier::RepeatDays(n) => repeat_days(s, *n),
};
s.scenario_name = format!("{} (modified)", s.scenario_name);
s
ScenarioModifier::CancelPeople(pct) => cancel_people(s, *pct, rng),
}
}
pub fn describe(&self) -> String {
match self {
ScenarioModifier::RepeatDays(n) => format!("repeat the entire day {} times", n),
ScenarioModifier::CancelPeople(pct) => {
format!("cancel all trips for {}% of people", pct)
}
}
}
}
@ -31,6 +38,7 @@ 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 {
s.scenario_name = format!("{} (repeated {} days)", s.scenario_name, days);
for person in &mut s.people {
let mut trips = Vec::new();
let mut offset = Duration::ZERO;
@ -39,6 +47,7 @@ fn repeat_days(mut s: Scenario, days: usize) -> Scenario {
trips.push(IndividTrip {
depart: trip.depart + offset,
trip: trip.trip.clone(),
cancelled: false,
});
}
offset += Duration::hours(24);
@ -47,3 +56,17 @@ fn repeat_days(mut s: Scenario, days: usize) -> Scenario {
}
s
}
fn cancel_people(mut s: Scenario, pct: usize, rng: &mut XorShiftRng) -> Scenario {
let pct = (pct as f64) / 100.0;
for person in &mut s.people {
if rng.gen_bool(pct) {
// TODO It's not obvious how to cancel individual trips. How are later trips affected?
// What if a car doesn't get moved to another place?
for trip in &mut person.trips {
trip.cancelled = true;
}
}
}
s
}

View File

@ -37,6 +37,7 @@ pub struct PersonSpec {
pub struct IndividTrip {
pub depart: Time,
pub trip: SpawnTrip,
pub cancelled: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
@ -124,7 +125,7 @@ impl Scenario {
&mut tmp_rng,
map,
);
spawner.schedule_trip(person, t.depart, spec, t.trip.start(map), map);
spawner.schedule_trip(person, t.depart, spec, t.trip.start(map), t.cancelled, map);
}
}
@ -575,6 +576,7 @@ impl PersonSpec {
// For each indexed car, is it parked somewhere, or off-map?
let mut car_locations: Vec<(usize, Option<BuildingID>)> = Vec::new();
// TODO If the trip is cancelled, this should be affected...
for trip in &self.trips {
let use_for_trip = match trip.trip {
SpawnTrip::VehicleAppearing {

View File

@ -62,7 +62,7 @@ pub enum TripSpec {
// This structure is created temporarily by a Scenario or to interactively spawn agents.
pub struct TripSpawner {
trips: Vec<(PersonID, Time, TripSpec, TripEndpoint)>,
trips: Vec<(PersonID, Time, TripSpec, TripEndpoint, bool)>,
}
impl TripSpawner {
@ -76,6 +76,7 @@ impl TripSpawner {
start_time: Time,
spec: TripSpec,
trip_start: TripEndpoint,
cancelled: bool,
map: &Map,
) {
// TODO We'll want to repeat this validation when we spawn stuff later for a second leg...
@ -164,6 +165,7 @@ impl TripSpawner {
goal: SidewalkSpot::building(*b, map),
},
trip_start,
cancelled,
));
return;
}
@ -173,7 +175,8 @@ impl TripSpawner {
TripSpec::Remote { .. } => {}
};
self.trips.push((person.id, start_time, spec, trip_start));
self.trips
.push((person.id, start_time, spec, trip_start, cancelled));
}
pub fn finalize(
@ -209,7 +212,7 @@ impl TripSpawner {
}
timer.start_iter("spawn trips", paths.len());
for ((p, start_time, spec, trip_start), maybe_req, maybe_path) in paths {
for ((p, start_time, spec, trip_start, cancelled), maybe_req, maybe_path) in paths {
timer.next();
// TODO clone() is super weird to do here, but we just need to make the borrow checker
@ -331,10 +334,15 @@ impl TripSpawner {
map,
),
};
scheduler.push(
start_time,
Command::StartTrip(trip, spec, maybe_req, maybe_path),
);
if cancelled {
trips.cancel_trip(trip);
} else {
scheduler.push(
start_time,
Command::StartTrip(trip, spec, maybe_req, maybe_path),
);
}
}
}
}

View File

@ -668,6 +668,14 @@ impl TripManager {
self.person_finished_trip(now, person, parking, scheduler, map);
}
// Different than aborting a trip. Don't warp any vehicles or change where the person is.
pub fn cancel_trip(&mut self, id: TripID) {
let trip = &mut self.trips[id.0];
self.unfinished_trips -= 1;
trip.aborted = true;
self.events.push(Event::TripAborted(trip.id));
}
pub fn abort_trip(
&mut self,
now: Time,
@ -918,6 +926,7 @@ impl TripManager {
scheduler: &mut Scheduler,
map: &Map,
) {
assert!(!self.trips[trip.0].aborted);
if !self.pathfinding_upfront && maybe_path.is_none() && maybe_req.is_some() {
maybe_path = map.pathfind(maybe_req.clone().unwrap());
}