From 107d585b19adb75a8fe30f33f62294004b962672 Mon Sep 17 00:00:00 2001 From: Jeff Wu Date: Sat, 19 Mar 2016 14:14:29 -0700 Subject: [PATCH] improvements, cleanup, readme --- README.md | 46 ++++++++++++++++++++++++++++++++++++++ src/game.rs | 4 ++++ src/main.rs | 2 +- src/strategies/cheating.rs | 36 +++++++++-------------------- 4 files changed, 62 insertions(+), 26 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..4663b84 --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +# Simulations of Hanabi strategies + +Hanabi is an interesting cooperative game, with +[simple rules](https://boardgamegeek.com/article/10670613#10670613). + +I plan on reimplementing strategies with ideas from [this paper](https://d0474d97-a-62cb3a1a-s-sites.googlegroups.com/site/rmgpgrwc/research-papers/Hanabi_final.pdf). + +Some similar projects I am aware of: + +- https://github.com/rjtobin/HanSim (written for the paper mentioned above) +- https://github.com/Quuxplusone/Hanabi + +## Setup + +Install rust/rustc and cargo, and change the options in main.rs appropriately. + +`cargo run -- -h` + +``` +Usage: target/debug/rust_hanabi [options] + +Options: + -l, --loglevel LOGLEVEL + Log level, one of 'trace', 'debug', 'info', 'warn', and 'error' + -n, --ntrials NTRIALS + Number of games to simulate + -t, --nthreads NTHREADS + Number of threads to use for simulation + -s, --seed SEED Seed for PRNG + -p, --nplayers NPLAYERS + Number of players + -h, --help Print this help menu +``` + +For example, + +`cargo run -- -n 10000 -s 0 -t 2 -p 3` + +## Results (sparsely updated) + +Currently, on seeds 0-9999, we have: + + | 2p | 3p | 4p | 5p | +----------|---------|---------|---------|---------| +cheating | 24.8600 | 24.9781 | 24.9715 | 24.9583 | + diff --git a/src/game.rs b/src/game.rs index b2ee641..90f8210 100644 --- a/src/game.rs +++ b/src/game.rs @@ -463,6 +463,10 @@ impl BoardState { self.deck.len() as u32 } + pub fn discard_size(&self) -> u32 { + self.discard.cards.len() as u32 + } + pub fn player_to_left(&self, player: &Player) -> Player { (player + 1) % self.num_players } diff --git a/src/main.rs b/src/main.rs index 62c77bf..1b6a0f2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -42,7 +42,7 @@ fn main() { 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("t", "nthreads", "Number of threads to use for simulation", "NTHREADS"); - opts.optopt("s", "seed", "Seed for PRNG (can only be used with n=1)", "SEED"); + opts.optopt("s", "seed", "Seed for PRNG", "SEED"); opts.optopt("p", "nplayers", "Number of players", "NPLAYERS"); opts.optflag("h", "help", "Print this help menu"); let matches = match opts.parse(&args[1..]) { diff --git a/src/strategies/cheating.rs b/src/strategies/cheating.rs index f025144..8bc6aaf 100644 --- a/src/strategies/cheating.rs +++ b/src/strategies/cheating.rs @@ -91,6 +91,8 @@ impl CheatingPlayerStrategy { continue } if !view.board.is_dispensable(card) { + value += 20 - card.value; + } else if view.board.is_playable(card) { value += 10 - card.value; } else { value += 1; @@ -111,15 +113,15 @@ impl CheatingPlayerStrategy { if view.has_card(&player, card) { let their_hand_value = self.hand_play_value(view, states.get(&player).unwrap()); // they can play this card, and have less urgent plays than i do - if their_hand_value <= my_hand_value { - return 1; + if their_hand_value < my_hand_value { + return 10 - (card.value as i32) } } } } // there are no hints // maybe value 5s more? - 5 + (5 - (card.value as i32)) + 20 - (card.value as i32) } fn find_useless_card(&self, view: &GameStateView, hand: &Cards) -> Option { @@ -161,17 +163,7 @@ impl PlayerStrategy for CheatingPlayerStrategy { view.board.is_playable(card) }).collect::>(); - let mut should_play = true; - if playable_cards.len() == 0 { - should_play = false; - } - // if (playable_cards.len() == 1) && - // (view.board.deck_size() == 1) && - // (view.board.hints_remaining > 1) { - // return self.throwaway_hint(view); - // } - - if should_play { + if playable_cards.len() > 0 { // play the best playable card // the higher the play_score, the better to play let mut play_card = None; @@ -195,10 +187,10 @@ impl PlayerStrategy for CheatingPlayerStrategy { // we would not reach the final countdown round // e.g. 50 total, 25 to play, 20 in hand let discard_threshold = - view.board.total_cards as usize - - (COLORS.len() * VALUES.len()) - - (view.board.num_players * view.board.hand_size) as usize; - if view.board.discard.cards.len() <= discard_threshold { + view.board.total_cards + - (COLORS.len() * VALUES.len()) as u32 + - (view.board.num_players * view.board.hand_size); + if view.board.discard_size() <= discard_threshold { // if anything is totally useless, discard it if let Some(i) = self.find_useless_card(view, my_cards) { return TurnChoice::Discard(i); @@ -207,7 +199,7 @@ impl PlayerStrategy for CheatingPlayerStrategy { // hinting is better than discarding dead cards // (probably because it stalls the deck-drawing). - if view.board.hints_remaining > 1 { + if view.board.hints_remaining > 0 { if self.someone_else_can_play(view) { return self.throwaway_hint(view); } @@ -236,12 +228,6 @@ impl PlayerStrategy for CheatingPlayerStrategy { } } if let Some(card) = discard_card { - if view.board.hints_remaining > 0 { - if !view.can_see(card) { - return self.throwaway_hint(view); - } - } - let index = my_cards.iter().position(|iter_card| { card == iter_card }).unwrap();