mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-01 02:33:54 +03:00
switch some UI stuff to use new analytics, get rid of the old FinishedTrips cruft. remove the a/b test scoreboard for now; not sure what info is needed, and a/b test mode is a bit dormant anyway now
This commit is contained in:
parent
a0956995c9
commit
3267ec4068
@ -1,4 +1,3 @@
|
||||
mod score;
|
||||
pub mod setup;
|
||||
|
||||
use crate::common::{AgentTools, CommonState};
|
||||
@ -60,10 +59,7 @@ impl ABTestMode {
|
||||
info_tools: MenuUnderButton::new(
|
||||
"assets/ui/info.png",
|
||||
"Info",
|
||||
vec![
|
||||
(hotkey(Key::Q), "scoreboard"),
|
||||
(hotkey(Key::Semicolon), "change agent colorscheme"),
|
||||
],
|
||||
vec![(hotkey(Key::Semicolon), "change agent colorscheme")],
|
||||
0.3,
|
||||
ctx,
|
||||
),
|
||||
@ -148,14 +144,6 @@ impl State for ABTestMode {
|
||||
self.flipped = !self.flipped;
|
||||
}
|
||||
|
||||
if self.info_tools.action("scoreboard") {
|
||||
return Transition::Push(Box::new(score::Scoreboard::new(
|
||||
ctx,
|
||||
&ui.primary,
|
||||
ui.secondary.as_ref().unwrap(),
|
||||
)));
|
||||
}
|
||||
|
||||
if let Some(t) =
|
||||
self.primary_agent_tools
|
||||
.event(ctx, ui, &mut self.menu, &mut self.info_tools)
|
||||
|
@ -1,202 +0,0 @@
|
||||
use crate::game::{State, Transition, WizardState};
|
||||
use crate::ui::PerMapUI;
|
||||
use crate::ui::UI;
|
||||
use abstutil::prettyprint_usize;
|
||||
use ezgui::{
|
||||
hotkey, Choice, Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, ModalMenu, Text,
|
||||
TextSpan, VerticalAlignment, Wizard,
|
||||
};
|
||||
use geom::Duration;
|
||||
use itertools::Itertools;
|
||||
use sim::{FinishedTrips, TripID, TripMode};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
pub struct Scoreboard {
|
||||
menu: ModalMenu,
|
||||
summary: Text,
|
||||
}
|
||||
|
||||
impl Scoreboard {
|
||||
pub fn new(ctx: &mut EventCtx, primary: &PerMapUI, secondary: &PerMapUI) -> Scoreboard {
|
||||
let menu = ModalMenu::new(
|
||||
"Scoreboard",
|
||||
vec![
|
||||
(hotkey(Key::Escape), "quit"),
|
||||
(hotkey(Key::B), "browse trips"),
|
||||
],
|
||||
ctx,
|
||||
);
|
||||
let t1 = primary.sim.get_finished_trips();
|
||||
let t2 = secondary.sim.get_finished_trips();
|
||||
|
||||
let mut summary = Text::new();
|
||||
summary.add_appended(vec![
|
||||
Line(format!("Score at {}... ", primary.sim.time())),
|
||||
// TODO Should we use consistent colors for the A and B?
|
||||
Line(&primary.map.get_edits().edits_name).fg(Color::RED),
|
||||
Line(" / "),
|
||||
Line(&secondary.map.get_edits().edits_name).fg(Color::CYAN),
|
||||
]);
|
||||
summary.add_appended(vec![
|
||||
Line(prettyprint_usize(t1.unfinished_trips)).fg(Color::RED),
|
||||
Line(" | "),
|
||||
Line(prettyprint_usize(t2.unfinished_trips)).fg(Color::CYAN),
|
||||
Line(" unfinished trips"),
|
||||
]);
|
||||
summary.add_appended(vec![
|
||||
Line(prettyprint_usize(t1.aborted_trips)).fg(Color::RED),
|
||||
Line(" | "),
|
||||
Line(prettyprint_usize(t2.aborted_trips)).fg(Color::CYAN),
|
||||
Line(" aborted trips"),
|
||||
]);
|
||||
summary.add_appended(vec![
|
||||
Line("faster (better)").fg(Color::GREEN),
|
||||
Line(" / "),
|
||||
Line("slower (worse)").fg(Color::YELLOW),
|
||||
]);
|
||||
|
||||
let cmp = CompareTrips::new(t1, t2);
|
||||
for (mode, trips) in &cmp
|
||||
.finished_trips
|
||||
.into_iter()
|
||||
.sorted_by_key(|(_, m, _, _)| *m)
|
||||
.group_by(|(_, m, _, _)| *m)
|
||||
{
|
||||
let mut num_same = 0;
|
||||
|
||||
// DurationHistogram doesn't handle deltas. Since the number of trips isn't huge,
|
||||
// manually do this...
|
||||
let mut deltas = Vec::new();
|
||||
for (_, _, t1, t2) in trips {
|
||||
if t1 == t2 {
|
||||
num_same += 1;
|
||||
} else {
|
||||
// Negative means the primary is faster
|
||||
deltas.push(t1 - t2);
|
||||
}
|
||||
}
|
||||
deltas.sort();
|
||||
let len = deltas.len() as f64;
|
||||
|
||||
summary.add_appended(vec![
|
||||
Line(format!("{}", mode)).fg(Color::PURPLE),
|
||||
Line(format!(
|
||||
" trips: {} same, {} different",
|
||||
abstutil::prettyprint_usize(num_same),
|
||||
abstutil::prettyprint_usize(deltas.len())
|
||||
)),
|
||||
]);
|
||||
if !deltas.is_empty() {
|
||||
summary.add_appended(vec![
|
||||
Line(" deltas: 50%ile "),
|
||||
print_delta(deltas[(0.5 * len).floor() as usize]),
|
||||
Line(", 90%ile "),
|
||||
print_delta(deltas[(0.9 * len).floor() as usize]),
|
||||
Line(", 99%ile "),
|
||||
print_delta(deltas[(0.99 * len).floor() as usize]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Scoreboard { menu, summary }
|
||||
}
|
||||
}
|
||||
|
||||
impl State for Scoreboard {
|
||||
fn event(&mut self, ctx: &mut EventCtx, _: &mut UI) -> Transition {
|
||||
self.menu.event(ctx);
|
||||
if self.menu.action("quit") {
|
||||
return Transition::Pop;
|
||||
}
|
||||
if self.menu.action("browse trips") {
|
||||
return Transition::Push(WizardState::new(Box::new(browse_trips)));
|
||||
}
|
||||
Transition::Keep
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, _: &UI) {
|
||||
g.draw_blocking_text(
|
||||
&self.summary,
|
||||
(HorizontalAlignment::Center, VerticalAlignment::Center),
|
||||
);
|
||||
self.menu.draw(g);
|
||||
}
|
||||
}
|
||||
|
||||
fn browse_trips(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option<Transition> {
|
||||
let mut wizard = wiz.wrap(ctx);
|
||||
let mode = wizard
|
||||
.choose("Browse which trips?", || {
|
||||
let trips = CompareTrips::new(
|
||||
ui.primary.sim.get_finished_trips(),
|
||||
ui.secondary.as_ref().unwrap().sim.get_finished_trips(),
|
||||
);
|
||||
let modes = trips
|
||||
.finished_trips
|
||||
.iter()
|
||||
.map(|(_, m, _, _)| *m)
|
||||
.collect::<BTreeSet<TripMode>>();
|
||||
TripMode::all()
|
||||
.into_iter()
|
||||
.map(|m| Choice::new(m.to_string(), m).active(modes.contains(&m)))
|
||||
.collect()
|
||||
})?
|
||||
.1;
|
||||
wizard.choose("Examine which trip?", || {
|
||||
let trips = CompareTrips::new(
|
||||
ui.primary.sim.get_finished_trips(),
|
||||
ui.secondary.as_ref().unwrap().sim.get_finished_trips(),
|
||||
);
|
||||
let mut filtered: Vec<&(TripID, TripMode, Duration, Duration)> = trips
|
||||
.finished_trips
|
||||
.iter()
|
||||
.filter(|(_, m, t1, t2)| *m == mode && *t1 != *t2)
|
||||
.collect();
|
||||
filtered.sort_by_key(|(_, _, t1, t2)| *t1 - *t2);
|
||||
filtered.reverse();
|
||||
filtered
|
||||
.into_iter()
|
||||
.map(|(id, _, t1, t2)| Choice::new(format!("{} taking {} vs {}", id, t1, t2), *id))
|
||||
.collect()
|
||||
})?;
|
||||
// TODO show more details...
|
||||
Some(Transition::Pop)
|
||||
}
|
||||
|
||||
pub struct CompareTrips {
|
||||
// Just finished in both, for now
|
||||
finished_trips: Vec<(TripID, TripMode, Duration, Duration)>,
|
||||
}
|
||||
|
||||
impl CompareTrips {
|
||||
fn new(t1: FinishedTrips, t2: FinishedTrips) -> CompareTrips {
|
||||
let trips1: BTreeMap<TripID, (TripMode, Duration)> = t1
|
||||
.finished_trips
|
||||
.into_iter()
|
||||
.map(|(id, mode, time)| (id, (mode, time)))
|
||||
.collect();
|
||||
let trips2: BTreeMap<TripID, (TripMode, Duration)> = t2
|
||||
.finished_trips
|
||||
.into_iter()
|
||||
.map(|(id, mode, time)| (id, (mode, time)))
|
||||
.collect();
|
||||
|
||||
let mut cmp = CompareTrips {
|
||||
finished_trips: Vec::new(),
|
||||
};
|
||||
for (id, (mode, time1)) in trips1 {
|
||||
if let Some((_, time2)) = trips2.get(&id) {
|
||||
cmp.finished_trips.push((id, mode, time1, *time2));
|
||||
}
|
||||
}
|
||||
cmp
|
||||
}
|
||||
}
|
||||
|
||||
fn print_delta(x: Duration) -> TextSpan {
|
||||
if x >= Duration::ZERO {
|
||||
Line(x.to_string()).fg(Color::YELLOW)
|
||||
} else {
|
||||
Line(x.to_string()).fg(Color::GREEN)
|
||||
}
|
||||
}
|
@ -41,9 +41,9 @@ impl TripExplorer {
|
||||
}
|
||||
|
||||
// Handle endpoints
|
||||
let status = ui.primary.sim.trip_status(trip);
|
||||
let (trip_start, trip_end) = ui.primary.sim.trip_endpoints(trip);
|
||||
let start_color = rotating_color_map(0);
|
||||
match status.start {
|
||||
match trip_start {
|
||||
TripStart::Bldg(b) => {
|
||||
let bldg = ui.primary.map.get_b(b);
|
||||
rows.insert(0, (format!("start at {}", bldg.get_name()), start_color));
|
||||
@ -75,7 +75,7 @@ impl TripExplorer {
|
||||
}
|
||||
|
||||
let end_color = rotating_color_map(rows.len());
|
||||
match status.end {
|
||||
match trip_end {
|
||||
TripEnd::Bldg(b) => {
|
||||
let bldg = ui.primary.map.get_b(b);
|
||||
rows.push((format!("end at {}", bldg.get_name()), end_color));
|
||||
|
@ -7,7 +7,7 @@ use ezgui::{
|
||||
hotkey, Choice, Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, ModalMenu, Text,
|
||||
VerticalAlignment, Wizard,
|
||||
};
|
||||
use geom::{Duration, Statistic};
|
||||
use geom::{Duration, Statistic, Time};
|
||||
use sim::{TripID, TripMode};
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
@ -120,11 +120,13 @@ impl State for Scoreboard {
|
||||
fn browse_trips(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option<Transition> {
|
||||
let mut wizard = wiz.wrap(ctx);
|
||||
let (_, mode) = wizard.choose("Browse which trips?", || {
|
||||
let trips = ui.primary.sim.get_finished_trips();
|
||||
let modes = trips
|
||||
let modes = ui
|
||||
.primary
|
||||
.sim
|
||||
.get_analytics()
|
||||
.finished_trips
|
||||
.iter()
|
||||
.map(|(_, m, _)| *m)
|
||||
.filter_map(|(_, _, m, _)| *m)
|
||||
.collect::<BTreeSet<TripMode>>();
|
||||
TripMode::all()
|
||||
.into_iter()
|
||||
@ -132,18 +134,20 @@ fn browse_trips(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option<Tra
|
||||
.collect()
|
||||
})?;
|
||||
let (_, trip) = wizard.choose("Examine which trip?", || {
|
||||
let trips = ui.primary.sim.get_finished_trips();
|
||||
let mut filtered: Vec<&(TripID, TripMode, Duration)> = trips
|
||||
let mut filtered: Vec<&(Time, TripID, Option<TripMode>, Duration)> = ui
|
||||
.primary
|
||||
.sim
|
||||
.get_analytics()
|
||||
.finished_trips
|
||||
.iter()
|
||||
.filter(|(_, m, _)| *m == mode)
|
||||
.filter(|(_, _, m, _)| *m == Some(mode))
|
||||
.collect();
|
||||
filtered.sort_by_key(|(_, _, dt)| *dt);
|
||||
filtered.sort_by_key(|(_, _, _, dt)| *dt);
|
||||
filtered.reverse();
|
||||
filtered
|
||||
.into_iter()
|
||||
// TODO Show percentile for time
|
||||
.map(|(id, _, dt)| Choice::new(format!("{} taking {}", id, dt), *id))
|
||||
.map(|(_, id, _, dt)| Choice::new(format!("{} taking {}", id, dt), *id))
|
||||
.collect()
|
||||
})?;
|
||||
|
||||
|
@ -22,8 +22,8 @@ pub(crate) use self::router::{ActionAtEnd, Router};
|
||||
pub(crate) use self::scheduler::{Command, Scheduler};
|
||||
pub use self::sim::{Sim, SimOptions};
|
||||
pub(crate) use self::transit::TransitSimState;
|
||||
pub use self::trips::{FinishedTrips, TripEnd, TripMode, TripStart, TripStatus};
|
||||
pub use self::trips::{TripCount, TripResult};
|
||||
pub use self::trips::{TripEnd, TripMode, TripStart};
|
||||
pub(crate) use self::trips::{TripLeg, TripManager};
|
||||
pub use crate::render::{
|
||||
AgentMetadata, CarStatus, DontDrawAgents, DrawCarInput, DrawPedCrowdInput, DrawPedestrianInput,
|
||||
|
@ -1,10 +1,10 @@
|
||||
use crate::{
|
||||
AgentID, AgentMetadata, Analytics, CarID, Command, CreateCar, DrawCarInput, DrawPedCrowdInput,
|
||||
DrawPedestrianInput, DrivingGoal, DrivingSimState, Event, FinishedTrips, GetDrawAgents,
|
||||
IntersectionSimState, ParkedCar, ParkingSimState, ParkingSpot, PedestrianID, Router, Scheduler,
|
||||
SidewalkPOI, SidewalkSpot, TransitSimState, TripCount, TripID, TripLeg, TripManager, TripMode,
|
||||
TripPositions, TripResult, TripSpawner, TripSpec, TripStart, TripStatus, UnzoomedAgent,
|
||||
VehicleSpec, VehicleType, WalkingSimState, BUS_LENGTH,
|
||||
DrawPedestrianInput, DrivingGoal, DrivingSimState, Event, GetDrawAgents, IntersectionSimState,
|
||||
ParkedCar, ParkingSimState, ParkingSpot, PedestrianID, Router, Scheduler, SidewalkPOI,
|
||||
SidewalkSpot, TransitSimState, TripCount, TripEnd, TripID, TripLeg, TripManager, TripMode,
|
||||
TripPositions, TripResult, TripSpawner, TripSpec, TripStart, UnzoomedAgent, VehicleSpec,
|
||||
VehicleType, WalkingSimState, BUS_LENGTH,
|
||||
};
|
||||
use abstutil::Timer;
|
||||
use derivative::Derivative;
|
||||
@ -768,10 +768,6 @@ impl Sim {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_finished_trips(&self) -> FinishedTrips {
|
||||
self.trips.get_finished_trips()
|
||||
}
|
||||
|
||||
pub fn count_trips_involving_bldg(&self, b: BuildingID) -> TripCount {
|
||||
self.trips.count_trips_involving_bldg(b, self.time)
|
||||
}
|
||||
@ -848,8 +844,8 @@ impl Sim {
|
||||
self.trips.trip_to_agent(id)
|
||||
}
|
||||
|
||||
pub fn trip_status(&self, id: TripID) -> TripStatus {
|
||||
self.trips.trip_status(id)
|
||||
pub fn trip_endpoints(&self, id: TripID) -> (TripStart, TripEnd) {
|
||||
self.trips.trip_endpoints(id)
|
||||
}
|
||||
|
||||
pub fn lookup_car_id(&self, idx: usize) -> Option<CarID> {
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
Vehicle, VehicleType, WalkingSimState,
|
||||
};
|
||||
use abstutil::{deserialize_btreemap, serialize_btreemap, Counter};
|
||||
use geom::{Duration, Speed, Time};
|
||||
use geom::{Speed, Time};
|
||||
use map_model::{
|
||||
BuildingID, BusRouteID, BusStopID, IntersectionID, Map, PathConstraints, PathRequest, Position,
|
||||
};
|
||||
@ -58,8 +58,7 @@ impl TripManager {
|
||||
mode = TripMode::Transit;
|
||||
}
|
||||
TripLeg::ServeBusRoute(_, _) => {
|
||||
// Confusing, because Transit usually means riding transit. But bus trips will
|
||||
// never get returned in FinishedTrips anyway.
|
||||
// Confusing, because Transit usually means riding transit
|
||||
mode = TripMode::Transit;
|
||||
}
|
||||
}
|
||||
@ -506,24 +505,6 @@ impl TripManager {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_finished_trips(&self) -> FinishedTrips {
|
||||
let mut result = FinishedTrips {
|
||||
unfinished_trips: self.unfinished_trips,
|
||||
aborted_trips: 0,
|
||||
finished_trips: Vec::new(),
|
||||
};
|
||||
for t in &self.trips {
|
||||
if let Some(end) = t.finished_at {
|
||||
result
|
||||
.finished_trips
|
||||
.push((t.id, t.mode, end - t.spawned_at));
|
||||
} else if t.aborted {
|
||||
result.aborted_trips += 1;
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn is_done(&self) -> bool {
|
||||
self.unfinished_trips == 0
|
||||
}
|
||||
@ -532,20 +513,17 @@ impl TripManager {
|
||||
std::mem::replace(&mut self.events, Vec::new())
|
||||
}
|
||||
|
||||
pub fn trip_status(&self, id: TripID) -> TripStatus {
|
||||
let trip = &self.trips[id.0];
|
||||
TripStatus {
|
||||
start: trip.start.clone(),
|
||||
end: trip.end.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
// Return trip start time too
|
||||
pub fn find_trip_using_car(&self, id: CarID, home: BuildingID) -> Option<(TripID, Time)> {
|
||||
let t = self.trips.iter().find(|t| t.uses_car(id, home))?;
|
||||
Some((t.id, t.spawned_at))
|
||||
}
|
||||
|
||||
pub fn trip_endpoints(&self, id: TripID) -> (TripStart, TripEnd) {
|
||||
let t = &self.trips[id.0];
|
||||
(t.start.clone(), t.end.clone())
|
||||
}
|
||||
|
||||
// TODO Refactor after wrangling the TripStart/TripEnd mess
|
||||
pub fn count_trips_involving_bldg(&self, b: BuildingID, now: Time) -> TripCount {
|
||||
self.count_trips(TripStart::Bldg(b), TripEnd::Bldg(b), now)
|
||||
@ -693,14 +671,6 @@ pub enum TripLeg {
|
||||
ServeBusRoute(CarID, BusRouteID),
|
||||
}
|
||||
|
||||
// As of a moment in time, not necessarily the end of the simulation
|
||||
pub struct FinishedTrips {
|
||||
pub unfinished_trips: usize,
|
||||
pub aborted_trips: usize,
|
||||
// (..., ..., time to complete trip)
|
||||
pub finished_trips: Vec<(TripID, TripMode, Duration)>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Copy, PartialOrd, Ord)]
|
||||
pub enum TripMode {
|
||||
Walk,
|
||||
@ -758,11 +728,6 @@ pub enum TripEnd {
|
||||
ServeBusRoute(BusRouteID),
|
||||
}
|
||||
|
||||
pub struct TripStatus {
|
||||
pub start: TripStart,
|
||||
pub end: TripEnd,
|
||||
}
|
||||
|
||||
pub enum TripResult<T> {
|
||||
Ok(T),
|
||||
ModeChange,
|
||||
|
Loading…
Reference in New Issue
Block a user