From e0edc4851d53c75b8a4252533f96794f1c6b90a3 Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Thu, 13 Jun 2019 13:36:19 -0700 Subject: [PATCH] starting a blank scoreboard for sandbox mode. removing old defunct attempts at score stuff first... --- editor/src/abtest/mod.rs | 8 ++-- editor/src/debug/neighborhood_summary.rs | 10 ++-- editor/src/sandbox/mod.rs | 23 +++++++++ editor/src/sandbox/route_viewer.rs | 2 +- editor/src/sandbox/score.rs | 37 +++++++++++++++ editor/src/sandbox/show_activity.rs | 4 +- headless/src/main.rs | 2 +- sim/src/lib.rs | 26 ++++++++--- sim/src/make/a_b_test.rs | 8 ---- sim/src/make/mod.rs | 2 +- sim/src/mechanics/driving.rs | 12 +++-- sim/src/mechanics/walking.rs | 8 ++-- sim/src/query.rs | 53 --------------------- sim/src/sim.rs | 59 ++++++++---------------- sim/src/trips.rs | 29 ++++++++++++ 15 files changed, 152 insertions(+), 131 deletions(-) create mode 100644 editor/src/sandbox/score.rs delete mode 100644 sim/src/query.rs diff --git a/editor/src/abtest/mod.rs b/editor/src/abtest/mod.rs index f924987779..b74d33e10d 100644 --- a/editor/src/abtest/mod.rs +++ b/editor/src/abtest/mod.rs @@ -305,12 +305,12 @@ pub struct DiffAllTrips { impl DiffAllTrips { fn new(primary: &mut PerMapUI, secondary: &mut PerMapUI) -> DiffAllTrips { - let stats1 = primary.sim.get_stats(&primary.map); - let stats2 = secondary.sim.get_stats(&secondary.map); + let trip_positions1 = primary.sim.get_trip_positions(&primary.map); + let trip_positions2 = secondary.sim.get_trip_positions(&secondary.map); let mut same_trips = 0; let mut lines: Vec = Vec::new(); - for (trip, pt1) in &stats1.canonical_pt_per_trip { - if let Some(pt2) = stats2.canonical_pt_per_trip.get(trip) { + for (trip, pt1) in &trip_positions1.canonical_pt_per_trip { + if let Some(pt2) = trip_positions2.canonical_pt_per_trip.get(trip) { if let Some(l) = Line::maybe_new(*pt1, *pt2) { lines.push(l); } else { diff --git a/editor/src/debug/neighborhood_summary.rs b/editor/src/debug/neighborhood_summary.rs index c6ebba05c8..9a06d104c9 100644 --- a/editor/src/debug/neighborhood_summary.rs +++ b/editor/src/debug/neighborhood_summary.rs @@ -5,7 +5,6 @@ use abstutil; use ezgui::{Color, Drawable, GfxCtx, ModalMenu, Prerender, Text}; use geom::{Duration, Polygon, Pt2D}; use map_model::{LaneID, Map, Neighborhood}; -use sim::Sim; use std::collections::HashSet; pub struct NeighborhoodSummary { @@ -55,7 +54,7 @@ impl NeighborhoodSummary { if self.active && Some(ui.primary.sim.time()) != self.last_summary { self.last_summary = Some(ui.primary.sim.time()); for r in self.regions.iter_mut() { - r.update_summary(&ui.primary.sim); + r.update_summary(); } } } @@ -105,13 +104,12 @@ impl Region { } } - fn update_summary(&mut self, primary: &Sim) { + fn update_summary(&mut self) { let mut txt = Text::new(); txt.add_styled_line(self.name.clone(), None, Some(Color::GREEN), Some(50)); txt.add_line(format!("contains {} lanes", self.lanes.len())); - let s1 = primary.summarize(&self.lanes); - + /*let s1 = primary.summarize(&self.lanes); txt.add_line(format!( "{} cars parked, {} spots free", s1.cars_parked, s1.open_parking_spots @@ -124,7 +122,7 @@ impl Region { "{} moving peds, {} stuck", s1.moving_peds, s1.stuck_peds )); - txt.add_line(format!("{} buses", s1.buses)); + txt.add_line(format!("{} buses", s1.buses));*/ self.summary = txt; } diff --git a/editor/src/sandbox/mod.rs b/editor/src/sandbox/mod.rs index 33d3c92b46..d89c8a8483 100644 --- a/editor/src/sandbox/mod.rs +++ b/editor/src/sandbox/mod.rs @@ -1,5 +1,6 @@ mod route_explorer; mod route_viewer; +mod score; mod show_activity; mod spawner; mod time_travel; @@ -33,6 +34,7 @@ enum State { TimeTraveling, ExploringRoute(route_explorer::RouteExplorer), JumpingToTime(Wizard), + Scoreboard(score::Scoreboard), } impl SandboxMode { @@ -68,6 +70,7 @@ impl SandboxMode { (hotkey(Key::L), "show/hide route for all agents"), (hotkey(Key::A), "show/hide active traffic"), (hotkey(Key::T), "start time traveling"), + (hotkey(Key::Q), "scoreboard"), (lctrl(Key::D), "debug mode"), (lctrl(Key::E), "edit mode"), ], @@ -146,6 +149,13 @@ impl SandboxMode { } EventLoopMode::InputOnly } + State::Scoreboard(ref mut s) => { + if s.event(ctx) { + mode.state = State::Playing; + mode.speed.pause(); + } + EventLoopMode::InputOnly + } State::Playing => { mode.time_travel.record(&state.ui); @@ -238,6 +248,10 @@ impl SandboxMode { mode.time_travel.start(ctx, &state.ui); return EventLoopMode::InputOnly; } + if mode.menu.action("scoreboard") { + mode.state = State::Scoreboard(score::Scoreboard::new(ctx, &state.ui)); + return EventLoopMode::InputOnly; + } if mode.menu.action("quit") { state.mode = Mode::SplashScreen(Wizard::new(), None); @@ -400,6 +414,15 @@ impl SandboxMode { ); wizard.draw(g); } + State::Scoreboard(ref s) => { + state.ui.draw( + g, + DrawOptions::new(), + &state.ui.primary.sim, + &ShowEverything::new(), + ); + s.draw(g); + } _ => { state.ui.draw( g, diff --git a/editor/src/sandbox/route_viewer.rs b/editor/src/sandbox/route_viewer.rs index e32eba3a67..1df85f4878 100644 --- a/editor/src/sandbox/route_viewer.rs +++ b/editor/src/sandbox/route_viewer.rs @@ -120,7 +120,7 @@ fn debug_all_routes(ui: &mut UI) -> RouteViewer { let trips: Vec = ui .primary .sim - .get_stats(&ui.primary.map) + .get_trip_positions(&ui.primary.map) .canonical_pt_per_trip .keys() .cloned() diff --git a/editor/src/sandbox/score.rs b/editor/src/sandbox/score.rs new file mode 100644 index 0000000000..ef284c4680 --- /dev/null +++ b/editor/src/sandbox/score.rs @@ -0,0 +1,37 @@ +use crate::ui::UI; +use ezgui::{ + hotkey, EventCtx, GfxCtx, HorizontalAlignment, Key, ModalMenu, Text, VerticalAlignment, +}; + +pub struct Scoreboard { + menu: ModalMenu, + summary: Text, +} + +impl Scoreboard { + pub fn new(ctx: &mut EventCtx, ui: &UI) -> Scoreboard { + let menu = ModalMenu::new("Scoreboard", vec![(hotkey(Key::Escape), "quit")], ctx); + + let mut summary = Text::new(); + summary.push(format!("Score at [red:{}]", ui.primary.sim.time())); + + Scoreboard { menu, summary } + } + + // Returns true if done and we should go back to main sandbox mode. + pub fn event(&mut self, ctx: &mut EventCtx) -> bool { + self.menu.handle_event(ctx, None); + if self.menu.action("quit") { + return true; + } + false + } + + pub fn draw(&self, g: &mut GfxCtx) { + g.draw_blocking_text( + &self.summary, + (HorizontalAlignment::Center, VerticalAlignment::Center), + ); + self.menu.draw(g); + } +} diff --git a/editor/src/sandbox/show_activity.rs b/editor/src/sandbox/show_activity.rs index dec9ca9ca4..2c71666202 100644 --- a/editor/src/sandbox/show_activity.rs +++ b/editor/src/sandbox/show_activity.rs @@ -131,8 +131,8 @@ impl Heatmap { fn active_agent_heatmap(ctx: &EventCtx, ui: &mut UI) -> Heatmap { let mut h = Heatmap::new(ctx.canvas.get_screen_bounds()); - let stats = ui.primary.sim.get_stats(&ui.primary.map); - for pt in stats.canonical_pt_per_trip.values() { + let trip_positions = ui.primary.sim.get_trip_positions(&ui.primary.map); + for pt in trip_positions.canonical_pt_per_trip.values() { h.add(*pt); } h diff --git a/headless/src/main.rs b/headless/src/main.rs index 7a0cd4ff4c..efae172ac8 100644 --- a/headless/src/main.rs +++ b/headless/src/main.rs @@ -85,7 +85,7 @@ fn main() { None, ); timer.done(); - println!("{:?}", sim.get_score()); + println!("Done at {}", sim.time()); if flags.enable_profiler && save_at.is_none() { cpuprofiler::PROFILER.lock().unwrap().stop().unwrap(); } diff --git a/sim/src/lib.rs b/sim/src/lib.rs index 16ca181df9..52ca39402a 100644 --- a/sim/src/lib.rs +++ b/sim/src/lib.rs @@ -1,7 +1,6 @@ mod events; mod make; mod mechanics; -mod query; mod render; mod router; mod scheduler; @@ -11,23 +10,23 @@ mod trips; pub use self::events::Event; pub use self::make::{ - ABTest, ABTestResults, BorderSpawnOverTime, OriginDestination, Scenario, SeedParkedCars, - SimFlags, SpawnOverTime, SpawnTrip, TripSpawner, TripSpec, + ABTest, BorderSpawnOverTime, OriginDestination, Scenario, SeedParkedCars, SimFlags, + SpawnOverTime, SpawnTrip, TripSpawner, TripSpec, }; pub(crate) use self::mechanics::{ DrivingSimState, IntersectionSimState, ParkingSimState, WalkingSimState, }; -pub use self::query::{ScoreSummary, SimStats, Summary}; pub(crate) use self::router::{ActionAtEnd, Router}; pub(crate) use self::scheduler::{Command, Scheduler}; pub use self::sim::Sim; pub(crate) use self::transit::TransitSimState; -pub(crate) use self::trips::{TripLeg, TripManager}; +pub(crate) use self::trips::{FinishedTrips, TripLeg, TripManager}; pub use crate::render::{CarStatus, DrawCarInput, DrawPedestrianInput, GetDrawAgents}; use abstutil::Cloneable; -use geom::{Distance, Duration, Speed}; +use geom::{Distance, Duration, Pt2D, Speed}; use map_model::{BuildingID, BusStopID, IntersectionID, LaneID, LaneType, Map, Path, Position}; use serde_derive::{Deserialize, Serialize}; +use std::collections::BTreeMap; use std::fmt; // http://pccsc.net/bicycle-parking-info/ says 68 inches, which is 1.73m @@ -439,6 +438,21 @@ impl CreateCar { } } +#[derive(Serialize, Deserialize, PartialEq)] +pub struct TripPositions { + pub time: Duration, + pub canonical_pt_per_trip: BTreeMap, +} + +impl TripPositions { + pub(crate) fn new(time: Duration) -> TripPositions { + TripPositions { + time, + canonical_pt_per_trip: BTreeMap::new(), + } + } +} + // We have to do this in the crate where these types are defined. Bit annoying, since it's really // kind of an ezgui concept. impl Cloneable for ABTest {} diff --git a/sim/src/make/a_b_test.rs b/sim/src/make/a_b_test.rs index 5763830e1d..7f43fc2cda 100644 --- a/sim/src/make/a_b_test.rs +++ b/sim/src/make/a_b_test.rs @@ -1,4 +1,3 @@ -use crate::ScoreSummary; use abstutil; use serde_derive::{Deserialize, Serialize}; @@ -24,10 +23,3 @@ impl ABTest { abstutil::save_json_object("ab_tests", &self.map_name, &self.test_name, self); } } - -#[derive(Serialize, Deserialize, Debug)] -pub struct ABTestResults { - pub test_name: String, - pub run1_score: ScoreSummary, - pub run2_score: ScoreSummary, -} diff --git a/sim/src/make/mod.rs b/sim/src/make/mod.rs index 9b15c9819a..96496c58c3 100644 --- a/sim/src/make/mod.rs +++ b/sim/src/make/mod.rs @@ -3,7 +3,7 @@ mod load; mod scenario; mod spawner; -pub use self::a_b_test::{ABTest, ABTestResults}; +pub use self::a_b_test::ABTest; pub use self::load::SimFlags; pub use self::scenario::{ BorderSpawnOverTime, OriginDestination, Scenario, SeedParkedCars, SpawnOverTime, SpawnTrip, diff --git a/sim/src/mechanics/driving.rs b/sim/src/mechanics/driving.rs index a30d107592..7b2dc00a9f 100644 --- a/sim/src/mechanics/driving.rs +++ b/sim/src/mechanics/driving.rs @@ -2,8 +2,8 @@ use crate::mechanics::car::{Car, CarState}; use crate::mechanics::queue::Queue; use crate::{ ActionAtEnd, AgentID, CarID, Command, CreateCar, DistanceInterval, DrawCarInput, - IntersectionSimState, ParkedCar, ParkingSimState, Scheduler, SimStats, TimeInterval, - TransitSimState, TripManager, VehicleType, WalkingSimState, FOLLOWING_DISTANCE, + IntersectionSimState, ParkedCar, ParkingSimState, Scheduler, TimeInterval, TransitSimState, + TripManager, TripPositions, VehicleType, WalkingSimState, FOLLOWING_DISTANCE, }; use abstutil::{deserialize_btreemap, serialize_btreemap}; use geom::{Distance, Duration, PolyLine, Pt2D}; @@ -648,14 +648,16 @@ impl DrivingSimState { (cars, bikes, buses) } - pub fn populate_stats(&self, stats: &mut SimStats, map: &Map) { + pub fn populate_trip_positions(&self, trip_positions: &mut TripPositions, map: &Map) { for queue in self.queues.values() { if queue.cars.is_empty() { continue; } - for (car, dist) in queue.get_car_positions(stats.time, &self.cars, &self.queues) { - stats + for (car, dist) in + queue.get_car_positions(trip_positions.time, &self.cars, &self.queues) + { + trip_positions .canonical_pt_per_trip .insert(self.cars[&car].trip, queue.id.dist_along(dist, map).0); } diff --git a/sim/src/mechanics/walking.rs b/sim/src/mechanics/walking.rs index 45fe805b96..23f25163d6 100644 --- a/sim/src/mechanics/walking.rs +++ b/sim/src/mechanics/walking.rs @@ -1,7 +1,7 @@ use crate::{ AgentID, Command, CreatePedestrian, DistanceInterval, DrawPedestrianInput, IntersectionSimState, ParkingSimState, PedestrianID, Scheduler, SidewalkPOI, SidewalkSpot, - SimStats, TimeInterval, TransitSimState, TripID, TripManager, + TimeInterval, TransitSimState, TripID, TripManager, TripPositions, }; use abstutil::{deserialize_multimap, serialize_multimap, MultiMap}; use geom::{Distance, Duration, Line, PolyLine, Pt2D, Speed}; @@ -272,11 +272,11 @@ impl WalkingSimState { peds } - pub fn populate_stats(&self, stats: &mut SimStats, map: &Map) { + pub fn populate_trip_positions(&self, trip_positions: &mut TripPositions, map: &Map) { for ped in self.peds.values() { - stats + trip_positions .canonical_pt_per_trip - .insert(ped.trip, ped.get_draw_ped(stats.time, map).pos); + .insert(ped.trip, ped.get_draw_ped(trip_positions.time, map).pos); } } } diff --git a/sim/src/query.rs b/sim/src/query.rs deleted file mode 100644 index 0983e95e8f..0000000000 --- a/sim/src/query.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::TripID; -use geom::{Duration, Pt2D}; -use serde_derive::{Deserialize, Serialize}; -use std::collections::BTreeMap; - -#[derive(Serialize, Deserialize, PartialEq)] -pub struct SimStats { - pub time: Duration, - pub canonical_pt_per_trip: BTreeMap, -} - -impl SimStats { - pub(crate) fn new(time: Duration) -> SimStats { - SimStats { - time, - canonical_pt_per_trip: BTreeMap::new(), - } - } -} - -// TODO This is totally unused, needs to be re-thought -// TODO moving vs stuck shouldn't be an instantaneous judgment -- stuck is if there's an agent -// directly in front limiting speed significantly, or if an intersection isn't allowing movement -// yet -pub struct Summary { - pub cars_parked: usize, - pub open_parking_spots: usize, - pub moving_cars: usize, - pub stuck_cars: usize, - pub moving_peds: usize, - pub stuck_peds: usize, - pub buses: usize, - // The agent in one or both worlds is in the requested set of lanes. - pub trips_with_ab_test_divergence: usize, -} - -// TODO This is totally unused, needs to be re-thought -// As of a moment in time, not necessarily the end of the simulation -#[derive(Serialize, Deserialize, Debug)] -pub struct ScoreSummary { - pub pending_walking_trips: usize, - pub total_walking_trips: usize, - pub total_walking_trip_time: Duration, - - pub pending_driving_trips: usize, - pub total_driving_trips: usize, - pub total_driving_trip_time: Duration, - - // If filled out, the sim took this long to complete. - // TODO This is maybe not a useful thing to measure; the agents moving at the end don't have - // others around, so things are stranger for them. - pub completion_time: Option, -} diff --git a/sim/src/sim.rs b/sim/src/sim.rs index f5fa76341b..ba1948ccfa 100644 --- a/sim/src/sim.rs +++ b/sim/src/sim.rs @@ -1,9 +1,9 @@ use crate::{ AgentID, CarID, Command, CreateCar, DrawCarInput, DrawPedestrianInput, DrivingGoal, - DrivingSimState, Event, GetDrawAgents, IntersectionSimState, ParkedCar, ParkingSimState, - ParkingSpot, PedestrianID, Router, Scheduler, ScoreSummary, SimStats, Summary, TransitSimState, - TripID, TripLeg, TripManager, TripSpawner, TripSpec, VehicleSpec, VehicleType, WalkingSimState, - BUS_LENGTH, + DrivingSimState, Event, FinishedTrips, GetDrawAgents, IntersectionSimState, ParkedCar, + ParkingSimState, ParkingSpot, PedestrianID, Router, Scheduler, TransitSimState, TripID, + TripLeg, TripManager, TripPositions, TripSpawner, TripSpec, VehicleSpec, VehicleType, + WalkingSimState, BUS_LENGTH, }; use abstutil::{elapsed_seconds, Timer}; use derivative::Derivative; @@ -45,7 +45,7 @@ pub struct Sim { // Lazily computed. #[derivative(PartialEq = "ignore")] #[serde(skip_serializing, skip_deserializing)] - stats: Option, + trip_positions: Option, #[derivative(PartialEq = "ignore")] #[serde(skip_serializing, skip_deserializing)] @@ -78,7 +78,7 @@ impl Sim { edits_name: "no_edits".to_string(), run_name, step_count: 0, - stats: None, + trip_positions: None, events_since_last_step: Vec::new(), } } @@ -408,7 +408,7 @@ impl Sim { } self.time = target_time; - self.stats = None; + self.trip_positions = None; self.events_since_last_step.clear(); self.events_since_last_step @@ -599,20 +599,6 @@ impl Sim { self.time == Duration::ZERO && self.is_done() } - // TODO Rethink this - pub fn summarize(&self, _lanes: &HashSet) -> Summary { - Summary { - cars_parked: 0, - open_parking_spots: 0, - moving_cars: 0, - stuck_cars: 0, - buses: 0, - moving_peds: 0, - stuck_peds: 0, - trips_with_ab_test_divergence: 0, - } - } - pub fn summary(&self) -> String { let (active, unfinished) = self.trips.num_trips(); format!( @@ -623,17 +609,8 @@ impl Sim { ) } - // TODO Rethink this - pub fn get_score(&self) -> ScoreSummary { - ScoreSummary { - pending_walking_trips: 0, - total_walking_trips: 0, - total_walking_trip_time: Duration::ZERO, - pending_driving_trips: 0, - total_driving_trips: 0, - total_driving_trip_time: Duration::ZERO, - completion_time: None, - } + pub fn get_finished_trips(&self) -> FinishedTrips { + self.trips.get_finished_trips() } pub fn debug_ped(&self, id: PedestrianID) { @@ -740,17 +717,19 @@ impl Sim { .or_else(|| self.parking.get_owner_of_car(id)) } - pub fn get_stats(&mut self, map: &Map) -> &SimStats { - if self.stats.is_some() { - return self.stats.as_ref().unwrap(); + pub fn get_trip_positions(&mut self, map: &Map) -> &TripPositions { + if self.trip_positions.is_some() { + return self.trip_positions.as_ref().unwrap(); } - let mut stats = SimStats::new(self.time); - self.driving.populate_stats(&mut stats, map); - self.walking.populate_stats(&mut stats, map); + let mut trip_positions = TripPositions::new(self.time); + self.driving + .populate_trip_positions(&mut trip_positions, map); + self.walking + .populate_trip_positions(&mut trip_positions, map); - self.stats = Some(stats); - self.stats.as_ref().unwrap() + self.trip_positions = Some(trip_positions); + self.trip_positions.as_ref().unwrap() } pub fn get_events_since_last_step(&self) -> &Vec { diff --git a/sim/src/trips.rs b/sim/src/trips.rs index 57aa64f165..0afc4ff90a 100644 --- a/sim/src/trips.rs +++ b/sim/src/trips.rs @@ -379,6 +379,18 @@ impl TripManager { ) } + pub fn get_finished_trips(&self) -> FinishedTrips { + FinishedTrips { + pending_walking_trips: 0, + total_walking_trips: 0, + total_walking_trip_time: Duration::ZERO, + pending_driving_trips: 0, + total_driving_trips: 0, + total_driving_trip_time: Duration::ZERO, + completion_time: None, + } + } + pub fn is_done(&self) -> bool { self.unfinished_trips == 0 } @@ -467,3 +479,20 @@ pub enum TripLeg { RideBus(PedestrianID, BusRouteID, BusStopID), ServeBusRoute(CarID, BusRouteID), } + +// As of a moment in time, not necessarily the end of the simulation +#[derive(Serialize, Deserialize, Debug)] +pub struct FinishedTrips { + pub pending_walking_trips: usize, + pub total_walking_trips: usize, + pub total_walking_trip_time: Duration, + + pub pending_driving_trips: usize, + pub total_driving_trips: usize, + pub total_driving_trip_time: Duration, + + // If filled out, the sim took this long to complete. + // TODO This is maybe not a useful thing to measure; the agents moving at the end don't have + // others around, so things are stranger for them. + pub completion_time: Option, +}