improvements, cleanup, readme

This commit is contained in:
Jeff Wu 2016-03-19 14:14:29 -07:00
parent e36700d93f
commit 107d585b19
4 changed files with 62 additions and 26 deletions

46
README.md Normal file
View file

@ -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 |

View file

@ -463,6 +463,10 @@ impl BoardState {
self.deck.len() as u32 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 { pub fn player_to_left(&self, player: &Player) -> Player {
(player + 1) % self.num_players (player + 1) % self.num_players
} }

View file

@ -42,7 +42,7 @@ fn main() {
opts.optopt("l", "loglevel", "Log level, one of 'trace', 'debug', 'info', 'warn', and 'error'", "LOGLEVEL"); 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("n", "ntrials", "Number of games to simulate", "NTRIALS");
opts.optopt("t", "nthreads", "Number of threads to use for simulation", "NTHREADS"); 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.optopt("p", "nplayers", "Number of players", "NPLAYERS");
opts.optflag("h", "help", "Print this help menu"); opts.optflag("h", "help", "Print this help menu");
let matches = match opts.parse(&args[1..]) { let matches = match opts.parse(&args[1..]) {

View file

@ -91,6 +91,8 @@ impl CheatingPlayerStrategy {
continue continue
} }
if !view.board.is_dispensable(card) { if !view.board.is_dispensable(card) {
value += 20 - card.value;
} else if view.board.is_playable(card) {
value += 10 - card.value; value += 10 - card.value;
} else { } else {
value += 1; value += 1;
@ -111,15 +113,15 @@ impl CheatingPlayerStrategy {
if view.has_card(&player, card) { if view.has_card(&player, card) {
let their_hand_value = self.hand_play_value(view, states.get(&player).unwrap()); 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 // they can play this card, and have less urgent plays than i do
if their_hand_value <= my_hand_value { if their_hand_value < my_hand_value {
return 1; return 10 - (card.value as i32)
} }
} }
} }
} }
// there are no hints // there are no hints
// maybe value 5s more? // 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<usize> { fn find_useless_card(&self, view: &GameStateView, hand: &Cards) -> Option<usize> {
@ -161,17 +163,7 @@ impl PlayerStrategy for CheatingPlayerStrategy {
view.board.is_playable(card) view.board.is_playable(card)
}).collect::<Vec<_>>(); }).collect::<Vec<_>>();
let mut should_play = true; if playable_cards.len() > 0 {
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 {
// play the best playable card // play the best playable card
// the higher the play_score, the better to play // the higher the play_score, the better to play
let mut play_card = None; let mut play_card = None;
@ -195,10 +187,10 @@ impl PlayerStrategy for CheatingPlayerStrategy {
// we would not reach the final countdown round // we would not reach the final countdown round
// e.g. 50 total, 25 to play, 20 in hand // e.g. 50 total, 25 to play, 20 in hand
let discard_threshold = let discard_threshold =
view.board.total_cards as usize view.board.total_cards
- (COLORS.len() * VALUES.len()) - (COLORS.len() * VALUES.len()) as u32
- (view.board.num_players * view.board.hand_size) as usize; - (view.board.num_players * view.board.hand_size);
if view.board.discard.cards.len() <= discard_threshold { if view.board.discard_size() <= discard_threshold {
// if anything is totally useless, discard it // if anything is totally useless, discard it
if let Some(i) = self.find_useless_card(view, my_cards) { if let Some(i) = self.find_useless_card(view, my_cards) {
return TurnChoice::Discard(i); return TurnChoice::Discard(i);
@ -207,7 +199,7 @@ impl PlayerStrategy for CheatingPlayerStrategy {
// hinting is better than discarding dead cards // hinting is better than discarding dead cards
// (probably because it stalls the deck-drawing). // (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) { if self.someone_else_can_play(view) {
return self.throwaway_hint(view); return self.throwaway_hint(view);
} }
@ -236,12 +228,6 @@ impl PlayerStrategy for CheatingPlayerStrategy {
} }
} }
if let Some(card) = discard_card { 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| { let index = my_cards.iter().position(|iter_card| {
card == iter_card card == iter_card
}).unwrap(); }).unwrap();