use scoreboard tool to show comparison for all trips

This commit is contained in:
Dustin Carlino 2019-11-15 13:19:57 -08:00
parent 2f31893c48
commit accc344054
6 changed files with 94 additions and 43 deletions

View File

@ -65,12 +65,13 @@ impl GameplayState for CreateGridlock {
} }
fn gridlock_panel(ui: &UI) -> Text { fn gridlock_panel(ui: &UI) -> Text {
let (now_all, now_per_mode) = ui let (now_all, _, now_per_mode) = ui
.primary .primary
.sim .sim
.get_analytics() .get_analytics()
.all_finished_trips(ui.primary.sim.time()); .all_finished_trips(ui.primary.sim.time());
let (baseline_all, baseline_per_mode) = ui.prebaked.all_finished_trips(ui.primary.sim.time()); let (baseline_all, _, baseline_per_mode) =
ui.prebaked.all_finished_trips(ui.primary.sim.time());
let mut txt = Text::new(); let mut txt = Text::new();
txt.add_appended(vec![ txt.add_appended(vec![

View File

@ -76,7 +76,11 @@ fn faster_trips_panel(mode: TripMode, ui: &UI) -> Text {
} }
for stat in Statistic::all() { for stat in Statistic::all() {
txt.add(Line(format!("{}: ", stat))); txt.add(Line(format!(
"{}: {} ",
stat,
now.select(stat).minimal_tostring()
)));
txt.append_all(cmp_duration_shorter( txt.append_all(cmp_duration_shorter(
now.select(stat), now.select(stat),
baseline.select(stat), baseline.select(stat),

View File

@ -229,7 +229,7 @@ fn manage_acs(
} }
// Shorter is better // Shorter is better
fn cmp_duration_shorter(now: Duration, baseline: Duration) -> Vec<TextSpan> { pub fn cmp_duration_shorter(now: Duration, baseline: Duration) -> Vec<TextSpan> {
if now.epsilon_eq(baseline) { if now.epsilon_eq(baseline) {
vec![Line(" (same as baseline)")] vec![Line(" (same as baseline)")]
} else if now < baseline { } else if now < baseline {
@ -250,7 +250,7 @@ fn cmp_duration_shorter(now: Duration, baseline: Duration) -> Vec<TextSpan> {
} }
// Fewer is better // Fewer is better
fn cmp_count_fewer(now: usize, baseline: usize) -> TextSpan { pub fn cmp_count_fewer(now: usize, baseline: usize) -> TextSpan {
if now < baseline { if now < baseline {
Line(format!("{} fewer", prettyprint_usize(baseline - now))).fg(Color::GREEN) Line(format!("{} fewer", prettyprint_usize(baseline - now))).fg(Color::GREEN)
} else if now > baseline { } else if now > baseline {
@ -261,7 +261,7 @@ fn cmp_count_fewer(now: usize, baseline: usize) -> TextSpan {
} }
// More is better // More is better
fn cmp_count_more(now: usize, baseline: usize) -> TextSpan { pub fn cmp_count_more(now: usize, baseline: usize) -> TextSpan {
if now < baseline { if now < baseline {
Line(format!("{} fewer", prettyprint_usize(baseline - now))).fg(Color::RED) Line(format!("{} fewer", prettyprint_usize(baseline - now))).fg(Color::RED)
} else if now > baseline { } else if now > baseline {

View File

@ -1,12 +1,12 @@
use crate::game::{State, Transition, WizardState}; use crate::game::{State, Transition, WizardState};
use crate::sandbox::gameplay::{cmp_count_fewer, cmp_count_more, cmp_duration_shorter};
use crate::ui::UI; use crate::ui::UI;
use abstutil::prettyprint_usize; use abstutil::prettyprint_usize;
use ezgui::{ use ezgui::{
hotkey, Choice, Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, ModalMenu, Text, hotkey, Choice, Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, ModalMenu, Text,
VerticalAlignment, Wizard, VerticalAlignment, Wizard,
}; };
use geom::{Duration, DurationHistogram}; use geom::{Duration, Statistic};
use itertools::Itertools;
use sim::{TripID, TripMode}; use sim::{TripID, TripMode};
use std::collections::BTreeSet; use std::collections::BTreeSet;
@ -18,46 +18,80 @@ pub struct Scoreboard {
impl Scoreboard { impl Scoreboard {
pub fn new(ctx: &mut EventCtx, ui: &UI) -> Scoreboard { pub fn new(ctx: &mut EventCtx, ui: &UI) -> Scoreboard {
let menu = ModalMenu::new( let menu = ModalMenu::new(
"Scoreboard", "Finished trips summary",
vec![ vec![
(hotkey(Key::Escape), "quit"), (hotkey(Key::Escape), "quit"),
(hotkey(Key::B), "browse trips"), (hotkey(Key::B), "browse trips"),
], ],
ctx, ctx,
); );
let t = ui.primary.sim.get_finished_trips();
let mut summary = Text::new(); let (now_all, now_aborted, now_per_mode) = ui
summary.add_appended(vec![ .primary
Line("Score at "), .sim
Line(ui.primary.sim.time().to_string()).fg(Color::RED), .get_analytics()
]); .all_finished_trips(ui.primary.sim.time());
summary.add_appended(vec![ let (baseline_all, baseline_aborted, baseline_per_mode) =
Line(prettyprint_usize(t.unfinished_trips)).fg(Color::CYAN), ui.prebaked.all_finished_trips(ui.primary.sim.time());
Line(" unfinished trips"),
]);
summary.add_appended(vec![
Line(prettyprint_usize(t.aborted_trips)).fg(Color::CYAN),
Line(" aborted trips"),
]);
for (mode, trips) in &t // TODO Include unfinished count
.finished_trips let mut txt = Text::new();
.into_iter() txt.add_appended(vec![
.sorted_by_key(|(_, m, _)| *m) Line("Finished trips as of "),
.group_by(|(_, m, _)| *m) Line(ui.primary.sim.time().ampm_tostring()).fg(Color::CYAN),
{ ]);
let mut distrib: DurationHistogram = DurationHistogram::new(); txt.add_appended(vec![
for (_, _, dt) in trips { Line(format!(
distrib.add(dt); " {} aborted trips (",
prettyprint_usize(now_aborted)
)),
cmp_count_fewer(now_aborted, baseline_aborted),
Line(")"),
]);
// TODO Refactor
txt.add_appended(vec![
Line(format!(
"{} total finished trips (",
prettyprint_usize(now_all.count())
)),
cmp_count_more(now_all.count(), baseline_all.count()),
Line(")"),
]);
if now_all.count() > 0 && baseline_all.count() > 0 {
for stat in Statistic::all() {
txt.add(Line(format!(
" {}: {} ",
stat,
now_all.select(stat).minimal_tostring()
)));
txt.append_all(cmp_duration_shorter(
now_all.select(stat),
baseline_all.select(stat),
));
} }
summary.add_appended(vec![
Line(format!("{}", mode)).fg(Color::CYAN),
Line(format!(" trips: {}", distrib.describe())),
]);
} }
Scoreboard { menu, summary } for mode in TripMode::all() {
let a = &now_per_mode[&mode];
let b = &baseline_per_mode[&mode];
txt.add_appended(vec![
Line(format!("{} {} trips (", prettyprint_usize(a.count()), mode)),
cmp_count_more(a.count(), b.count()),
Line(")"),
]);
if a.count() > 0 && b.count() > 0 {
for stat in Statistic::all() {
txt.add(Line(format!(
" {}: {} ",
stat,
a.select(stat).minimal_tostring()
)));
txt.append_all(cmp_duration_shorter(a.select(stat), b.select(stat)));
}
}
}
Scoreboard { menu, summary: txt }
} }
} }

View File

@ -339,13 +339,14 @@ impl DurationHistogram {
} }
format!( format!(
"{} count, 50%ile {}, 90%ile {}, 99%ile {}, min {}, max {}", "{} count, 50%ile {}, 90%ile {}, 99%ile {}, min {}, mean {}, max {}",
abstutil::prettyprint_usize(self.count), abstutil::prettyprint_usize(self.count),
self.select(Statistic::P50).minimal_tostring(), self.select(Statistic::P50).minimal_tostring(),
self.select(Statistic::P90).minimal_tostring(), self.select(Statistic::P90).minimal_tostring(),
self.select(Statistic::P99).minimal_tostring(), self.select(Statistic::P99).minimal_tostring(),
self.min.minimal_tostring(), self.select(Statistic::Min).minimal_tostring(),
self.max.minimal_tostring(), self.select(Statistic::Mean).minimal_tostring(),
self.select(Statistic::Max).minimal_tostring(),
) )
} }
@ -366,6 +367,7 @@ impl DurationHistogram {
Statistic::Min => { Statistic::Min => {
return self.min; return self.min;
} }
Statistic::Mean => self.histogram.mean().unwrap(),
Statistic::Max => { Statistic::Max => {
return self.max; return self.max;
} }
@ -381,6 +383,7 @@ impl DurationHistogram {
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Statistic { pub enum Statistic {
Min, Min,
Mean,
P50, P50,
P90, P90,
P99, P99,
@ -391,6 +394,7 @@ impl Statistic {
pub fn all() -> Vec<Statistic> { pub fn all() -> Vec<Statistic> {
vec![ vec![
Statistic::Min, Statistic::Min,
Statistic::Mean,
Statistic::P50, Statistic::P50,
Statistic::P90, Statistic::P90,
Statistic::P99, Statistic::P99,
@ -403,6 +407,7 @@ impl std::fmt::Display for Statistic {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self { match self {
Statistic::Min => write!(f, "minimum"), Statistic::Min => write!(f, "minimum"),
Statistic::Mean => write!(f, "mean"),
Statistic::P50 => write!(f, "50%ile"), Statistic::P50 => write!(f, "50%ile"),
Statistic::P90 => write!(f, "90%ile"), Statistic::P90 => write!(f, "90%ile"),
Statistic::P99 => write!(f, "99%ile"), Statistic::P99 => write!(f, "99%ile"),

View File

@ -122,16 +122,21 @@ impl Analytics {
distrib distrib
} }
// Returns (all trips except aborted, trips by mode) // Returns (all trips except aborted, number of aborted trips, trips by mode)
pub fn all_finished_trips( pub fn all_finished_trips(
&self, &self,
now: Duration, now: Duration,
) -> (DurationHistogram, BTreeMap<TripMode, DurationHistogram>) { ) -> (
DurationHistogram,
usize,
BTreeMap<TripMode, DurationHistogram>,
) {
let mut per_mode = TripMode::all() let mut per_mode = TripMode::all()
.into_iter() .into_iter()
.map(|m| (m, DurationHistogram::new())) .map(|m| (m, DurationHistogram::new()))
.collect::<BTreeMap<_, _>>(); .collect::<BTreeMap<_, _>>();
let mut all = DurationHistogram::new(); let mut all = DurationHistogram::new();
let mut num_aborted = 0;
for (t, m, dt) in &self.finished_trips { for (t, m, dt) in &self.finished_trips {
if *t > now { if *t > now {
break; break;
@ -139,9 +144,11 @@ impl Analytics {
if let Some(mode) = *m { if let Some(mode) = *m {
all.add(*dt); all.add(*dt);
per_mode.get_mut(&mode).unwrap().add(*dt); per_mode.get_mut(&mode).unwrap().add(*dt);
} else {
num_aborted += 1;
} }
} }
(all, per_mode) (all, num_aborted, per_mode)
} }
pub fn bus_arrivals( pub fn bus_arrivals(