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:
Dustin Carlino 2019-12-02 14:55:47 -08:00
parent a0956995c9
commit 3267ec4068
7 changed files with 32 additions and 281 deletions

View File

@ -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)

View File

@ -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)
}
}

View File

@ -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));

View File

@ -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()
})?;

View File

@ -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,

View File

@ -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> {

View File

@ -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,