sim/make/
load.rs

1use anyhow::Result;
2use rand::SeedableRng;
3use rand_xorshift::XorShiftRng;
4use structopt::StructOpt;
5
6use abstio::MapName;
7use map_model::{Map, MapEdits};
8use synthpop::{Scenario, ScenarioModifier};
9
10use crate::{Sim, SimOptions};
11
12/// SimFlags specifies a simulation to setup. After parsing from structopt, you must call
13/// `initialize`.
14#[derive(Clone, StructOpt)]
15pub struct SimFlags {
16    /// A path to some file:
17    ///
18    /// - some kind of map: start an empty simulation on the map
19    /// - a scenario
20    /// - a savestate: restore the simulation exactly from some savestate
21    #[structopt()]
22    load_path: Option<String>,
23    /// The same as `load_path`, but with a default value filled out. Call `initialize` to set this
24    /// up.
25    #[structopt(skip)]
26    pub load: String,
27    /// A JSON list of modifiers to transform the scenario. These can be generated with the GUI.
28    #[structopt(long, parse(try_from_str = parse_modifiers), default_value = "[]")]
29    pub scenario_modifiers: ModifierList,
30    /// An arbitrary number to seed the random number generator. This is input to the deterministic
31    /// simulation, so different values affect results.
32    // TODO default_value can only handle strings, so copying SimFlags::RNG_SEED
33    #[structopt(long, default_value = "42")]
34    pub rng_seed: u64,
35    #[structopt(flatten)]
36    pub opts: SimOptions,
37}
38
39// See https://github.com/TeXitoi/structopt/issues/94
40type ModifierList = Vec<ScenarioModifier>;
41
42fn parse_modifiers(x: &str) -> Result<ModifierList> {
43    abstutil::from_json(&x.to_string().into_bytes())
44}
45
46impl SimFlags {
47    pub const RNG_SEED: u64 = 42;
48
49    pub fn initialize(&mut self) {
50        // default_value can't call functions and this value can't be hardcoded
51        self.load = self
52            .load_path
53            .clone()
54            .unwrap_or_else(|| MapName::seattle("montlake").path());
55    }
56
57    // TODO rename seattle_test
58    pub fn for_test(run_name: &str) -> SimFlags {
59        SimFlags {
60            load_path: None,
61            load: MapName::seattle("montlake").path(),
62            scenario_modifiers: Vec::new(),
63            rng_seed: SimFlags::RNG_SEED,
64            opts: SimOptions::new(run_name),
65        }
66    }
67
68    pub fn make_rng(&self) -> XorShiftRng {
69        XorShiftRng::seed_from_u64(self.rng_seed)
70    }
71
72    /// Loads a map and simulation. Not appropriate for use in the UI or on web.
73    pub fn load_synchronously(&self, timer: &mut abstutil::Timer) -> (Map, Sim, XorShiftRng) {
74        if self.load.is_empty() {
75            panic!("You forgot to call initialize on SimFlags after parsing from structopt");
76        }
77
78        let mut rng = self.make_rng();
79
80        let mut opts = self.opts.clone();
81
82        if self.load.starts_with(&abstio::path_player("saves/")) {
83            info!("Resuming from {}", self.load);
84
85            let sim: Sim = abstio::must_read_object(self.load.clone(), timer);
86
87            let mut map = Map::load_synchronously(sim.map_name.path(), timer);
88            match MapEdits::load_from_file(
89                &map,
90                abstio::path_edits(map.get_name(), &sim.edits_name),
91                timer,
92            ) {
93                Ok(edits) => {
94                    map.must_apply_edits(edits, timer);
95                    map.recalculate_pathfinding_after_edits(timer);
96                }
97                Err(err) => {
98                    panic!("Couldn't load edits \"{}\": {}", sim.edits_name, err);
99                }
100            }
101
102            (map, sim, rng)
103        } else if self.load.contains("/scenarios/") {
104            info!("Seeding the simulation from scenario {}", self.load);
105
106            let mut scenario: Scenario = abstio::must_read_object(self.load.clone(), timer);
107
108            let map = Map::load_synchronously(scenario.map_name.path(), timer);
109
110            for m in &self.scenario_modifiers {
111                scenario = m.apply(&map, scenario, &mut rng);
112            }
113
114            if opts.run_name == "unnamed" {
115                opts.run_name = scenario.scenario_name.clone();
116            }
117            let mut sim = Sim::new(&map, opts);
118            sim.instantiate(&scenario, &map, &mut rng, timer);
119
120            (map, sim, rng)
121        } else if self.load.contains("/raw_maps/") || self.load.contains("/maps/") {
122            info!("Loading map {}", self.load);
123
124            let map = Map::load_synchronously(self.load.clone(), timer);
125
126            timer.start("create sim");
127            let sim = Sim::new(&map, opts);
128            timer.stop("create sim");
129
130            (map, sim, rng)
131        } else {
132            panic!("Don't know how to load {}", self.load);
133        }
134    }
135}