diff --git a/Cargo.lock b/Cargo.lock index 2a51773..a94108f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,10 +2,16 @@ name = "rust_hanabi" version = "0.1.0" dependencies = [ + "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "getopts" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "libc" version = "0.2.7" diff --git a/Cargo.toml b/Cargo.toml index b51f826..2768528 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,4 @@ authors = ["Jeff Wu "] [dependencies] rand = "*" log = "*" +getopts = "*" diff --git a/src/main.rs b/src/main.rs index 101d447..b1d0f1c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ -extern crate rand; +extern crate getopts; #[macro_use] extern crate log; +extern crate rand; mod game; mod simulator; @@ -10,13 +11,13 @@ mod strategies { } mod info; -#[allow(unused_imports)] -use log::LogLevel::{Trace, Debug, Info, Warn, Error}; +use getopts::Options; +use std::str::FromStr; struct SimpleLogger; impl log::Log for SimpleLogger { fn enabled(&self, metadata: &log::LogMetadata) -> bool { - metadata.level() <= Debug + metadata.level() <= log::LogLevel::Trace } fn log(&self, record: &log::LogRecord) { @@ -26,37 +27,75 @@ impl log::Log for SimpleLogger { } } + +fn print_usage(program: &str, opts: Options) { + print!("{}", opts.usage(&format!("Usage: {} [options]", program))); +} + + fn main() { - // TODO: make a binary with command line options + let args: Vec = std::env::args().collect(); + let program = args[0].clone(); + + let mut opts = Options::new(); + opts.optopt("l", "loglevel", "Log level, one of 'trace', 'debug', 'info', 'warn', and 'error'", "LOGLEVEL"); + opts.optopt("n", "ntrials", "Number of games to simulate", "NTRIALS"); + opts.optopt("s", "seed", "Seed for PRNG (can only be used with n=1)", "SEED"); + opts.optflag("h", "help", "Print this help menu"); + let matches = match opts.parse(&args[1..]) { + Ok(m) => { m } + Err(f) => { + print_usage(&program, opts); + panic!(f.to_string()) + } + }; + if matches.opt_present("h") { + return print_usage(&program, opts); + } + if !matches.free.is_empty() { + return print_usage(&program, opts); + } + + let log_level_str : &str = &matches.opt_str("l").unwrap_or("info".to_string()); + let log_level = match log_level_str { + "trace" => { log::LogLevelFilter::Trace } + "debug" => { log::LogLevelFilter::Debug } + "info" => { log::LogLevelFilter::Info } + "warn" => { log::LogLevelFilter::Warn } + "error" => { log::LogLevelFilter::Error } + _ => { panic!("Unexpected log level argument {}", log_level_str); } + }; log::set_logger(|max_log_level| { - // max_log_level.set(log::LogLevelFilter::Trace); - max_log_level.set(log::LogLevelFilter::Info); + max_log_level.set(log_level); Box::new(SimpleLogger) }).unwrap(); + let n = u32::from_str(&matches.opt_str("n").unwrap_or("1".to_string())).unwrap(); + + let seed = matches.opt_str("s").map(|seed_str| { u32::from_str(&seed_str).unwrap() }); + + // TODO: make these configurable let opts = game::GameOptions { num_players: 4, hand_size: 4, num_hints: 8, num_lives: 3, }; - let n = 1000; - // simulator::simulate(&opts, &strategies::examples::AlwaysDiscard, n); - // simulator::simulate_symmetric(&opts, strategies::examples::AlwaysPlayConfig, n); - // simulator::simulate_symmetric( - // &opts, - // strategies::examples::RandomStrategyConfig { - // hint_probability: 0.4, - // play_probability: 0.2, - // }, - // n - // ); - simulator::simulate_symmetric( - &opts, - strategies::cheating::CheatingStrategyConfig::new(), - n - ); + + // TODO: make this configurable + // let strategy = strategies::examples::AlwaysPlayConfig; + // let strategy = strategies::examples::RandomStrategyConfig { + // hint_probability: 0.4, + // play_probability: 0.2, + // }; + let strategy = strategies::cheating::CheatingStrategyConfig::new(); + if n == 1 { + simulator::simulate_symmetric_once(&opts, strategy, seed); + } else { + simulator::simulate_symmetric(&opts, strategy, n); + } + // simulator::simulate_symmetric_once( // &opts, Some(999), // strategies::cheating::CheatingStrategyConfig::new(), diff --git a/src/simulator.rs b/src/simulator.rs index 34b4263..0df7abd 100644 --- a/src/simulator.rs +++ b/src/simulator.rs @@ -14,8 +14,8 @@ pub trait StrategyConfig { pub fn simulate_once<'a>( opts: &GameOptions, + strat_configs: &Vec>, seed_opt: Option, - strat_configs: &Vec> ) -> Score { let seed = if let Some(seed) = seed_opt { @@ -69,7 +69,7 @@ pub fn simulate_once<'a>( pub fn simulate<'a>(opts: &GameOptions, strat_configs: &Vec>, n_trials: u32) -> f32 { let mut total_score = 0; for seed in 0..n_trials { - let score = simulate_once(&opts, Some(seed), strat_configs); + let score = simulate_once(&opts, strat_configs, Some(seed)); debug!("Scored: {:?}", score); if score != 25 { info!("Seed with non-perfect score: {:?}", seed); @@ -83,15 +83,15 @@ pub fn simulate<'a>(opts: &GameOptions, strat_configs: &Vec( opts: &GameOptions, + strat_config: S, seed_opt: Option, - strat_config: S ) -> Score { let mut strat_configs = Vec::new(); for _ in 0..opts.num_players { strat_configs.push(Box::new(strat_config.clone()) as Box); } - simulate_once(opts, seed_opt, &strat_configs) + simulate_once(opts, &strat_configs, seed_opt) } pub fn simulate_symmetric<'a, S: StrategyConfig + Clone + 'a>(opts: &GameOptions, strat_config: S, n_trials: u32) -> f32 {