improvements, cleanup, readme
This commit is contained in:
parent
e36700d93f
commit
107d585b19
4 changed files with 62 additions and 26 deletions
46
README.md
Normal file
46
README.md
Normal 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 |
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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..]) {
|
||||
|
|
|
@ -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<usize> {
|
||||
|
@ -161,17 +163,7 @@ impl PlayerStrategy for CheatingPlayerStrategy {
|
|||
view.board.is_playable(card)
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
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();
|
||||
|
|
Loading…
Reference in a new issue