Compare commits

...

3 Commits

5 changed files with 180 additions and 18 deletions

131
src/arguments.rs Normal file
View File

@ -0,0 +1,131 @@
use std::collections::{BTreeMap};
pub enum ArgumentFormat {
Flag,
Store
}
pub struct FlagArgument {
pub short_name: char,
pub long_name: &'static str,
pub description: &'static str,
pub format: ArgumentFormat,
pub required: bool,
pub default: Option<&'static str>
}
pub struct PositionalArgument {
pub long_name: &'static str,
pub description: &'static str,
pub default: Option<&'static str>,
pub required: bool,
}
pub struct ArgumentCollection {
storage_map: BTreeMap<&'static str, String>
}
pub fn parse_arguments(
accepted_flag_arguments: Vec<FlagArgument>,
mut accepted_positional_arguments: Vec<PositionalArgument>,
arguments: Vec<String>
) -> Result<ArgumentCollection, String> {
let mut accepted_long_arguments: BTreeMap<&'static str, FlagArgument> = BTreeMap::new();
let mut accepted_short_arguments: BTreeMap<char, &'static str> = BTreeMap::new();
let mut storage_map: BTreeMap<&'static str, String> = BTreeMap::new();
// Re-map the flag arguments for ease of access when parsing
for argument in accepted_flag_arguments {
let long_name = argument.long_name;
let short_name = argument.short_name;
if argument.default.is_some() {
storage_map.insert(long_name, argument.default.unwrap().to_string());
}
accepted_long_arguments.insert(long_name, argument);
accepted_short_arguments.insert(
short_name,
long_name
);
}
let positional_argument_count = accepted_positional_arguments.len();
// `.pop()` takes from the back of the vector, so we need to reverse it
let mut reversed_arguments = arguments.clone();
reversed_arguments.reverse();
// The first argument is always the binary name
let _ = reversed_arguments.pop();
while let Some(arg) = reversed_arguments.pop() {
if arg.starts_with("-") {
let argument_struct = if arg.starts_with("--") {
match accepted_long_arguments.get(&arg[2..]) {
Some(a) => a,
None => return Err(format!("Unknown argument: {arg}"))
}
} else {
let long_name = match accepted_short_arguments.get(&arg[1..].chars().next().unwrap()) {
Some(a) => a,
None => return Err(format!("Unknown argument: {arg}"))
};
accepted_long_arguments.get(long_name).unwrap()
};
let value_to_store = match argument_struct.format {
ArgumentFormat::Flag => "".to_string(),
ArgumentFormat::Store => match reversed_arguments.pop() {
Some(v) => v,
None => return Err(format!("Missing value for argument {arg}"))
}
};
storage_map.insert(argument_struct.long_name, value_to_store);
} else {
let positional_argument = accepted_positional_arguments.pop()
.expect(format!("Too many positional arguments given. Limit: {positional_argument_count}").as_str());
storage_map.insert(positional_argument.long_name, arg);
}
}
for argument in accepted_long_arguments.values() {
if !storage_map.contains_key(argument.long_name) {
if argument.required {
panic!("Missing required flag: --{}", argument.long_name);
}
if argument.default.is_some() {
storage_map.insert(argument.long_name, argument.default.unwrap().to_string());
}
}
}
for argument in accepted_positional_arguments {
if storage_map.get(argument.long_name).is_none() {
if argument.required {
panic!("Missing required positional argument: {}", argument.long_name);
}
if argument.default.is_some() {
storage_map.insert(argument.long_name, argument.default.unwrap().to_string());
}
}
}
Ok(ArgumentCollection { storage_map: storage_map })
}
impl ArgumentCollection {
pub fn get(&self, arg: &str) -> &String {
self.storage_map.get(arg).expect(format!("Unknown argument: {arg}").as_str())
}
pub fn flag(&self, arg: &str) -> bool {
self.storage_map.contains_key(arg)
}
}

View File

@ -5,6 +5,7 @@ use std::io::Read;
use std::collections::{VecDeque, HashMap, BTreeSet, BTreeMap};
use ponderosa::{math, stack, state, control, forestry};
use ponderosa::arguments::{parse_arguments, PositionalArgument, FlagArgument, ArgumentFormat};
fn parse_rom(path: &String) -> io::Result<(state::Land, Vec<state::Rule>, HashMap<u16, state::Tree>)> {
let mut rom_file = File::open(path)?;
@ -66,13 +67,7 @@ fn parse_rom(path: &String) -> io::Result<(state::Land, Vec<state::Rule>, HashMa
}
println!("World\n=====");
for y in (0..land.height).rev() {
for x in 0..land.width {
print!("{}\t", land.map.get(&(x, y)).unwrap_or(&0u16));
}
println!();
}
dbg!(&land);
rom_words.pop_front().unwrap(); // null-word delimiter
@ -106,15 +101,41 @@ fn parse_rom(path: &String) -> io::Result<(state::Land, Vec<state::Rule>, HashMa
}
fn main() -> io::Result<()> {
let args: Vec<String> = env::args().collect();
let rom_path = &args[1];
let unparsed_args: Vec<String> = env::args().collect();
let positional_arguments = vec![
PositionalArgument {
long_name: "path",
description: "Path to the compiled ROM file",
required: true,
default: None
},
];
let flag_arguments = vec![
FlagArgument {
long_name: "generations",
short_name: 'g',
description: "Number of generations before halting. If 0, the program will run until all trees have been composted",
format: ArgumentFormat::Store,
required: false,
default: Some("1")
},
];
let arguments = match parse_arguments(flag_arguments, positional_arguments, unparsed_args) {
Ok(map) => map,
Err(e) => panic!("{}", e),
};
let rom_path = &arguments.get("path");
let (mut land, mut rules, mut trees) = parse_rom(rom_path).unwrap();
let mut scheduled_trees = vec![0u16];
let mut next_tree_id = 1u16;
let generation_count = 1;
let generation_count = arguments.get("generations").parse::<u16>().unwrap();
for _ in 0..generation_count {
let mut trees_to_plant: Vec<state::Tree> = Vec::new();
@ -227,13 +248,7 @@ fn main() -> io::Result<()> {
}
println!("Resulting World\n=====");
for y in (0..land.height).rev() {
for x in 0..land.width {
print!("{}\t", land.map.get(&(x, y)).unwrap_or(&0u16));
}
println!();
}
dbg!(land);
Ok(())
}

View File

@ -3,3 +3,4 @@ pub mod stack;
pub mod state;
pub mod control;
pub mod forestry;
pub mod arguments;

View File

@ -12,7 +12,7 @@ macro_rules! define_binary_op {
let $left = tree.pull_by_stack_code((stack_code & 0b100) >> 2);
let $right = tree.pull_by_stack_code((stack_code & 0b010) >> 1);
let $right = if !use_literal { $left } else { literal };
let $right = if !use_literal { $right } else { literal };
if is_signed {
let $left = $left as i16;

View File

@ -92,6 +92,21 @@ impl Land {
}
}
impl std::fmt::Debug for Land {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let mut built_string = "\n".to_owned();
for y in (0..self.height).rev() {
for x in 0..self.width {
built_string.push_str(&format!("{}\t", self.map.get(&(x, y)).unwrap_or(&0u16)));
}
built_string.push_str("\n");
}
write!(f, "{}", built_string)
}
}
pub type Rule = VecDeque<u16>;
pub struct Tree {