diff options
-rw-r--r-- | src/cli.yml | 26 | ||||
-rw-r--r-- | src/level_gen/cave_automata.rs | 48 | ||||
-rw-r--r-- | src/main.rs | 51 | ||||
-rw-r--r-- | src/types/mod.rs | 6 |
4 files changed, 106 insertions, 25 deletions
diff --git a/src/cli.yml b/src/cli.yml index 937b44c9c6f9..4b2e94e57b0e 100644 --- a/src/cli.yml +++ b/src/cli.yml @@ -10,7 +10,7 @@ args: help: Sets a custom config file takes_value: true subcommands: - - debug: + - info: about: Writes debug information to the terminal and exits - generate-level: about: Generate a level and print it to the screen @@ -20,3 +20,27 @@ subcommands: value_name: GEN help: Select which generator to use takes_value: true + - width: + long: width + short: w + value_name: WIDTH + takes_value: true + - height: + long: height + short: h + value_name: HEIGHT + takes_value: true + - start-alive-chance: + long: start-alive-chance + takes_value: true + - birth_limit: + long: birth-limit + takes_value: true + - death_limit: + long: death-limit + takes_value: true + - steps: + long: steps + short: s + value_name: STEPS + takes_value: true diff --git a/src/level_gen/cave_automata.rs b/src/level_gen/cave_automata.rs index e46d542e6955..6a237c0303df 100644 --- a/src/level_gen/cave_automata.rs +++ b/src/level_gen/cave_automata.rs @@ -4,17 +4,46 @@ use rand::Rng; pub struct Params { chance_to_start_alive: f64, - dimensions: Dimensions, birth_limit: i32, death_limit: i32, steps: usize, } +macro_rules! parse_optional { + ($out: ident . $attr: ident, $matches: expr, $arg: expr) => { + if let Some(val_s) = $matches.value_of($arg) { + $out.$attr = val_s.parse().unwrap(); + } + }; +} + +macro_rules! parse_optional_matches { + ($matches: expr) => {}; + ($matches: expr , { $ret: ident . $attr: ident = $arg: expr }) => { + parse_optional!($ret.$attr, $matches, $arg); + }; + ($matches: expr, { $($ret: ident . $attr: ident = $arg: expr ,)* }) => { + $(parse_optional!($ret.$attr, $matches, $arg);)* + }; +} + +impl Params { + pub fn from_matches<'a>(matches: &clap::ArgMatches<'a>) -> Self { + let mut ret: Self = Default::default(); + parse_optional_matches!(matches, { + ret.chance_to_start_alive = "start-alive-chance", + ret.birth_limit = "birth-limit", + ret.death_limit = "death-limit", + ret.steps = "steps", + }); + ret + } +} + impl Default for Params { fn default() -> Self { Params { chance_to_start_alive: 0.45, - dimensions: Dimensions { w: 80, h: 20 }, birth_limit: 4, death_limit: 3, steps: 2, @@ -23,21 +52,26 @@ impl Default for Params { } pub fn generate<R: Rng + ?Sized>( + dimensions: &Dimensions, params: &Params, rand: &mut R, ) -> Vec<Vec<bool>> { let mut cells = - rand_initialize(¶ms.dimensions, rand, params.chance_to_start_alive); + rand_initialize(&dimensions, rand, params.chance_to_start_alive); for _ in 0..params.steps { - step_automata(&mut cells, params); + step_automata(&mut cells, dimensions, params); } cells } -fn step_automata(cells: &mut Vec<Vec<bool>>, params: &Params) { +fn step_automata( + cells: &mut Vec<Vec<bool>>, + dimensions: &Dimensions, + params: &Params, +) { let orig_cells = (*cells).clone(); - for x in 0..(params.dimensions.h as usize) { - for y in 0..(params.dimensions.w as usize) { + for x in 0..(dimensions.h as usize) { + for y in 0..(dimensions.w as usize) { let nbs = num_alive_neighbors(&orig_cells, x as i32, y as i32); if orig_cells[x][y] { if nbs < params.death_limit { diff --git a/src/main.rs b/src/main.rs index 8479b5fa437c..dc958ca1a16d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,6 +39,7 @@ mod level_gen; mod messages; mod settings; +use crate::types::Dimensions; use clap::App; use game::Game; use prettytable::format::consts::FORMAT_BOX_CHARS; @@ -59,7 +60,7 @@ fn init( stdin: StdinLock<'_>, w: u16, h: u16, -) { +) -> io::Result<()> { panic::set_hook(if settings.logging.print_backtrace { Box::new(|info| (error!("{}\n{:#?}", info, Backtrace::new()))) } else { @@ -67,10 +68,36 @@ fn init( }); let game = Game::new(settings, stdout, stdin, w, h); - game.run().unwrap() + game.run() } -fn main() { +fn generate_level<'a, W: io::Write>( + stdout: &mut W, + params: &clap::ArgMatches<'a>, +) -> io::Result<()> { + let mut rand = SmallRng::from_entropy(); + + let mut dimensions: Dimensions = Default::default(); + if let Some(h_s) = params.value_of("height") { + dimensions.h = h_s.parse().unwrap(); + } + if let Some(w_s) = params.value_of("width") { + dimensions.w = w_s.parse().unwrap(); + } + + let level = match params.value_of("generator") { + None => panic!("Must supply a generator with --generator"), + Some("cave_automata") => level_gen::cave_automata::generate( + &dimensions, + &level_gen::cave_automata::Params::from_matches(params), + &mut rand, + ), + Some(gen) => panic!("Unrecognized generator: {}", gen), + }; + level_gen::display::print_generated_level(&level, stdout) +} + +fn main() -> io::Result<()> { let yaml = load_yaml!("cli.yml"); let matches = App::from_yaml(yaml).get_matches(); let settings = Settings::load().unwrap(); @@ -85,7 +112,7 @@ fn main() { let (termwidth, termheight) = termsize.unwrap_or((70, 40)); match matches.subcommand() { - ("debug", _) => { + ("info", _) => { let mut table = table!( [br->"termwidth", termwidth], [br->"termheight", termheight], @@ -94,24 +121,14 @@ fn main() { ); table.set_format(*FORMAT_BOX_CHARS); table.printstd(); + Ok(()) } ("generate-level", params) => { - let params = params.unwrap(); - let mut rand = SmallRng::from_entropy(); - let level = match params.value_of("generator") { - None => panic!("Must supply a generator with --generator"), - Some("cave_automata") => level_gen::cave_automata::generate( - &Default::default(), - &mut rand, - ), - Some(gen) => panic!("Unrecognized generator: {}", gen), - }; - level_gen::display::print_generated_level(&level, &mut stdout) - .unwrap(); + generate_level(&mut stdout, params.unwrap()) } _ => { let stdout = stdout.into_raw_mode().unwrap(); - init(settings, stdout, stdin, termwidth, termheight); + init(settings, stdout, stdin, termwidth, termheight) } } } diff --git a/src/types/mod.rs b/src/types/mod.rs index 1e86fb369e86..e656048e873c 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -24,6 +24,12 @@ pub struct Dimensions { pub const ZERO_DIMENSIONS: Dimensions = Dimensions { w: 0, h: 0 }; pub const UNIT_DIMENSIONS: Dimensions = Dimensions { w: 1, h: 1 }; +impl Default for Dimensions { + fn default() -> Self { + Dimensions { w: 80, h: 20 } + } +} + impl ops::Sub<Dimensions> for Dimensions { type Output = Dimensions; fn sub(self, dims: Dimensions) -> Dimensions { |