From 0b9734c20b6ede9d6fa7bd96b9a1d0868879d617 Mon Sep 17 00:00:00 2001 From: Jeff Wu Date: Sun, 13 Mar 2016 22:28:34 -0700 Subject: [PATCH] replicated 24.88 from paper --- src/game.rs | 28 ++++++++++++++----------- src/main.rs | 4 ++-- src/simulator.rs | 43 +++++++++++++++++++++++++++++--------- src/strategies/cheating.rs | 24 ++++++++++++++++++--- 4 files changed, 72 insertions(+), 27 deletions(-) diff --git a/src/game.rs b/src/game.rs index 5e420cf..7bff869 100644 --- a/src/game.rs +++ b/src/game.rs @@ -366,6 +366,10 @@ impl BoardState { self.fireworks.get(color).unwrap() } + fn get_firework_mut(&mut self, color: &Color) -> &mut Firework { + self.fireworks.get_mut(color).unwrap() + } + // returns whether a card would place on a firework pub fn is_playable(&self, card: &Card) -> bool { Some(card.value) == self.get_firework(&card.color).desired_value() @@ -658,16 +662,20 @@ impl GameState { index, card ); - let mut firework_made = false; - if self.board.is_playable(&card) { - let ref mut firework = self.board.fireworks.get_mut(&card.color).unwrap(); - firework_made = card.value == FINAL_VALUE; - debug!("Successfully played {}!", card); - if firework_made { - debug!("Firework complete for {}!", card.color); + let finished = { + let firework = self.board.get_firework_mut(&card.color); + debug!("Successfully played {}!", card); + let finished = card.value == FINAL_VALUE; + if finished { + debug!("Firework complete for {}!", card.color); + } + firework.place(card); + finished + }; + if finished { + self.board.try_add_hint(); } - firework.place(card); } else { self.board.discard.place(card); self.board.lives_remaining -= 1; @@ -676,10 +684,6 @@ impl GameState { self.board.lives_remaining ); } - - if firework_made { - self.board.try_add_hint(); - } } } diff --git a/src/main.rs b/src/main.rs index b1d0f1c..9b0b8e0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -77,7 +77,7 @@ fn main() { // TODO: make these configurable let opts = game::GameOptions { - num_players: 4, + num_players: 5, hand_size: 4, num_hints: 8, num_lives: 3, @@ -93,7 +93,7 @@ fn main() { if n == 1 { simulator::simulate_symmetric_once(&opts, strategy, seed); } else { - simulator::simulate_symmetric(&opts, strategy, n); + simulator::simulate_symmetric(&opts, strategy, n, seed); } // simulator::simulate_symmetric_once( diff --git a/src/simulator.rs b/src/simulator.rs index 0df7abd..86a31f5 100644 --- a/src/simulator.rs +++ b/src/simulator.rs @@ -18,11 +18,7 @@ pub fn simulate_once<'a>( seed_opt: Option, ) -> Score { - let seed = if let Some(seed) = seed_opt { - seed - } else { - rand::thread_rng().next_u32() - }; + let seed = seed_opt.unwrap_or(rand::thread_rng().next_u32()); let mut game = GameState::new(opts, seed); @@ -66,16 +62,37 @@ pub fn simulate_once<'a>( game.score() } -pub fn simulate<'a>(opts: &GameOptions, strat_configs: &Vec>, n_trials: u32) -> f32 { +pub fn simulate<'a>( + opts: &GameOptions, + strat_configs: &Vec>, + n_trials: u32, + first_seed_opt: Option + ) -> f32 { + let mut total_score = 0; - for seed in 0..n_trials { + let mut non_perfect_seeds = Vec::new(); + + let first_seed = first_seed_opt.unwrap_or(rand::thread_rng().next_u32()); + let mut histogram = HashMap::::new(); + for i in 0..n_trials { + if (i > 0) && (i % 1000 == 0) { + let average: f32 = (total_score as f32) / (i as f32); + info!("Trials: {}, Average so far: {}", i, average); + } + let seed = first_seed + i; let score = simulate_once(&opts, strat_configs, Some(seed)); debug!("Scored: {:?}", score); + let count = histogram.get(&score).unwrap_or(&0) + 1; + histogram.insert(score, count); if score != 25 { - info!("Seed with non-perfect score: {:?}", seed); + non_perfect_seeds.push((score, seed)); } total_score += score; } + + non_perfect_seeds.sort(); + info!("Score histogram: {:?}", histogram); + info!("Seeds with non-perfect score: {:?}", non_perfect_seeds); let average: f32 = (total_score as f32) / (n_trials as f32); info!("Average score: {:?}", average); average @@ -94,10 +111,16 @@ pub fn simulate_symmetric_once<'a, S: StrategyConfig + Clone + 'a>( 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 { +pub fn simulate_symmetric<'a, S: StrategyConfig + Clone + 'a>( + opts: &GameOptions, + strat_config: S, + n_trials: u32, + first_seed_opt: Option, + ) -> f32 { + let mut strat_configs = Vec::new(); for _ in 0..opts.num_players { strat_configs.push(Box::new(strat_config.clone()) as Box); } - simulate(opts, &strat_configs, n_trials) + simulate(opts, &strat_configs, n_trials, first_seed_opt) } diff --git a/src/strategies/cheating.rs b/src/strategies/cheating.rs index a1c3377..141ca8c 100644 --- a/src/strategies/cheating.rs +++ b/src/strategies/cheating.rs @@ -117,15 +117,27 @@ impl Strategy for CheatingStrategy { }).peekable(); if playable_cards.peek() == None { + // if view.board.deck_size() > 10 { + if view.board.discard.cards.len() < 5 { + // if anything is totally useless, discard it + for (i, card) in my_cards.iter().enumerate() { + if view.board.is_dead(card) { + return TurnChoice::Discard(i); + } + } + } + + // it's unintuitive that hinting is better than + // discarding dead cards, but turns out true + if view.board.hints_remaining > 1 { + return self.throwaway_hint(view); + } // if anything is totally useless, discard it for (i, card) in my_cards.iter().enumerate() { if view.board.is_dead(card) { return TurnChoice::Discard(i); } } - if view.board.hints_remaining > 0 { - return self.throwaway_hint(view); - } // All cards are plausibly useful. // Play the best discardable card, according to the ordering induced by comparing // (is in another hand, is dispensable, value) @@ -144,6 +156,12 @@ impl Strategy for CheatingStrategy { } } 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();