mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-28 08:53:26 +03:00
moving a/b test edit plugin to a/b test mode as the initial state. putting secondary sim state into the mode directly
This commit is contained in:
parent
8cc86f623a
commit
58b3d6d201
@ -1,4 +1,7 @@
|
||||
mod setup;
|
||||
|
||||
use crate::game::{GameState, Mode};
|
||||
use crate::state::PerMapUI;
|
||||
use crate::ui::ShowEverything;
|
||||
use abstutil::elapsed_seconds;
|
||||
use ezgui::{Color, EventCtx, EventLoopMode, GfxCtx, Text, Wizard};
|
||||
@ -10,11 +13,14 @@ use std::time::Instant;
|
||||
const ADJUST_SPEED: f64 = 0.1;
|
||||
|
||||
pub struct ABTestMode {
|
||||
desired_speed: f64, // sim seconds per real second
|
||||
state: State,
|
||||
pub desired_speed: f64, // sim seconds per real second
|
||||
pub state: State,
|
||||
// TODO Urgh, hack. Need to be able to take() it to switch states sometimes.
|
||||
pub secondary: Option<PerMapUI>,
|
||||
}
|
||||
|
||||
enum State {
|
||||
pub enum State {
|
||||
Setup(setup::ABTestSetup),
|
||||
Paused,
|
||||
Running {
|
||||
last_step: Instant,
|
||||
@ -27,13 +33,19 @@ impl ABTestMode {
|
||||
pub fn new() -> ABTestMode {
|
||||
ABTestMode {
|
||||
desired_speed: 1.0,
|
||||
state: State::Paused,
|
||||
state: State::Setup(setup::ABTestSetup::Pick(Wizard::new())),
|
||||
secondary: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event(state: &mut GameState, ctx: &mut EventCtx) -> EventLoopMode {
|
||||
match state.mode {
|
||||
Mode::ABTest(ref mut mode) => {
|
||||
if let State::Setup(_) = mode.state {
|
||||
setup::ABTestSetup::event(state, ctx);
|
||||
return EventLoopMode::InputOnly;
|
||||
}
|
||||
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
state.ui.state.primary.current_selection = state.ui.handle_mouseover(
|
||||
ctx,
|
||||
@ -97,6 +109,10 @@ impl ABTestMode {
|
||||
};
|
||||
} else if ctx.input.modal_action("run one step of sim") {
|
||||
state.ui.state.primary.sim.step(&state.ui.state.primary.map);
|
||||
{
|
||||
let s = mode.secondary.as_mut().unwrap();
|
||||
s.sim.step(&s.map);
|
||||
}
|
||||
//*ctx.recalculate_current_selection = true;
|
||||
}
|
||||
EventLoopMode::InputOnly
|
||||
@ -118,6 +134,10 @@ impl ABTestMode {
|
||||
if dt_s >= sim::TIMESTEP.inner_seconds() / mode.desired_speed {
|
||||
ctx.input.use_update_event();
|
||||
state.ui.state.primary.sim.step(&state.ui.state.primary.map);
|
||||
{
|
||||
let s = mode.secondary.as_mut().unwrap();
|
||||
s.sim.step(&s.map);
|
||||
}
|
||||
//*ctx.recalculate_current_selection = true;
|
||||
*last_step = Instant::now();
|
||||
|
||||
@ -131,6 +151,7 @@ impl ABTestMode {
|
||||
}
|
||||
EventLoopMode::Animation
|
||||
}
|
||||
State::Setup(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
@ -138,17 +159,20 @@ impl ABTestMode {
|
||||
}
|
||||
|
||||
pub fn draw(state: &GameState, g: &mut GfxCtx) {
|
||||
state.ui.new_draw(
|
||||
g,
|
||||
None,
|
||||
HashMap::new(),
|
||||
&state.ui.state.primary.sim,
|
||||
&ShowEverything::new(),
|
||||
);
|
||||
|
||||
match state.mode {
|
||||
Mode::ABTest(ref mode) => match mode.state {
|
||||
_ => {
|
||||
state.ui.new_draw(
|
||||
g,
|
||||
None,
|
||||
HashMap::new(),
|
||||
&state.ui.state.primary.sim,
|
||||
&ShowEverything::new(),
|
||||
);
|
||||
State::Setup(ref setup) => {
|
||||
setup.draw(g);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
132
editor/src/abtest/setup.rs
Normal file
132
editor/src/abtest/setup.rs
Normal file
@ -0,0 +1,132 @@
|
||||
use crate::abtest::{ABTestMode, State};
|
||||
use crate::game::{GameState, Mode};
|
||||
use crate::plugins::{choose_edits, choose_scenario, load_ab_test};
|
||||
use crate::state::{Flags, PerMapUI, UIState};
|
||||
use ezgui::{EventCtx, GfxCtx, LogScroller, Wizard, WrappedWizard};
|
||||
use map_model::Map;
|
||||
use sim::{ABTest, SimFlags};
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub enum ABTestSetup {
|
||||
Pick(Wizard),
|
||||
Manage(ABTest, LogScroller),
|
||||
}
|
||||
|
||||
impl ABTestSetup {
|
||||
pub fn event(state: &mut GameState, ctx: &mut EventCtx) {
|
||||
match state.mode {
|
||||
Mode::ABTest(ref mut mode) => match mode.state {
|
||||
State::Setup(ref mut setup) => match setup {
|
||||
ABTestSetup::Pick(ref mut wizard) => {
|
||||
if let Some(ab_test) = pick_ab_test(
|
||||
&state.ui.state.primary.map,
|
||||
wizard.wrap(ctx.input, ctx.canvas),
|
||||
) {
|
||||
let scroller =
|
||||
LogScroller::new(ab_test.test_name.clone(), ab_test.describe());
|
||||
*setup = ABTestSetup::Manage(ab_test, scroller);
|
||||
} else if wizard.aborted() {
|
||||
state.mode = Mode::SplashScreen(Wizard::new(), None);
|
||||
}
|
||||
}
|
||||
ABTestSetup::Manage(test, ref mut scroller) => {
|
||||
ctx.input.set_mode_with_prompt(
|
||||
"A/B Test Editor",
|
||||
format!("A/B Test Editor for {}", test.test_name),
|
||||
&ctx.canvas,
|
||||
);
|
||||
if scroller.event(ctx.input) {
|
||||
state.mode = Mode::SplashScreen(Wizard::new(), None);
|
||||
} else if ctx.input.modal_action("run A/B test") {
|
||||
state.mode = launch_test(test, &mut state.ui.state, ctx);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx) {
|
||||
match self {
|
||||
ABTestSetup::Pick(wizard) => {
|
||||
wizard.draw(g);
|
||||
}
|
||||
ABTestSetup::Manage(_, scroller) => {
|
||||
scroller.draw(g);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pick_ab_test(map: &Map, mut wizard: WrappedWizard) -> Option<ABTest> {
|
||||
let load_existing = "Load existing A/B test";
|
||||
let create_new = "Create new A/B test";
|
||||
if wizard.choose_string("What A/B test to manage?", vec![load_existing, create_new])?
|
||||
== load_existing
|
||||
{
|
||||
load_ab_test(map, &mut wizard, "Load which A/B test?")
|
||||
} else {
|
||||
let test_name = wizard.input_string("Name the A/B test")?;
|
||||
let ab_test = ABTest {
|
||||
test_name,
|
||||
map_name: map.get_name().to_string(),
|
||||
scenario_name: choose_scenario(map, &mut wizard, "What scenario to run?")?,
|
||||
edits1_name: choose_edits(map, &mut wizard, "For the 1st run, what map edits to use?")?,
|
||||
edits2_name: choose_edits(map, &mut wizard, "For the 2nd run, what map edits to use?")?,
|
||||
};
|
||||
ab_test.save();
|
||||
Some(ab_test)
|
||||
}
|
||||
}
|
||||
|
||||
fn launch_test(test: &ABTest, state: &mut UIState, ctx: &mut EventCtx) -> Mode {
|
||||
println!("Launching A/B test {}...", test.test_name);
|
||||
let load = PathBuf::from(format!(
|
||||
"../data/scenarios/{}/{}.json",
|
||||
test.map_name, test.scenario_name
|
||||
));
|
||||
let current_flags = &state.primary.current_flags;
|
||||
let rng_seed = if current_flags.sim_flags.rng_seed.is_some() {
|
||||
current_flags.sim_flags.rng_seed
|
||||
} else {
|
||||
Some(42)
|
||||
};
|
||||
|
||||
// TODO Cheaper to load the edits for the map and then instantiate the scenario for the
|
||||
// primary.
|
||||
let (primary, _) = PerMapUI::new(
|
||||
Flags {
|
||||
sim_flags: SimFlags {
|
||||
load: load.clone(),
|
||||
rng_seed,
|
||||
run_name: format!("{} with {}", test.test_name, test.edits1_name),
|
||||
edits_name: test.edits1_name.clone(),
|
||||
},
|
||||
..current_flags.clone()
|
||||
},
|
||||
&state.cs,
|
||||
ctx.prerender,
|
||||
);
|
||||
let (secondary, _) = PerMapUI::new(
|
||||
Flags {
|
||||
sim_flags: SimFlags {
|
||||
load,
|
||||
rng_seed,
|
||||
run_name: format!("{} with {}", test.test_name, test.edits2_name),
|
||||
edits_name: test.edits2_name.clone(),
|
||||
},
|
||||
..current_flags.clone()
|
||||
},
|
||||
&state.cs,
|
||||
ctx.prerender,
|
||||
);
|
||||
|
||||
state.primary = primary;
|
||||
Mode::ABTest(ABTestMode {
|
||||
desired_speed: 1.0,
|
||||
state: State::Paused,
|
||||
secondary: Some(secondary),
|
||||
})
|
||||
}
|
@ -1,141 +0,0 @@
|
||||
use crate::colors::ColorScheme;
|
||||
use crate::objects::DrawCtx;
|
||||
use crate::plugins::{choose_edits, choose_scenario, load_ab_test, BlockingPlugin, PluginCtx};
|
||||
use crate::state::{Flags, PerMapUI, PluginsPerMap};
|
||||
use ezgui::{GfxCtx, LogScroller, Prerender, Wizard, WrappedWizard};
|
||||
use map_model::Map;
|
||||
use sim::{ABTest, SimFlags};
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub enum ABTestManager {
|
||||
PickABTest(Wizard),
|
||||
ManageABTest(ABTest, LogScroller),
|
||||
}
|
||||
|
||||
impl ABTestManager {
|
||||
pub fn new(ctx: &mut PluginCtx) -> Option<ABTestManager> {
|
||||
if ctx.primary.current_selection.is_none() && ctx.input.action_chosen("manage A/B tests") {
|
||||
return Some(ABTestManager::PickABTest(Wizard::new()));
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockingPlugin for ABTestManager {
|
||||
fn blocking_event_with_plugins(
|
||||
&mut self,
|
||||
ctx: &mut PluginCtx,
|
||||
primary_plugins: &mut PluginsPerMap,
|
||||
) -> bool {
|
||||
match self {
|
||||
ABTestManager::PickABTest(ref mut wizard) => {
|
||||
if let Some(ab_test) =
|
||||
pick_ab_test(&ctx.primary.map, wizard.wrap(ctx.input, ctx.canvas))
|
||||
{
|
||||
let scroller = LogScroller::new(ab_test.test_name.clone(), ab_test.describe());
|
||||
*self = ABTestManager::ManageABTest(ab_test, scroller);
|
||||
} else if wizard.aborted() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
ABTestManager::ManageABTest(test, ref mut scroller) => {
|
||||
ctx.input.set_mode_with_prompt(
|
||||
"A/B Test Editor",
|
||||
format!("A/B Test Editor for {}", test.test_name),
|
||||
&ctx.canvas,
|
||||
);
|
||||
if ctx.input.modal_action("run A/B test") {
|
||||
let ((new_primary, new_primary_plugins), new_secondary) =
|
||||
launch_test(test, &ctx.primary.current_flags, &ctx.cs, &ctx.prerender);
|
||||
*ctx.primary = new_primary;
|
||||
*primary_plugins = new_primary_plugins;
|
||||
*ctx.secondary = Some(new_secondary);
|
||||
return false;
|
||||
}
|
||||
if scroller.event(ctx.input) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, _ctx: &DrawCtx) {
|
||||
match self {
|
||||
ABTestManager::PickABTest(wizard) => {
|
||||
wizard.draw(g);
|
||||
}
|
||||
ABTestManager::ManageABTest(_, scroller) => {
|
||||
scroller.draw(g);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pick_ab_test(map: &Map, mut wizard: WrappedWizard) -> Option<ABTest> {
|
||||
let load_existing = "Load existing A/B test";
|
||||
let create_new = "Create new A/B test";
|
||||
if wizard.choose_string("What A/B test to manage?", vec![load_existing, create_new])?
|
||||
== load_existing
|
||||
{
|
||||
load_ab_test(map, &mut wizard, "Load which A/B test?")
|
||||
} else {
|
||||
let test_name = wizard.input_string("Name the A/B test")?;
|
||||
let ab_test = ABTest {
|
||||
test_name,
|
||||
map_name: map.get_name().to_string(),
|
||||
scenario_name: choose_scenario(map, &mut wizard, "What scenario to run?")?,
|
||||
edits1_name: choose_edits(map, &mut wizard, "For the 1st run, what map edits to use?")?,
|
||||
edits2_name: choose_edits(map, &mut wizard, "For the 2nd run, what map edits to use?")?,
|
||||
};
|
||||
ab_test.save();
|
||||
Some(ab_test)
|
||||
}
|
||||
}
|
||||
|
||||
fn launch_test(
|
||||
test: &ABTest,
|
||||
current_flags: &Flags,
|
||||
cs: &ColorScheme,
|
||||
prerender: &Prerender,
|
||||
) -> ((PerMapUI, PluginsPerMap), (PerMapUI, PluginsPerMap)) {
|
||||
println!("Launching A/B test {}...", test.test_name);
|
||||
let load = PathBuf::from(format!(
|
||||
"../data/scenarios/{}/{}.json",
|
||||
test.map_name, test.scenario_name
|
||||
));
|
||||
let rng_seed = if current_flags.sim_flags.rng_seed.is_some() {
|
||||
current_flags.sim_flags.rng_seed
|
||||
} else {
|
||||
Some(42)
|
||||
};
|
||||
|
||||
let primary = PerMapUI::new(
|
||||
Flags {
|
||||
sim_flags: SimFlags {
|
||||
load: load.clone(),
|
||||
rng_seed,
|
||||
run_name: format!("{} with {}", test.test_name, test.edits1_name),
|
||||
edits_name: test.edits1_name.clone(),
|
||||
},
|
||||
..current_flags.clone()
|
||||
},
|
||||
cs,
|
||||
prerender,
|
||||
);
|
||||
let secondary = PerMapUI::new(
|
||||
Flags {
|
||||
sim_flags: SimFlags {
|
||||
load,
|
||||
rng_seed,
|
||||
run_name: format!("{} with {}", test.test_name, test.edits2_name),
|
||||
edits_name: test.edits2_name.clone(),
|
||||
},
|
||||
..current_flags.clone()
|
||||
},
|
||||
cs,
|
||||
prerender,
|
||||
);
|
||||
// That's all! The scenario will be instantiated.
|
||||
(primary, secondary)
|
||||
}
|
@ -1 +0,0 @@
|
||||
pub mod a_b_tests;
|
@ -1,4 +1,3 @@
|
||||
pub mod edit;
|
||||
pub mod sim;
|
||||
pub mod view;
|
||||
|
||||
|
@ -278,8 +278,6 @@ impl SandboxMode {
|
||||
*last_step = Instant::now();
|
||||
|
||||
if benchmark.has_real_time_passed(Duration::seconds(1.0)) {
|
||||
// I think the benchmark should naturally account for the delay of
|
||||
// the secondary sim.
|
||||
*speed =
|
||||
state.ui.state.primary.sim.measure_speed(benchmark, false);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::colors::ColorScheme;
|
||||
use crate::objects::{DrawCtx, RenderingHints, ID};
|
||||
use crate::plugins;
|
||||
use crate::plugins::{edit, view, AmbientPlugin, BlockingPlugin, NonblockingPlugin, PluginCtx};
|
||||
use crate::plugins::{view, AmbientPlugin, BlockingPlugin, NonblockingPlugin, PluginCtx};
|
||||
use crate::render::DrawMap;
|
||||
use abstutil::MeasureMemory;
|
||||
use ezgui::EventCtx;
|
||||
@ -120,10 +120,6 @@ impl UIState {
|
||||
|
||||
if let Some(p) = view::warp::WarpState::new(&mut ctx) {
|
||||
self.exclusive_blocking_plugin = Some(Box::new(p));
|
||||
} else if ctx.secondary.is_none() {
|
||||
if let Some(p) = edit::a_b_tests::ABTestManager::new(&mut ctx) {
|
||||
self.exclusive_blocking_plugin = Some(Box::new(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,6 @@ impl GUI for UI {
|
||||
));
|
||||
}
|
||||
folders.extend(vec![
|
||||
Folder::new("Edit", vec![(Some(Key::B), "manage A/B tests")]),
|
||||
Folder::new("Simulation", vec![(Some(Key::D), "diff all A/B trips")]),
|
||||
Folder::new("View", vec![(Some(Key::J), "warp to an object")]),
|
||||
]);
|
||||
@ -40,7 +39,6 @@ impl GUI for UI {
|
||||
|
||||
fn modal_menus(&self) -> Vec<ModalMenu> {
|
||||
vec![
|
||||
ModalMenu::new("A/B Test Editor", vec![(Key::R, "run A/B test")]),
|
||||
ModalMenu::new("A/B Trip Explorer", vec![(Key::Enter, "quit")]),
|
||||
ModalMenu::new("A/B All Trips Explorer", vec![(Key::Enter, "quit")]),
|
||||
// The new exciting things!
|
||||
@ -173,6 +171,10 @@ impl GUI for UI {
|
||||
(Key::V, "visualize"),
|
||||
],
|
||||
),
|
||||
ModalMenu::new(
|
||||
"A/B Test Editor",
|
||||
vec![(Key::Escape, "quit"), (Key::R, "run A/B test")],
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user