mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-28 12:12:00 +03:00
very simple modifier to cancel all trips for some percentage of people
This commit is contained in:
parent
455b17faef
commit
a6b90d00d8
@ -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 })
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
@ -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!(
|
||||
Text::from_multiline(vec![
|
||||
Line(format!(
|
||||
"{} trips aborted due to simulation glitch",
|
||||
prettyprint_usize(aborted)
|
||||
)
|
||||
.draw_text(ctx)
|
||||
.margin_below(5),
|
||||
)),
|
||||
Line(format!(
|
||||
"{} unfinished trips remaining",
|
||||
prettyprint_usize(unfinished)
|
||||
)),
|
||||
])
|
||||
.draw(ctx)
|
||||
.margin_below(10),
|
||||
);
|
||||
|
||||
col.push(
|
||||
Widget::row(vec![
|
||||
if opts.skip > 0 {
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 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(ScenarioModifier::RepeatDays(n));
|
||||
mods.push(new_mod);
|
||||
Some(Transition::PopThenReplace(EditScenarioModifiers::new(
|
||||
ctx,
|
||||
app,
|
||||
scenario_name.clone(),
|
||||
mods,
|
||||
)))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
@ -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,
|
||||
}],
|
||||
});
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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,
|
||||
}],
|
||||
});
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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,6 +334,10 @@ impl TripSpawner {
|
||||
map,
|
||||
),
|
||||
};
|
||||
|
||||
if cancelled {
|
||||
trips.cancel_trip(trip);
|
||||
} else {
|
||||
scheduler.push(
|
||||
start_time,
|
||||
Command::StartTrip(trip, spec, maybe_req, maybe_path),
|
||||
@ -338,6 +345,7 @@ impl TripSpawner {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TripSpec {
|
||||
// If possible, fixes problems that schedule_trip would hit.
|
||||
|
@ -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());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user