From ef860fa73bb22096a285511b240fb7633743f66a Mon Sep 17 00:00:00 2001 From: Felix Bauckholt Date: Thu, 7 Mar 2019 13:54:30 +0100 Subject: [PATCH] Make runs reproducible by replacing HashMaps with FnvHashMaps --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + src/game.rs | 22 +++++++++++----------- src/main.rs | 1 + src/simulator.rs | 16 +++++++--------- src/strategies/cheating.rs | 10 +++++----- src/strategies/information.rs | 22 +++++++++++----------- 7 files changed, 43 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ce7bef7..bc376d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,6 +3,11 @@ name = "crossbeam" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "fnv" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "getopts" version = "0.2.14" @@ -34,6 +39,7 @@ name = "rust_hanabi" version = "0.1.0" dependencies = [ "crossbeam 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "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)", @@ -41,6 +47,7 @@ dependencies = [ [metadata] "checksum crossbeam 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "348228ce9f93d20ffc30c18e575f82fa41b9c8bf064806c65d41eba4771595a0" +"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9047cfbd08a437050b363d35ef160452c5fe8ea5187ae0a624708c91581d685" "checksum libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "4870ef6725dde13394134e587e4ab4eca13cb92e916209a31c851b49131d3c75" "checksum log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "038b5d13189a14e5b6ac384fdb7c691a45ef0885f6d2dddbf422e6c3506b8234" diff --git a/Cargo.toml b/Cargo.toml index 3331f4c..85f0aae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,5 @@ authors = ["Jeff Wu "] rand = "*" log = "*" getopts = "*" +fnv = "*" crossbeam = "0.2.5" diff --git a/src/game.rs b/src/game.rs index 9f4511c..ce6d388 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use fnv::FnvHashMap; use std::fmt; use std::ops::Range; @@ -46,11 +46,11 @@ impl fmt::Debug for Card { #[derive(Debug,Clone)] pub struct CardCounts { - counts: HashMap, + counts: FnvHashMap, } impl CardCounts { pub fn new() -> CardCounts { - let mut counts = HashMap::new(); + let mut counts = FnvHashMap::default(); for &color in COLORS.iter() { for &value in VALUES.iter() { counts.insert(Card::new(color, value), 0); @@ -248,7 +248,7 @@ pub struct BoardState { pub deck_size: u32, pub total_cards: u32, pub discard: Discard, - pub fireworks: HashMap, + pub fireworks: FnvHashMap, pub num_players: u32, @@ -271,7 +271,7 @@ impl BoardState { pub fn new(opts: &GameOptions, deck_size: u32) -> BoardState { let fireworks = COLORS.iter().map(|&color| { (color, Firework::new(color)) - }).collect::>(); + }).collect::>(); BoardState { deck_size: deck_size, @@ -479,7 +479,7 @@ pub struct BorrowedGameView<'a> { pub player: Player, pub hand_size: usize, // the cards of the other players, as well as the information they have - pub other_hands: HashMap, + pub other_hands: FnvHashMap, // board state pub board: &'a BoardState, } @@ -506,7 +506,7 @@ pub struct OwnedGameView { pub player: Player, pub hand_size: usize, // the cards of the other players, as well as the information they have - pub other_hands: HashMap, + pub other_hands: FnvHashMap, // board state pub board: BoardState, } @@ -515,7 +515,7 @@ impl OwnedGameView { let other_hands = borrowed_view.other_hands.iter() .map(|(&other_player, &player_state)| { (other_player, player_state.clone()) - }).collect::>(); + }).collect::>(); OwnedGameView { player: borrowed_view.player.clone(), @@ -544,7 +544,7 @@ impl GameView for OwnedGameView { // complete game state (known to nobody!) #[derive(Debug)] pub struct GameState { - pub hands: HashMap, + pub hands: FnvHashMap, pub board: BoardState, pub deck: Cards, } @@ -582,7 +582,7 @@ impl GameState { deck.pop().unwrap() }).collect::>(); (player, hand) - }).collect::>(); + }).collect::>(); GameState { hands: hands, @@ -605,7 +605,7 @@ impl GameState { // get the game state view of a particular player pub fn get_view(&self, player: Player) -> BorrowedGameView { - let mut other_hands = HashMap::new(); + let mut other_hands = FnvHashMap::default(); for (&other_player, hand) in &self.hands { if player != other_player { other_hands.insert(other_player, hand); diff --git a/src/main.rs b/src/main.rs index 5069fd3..7f46040 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ extern crate getopts; extern crate log; extern crate rand; extern crate crossbeam; +extern crate fnv; mod helpers; mod game; diff --git a/src/simulator.rs b/src/simulator.rs index 2ebf36a..39b6d62 100644 --- a/src/simulator.rs +++ b/src/simulator.rs @@ -1,5 +1,5 @@ use rand::{self, Rng, SeedableRng}; -use std::collections::HashMap; +use fnv::FnvHashMap; use std::fmt; use crossbeam; @@ -25,17 +25,15 @@ fn new_deck(seed: u32) -> Cards { pub fn simulate_once( opts: &GameOptions, game_strategy: Box, - seed_opt: Option, + seed: u32, ) -> GameState { - - let seed = seed_opt.unwrap_or(rand::thread_rng().next_u32()); let deck = new_deck(seed); let mut game = GameState::new(opts, deck); let mut strategies = game.get_players().map(|player| { (player, game_strategy.initialize(player, &game.get_view(player))) - }).collect::>>(); + }).collect::>>(); while !game.is_over() { let player = game.board.player; @@ -69,14 +67,14 @@ pub fn simulate_once( #[derive(Debug)] struct Histogram { - pub hist: HashMap, + pub hist: FnvHashMap, pub sum: Score, pub total_count: u32, } impl Histogram { pub fn new() -> Histogram { Histogram { - hist: HashMap::new(), + hist: FnvHashMap::default(), sum: 0, total_count: 0, } @@ -127,7 +125,7 @@ pub fn simulate( progress_info: Option, ) where T: GameStrategyConfig + Sync { - let first_seed = first_seed_opt.unwrap_or(rand::thread_rng().next_u32()); + let first_seed = first_seed_opt.unwrap_or_else(|| rand::thread_rng().next_u32()); let strat_config_ref = &strat_config; crossbeam::scope(|scope| { @@ -154,7 +152,7 @@ pub fn simulate( ); } } - let game = simulate_once(&opts, strat_config_ref.initialize(&opts), Some(seed)); + let game = simulate_once(&opts, strat_config_ref.initialize(&opts), seed); let score = game.score(); lives_histogram.insert(game.board.lives_remaining); score_histogram.insert(score); diff --git a/src/strategies/cheating.rs b/src/strategies/cheating.rs index 6f64406..c71bfd5 100644 --- a/src/strategies/cheating.rs +++ b/src/strategies/cheating.rs @@ -1,6 +1,6 @@ use std::rc::Rc; use std::cell::{RefCell}; -use std::collections::{HashMap, HashSet}; +use fnv::{FnvHashMap, FnvHashSet}; use strategy::*; use game::*; @@ -31,13 +31,13 @@ impl GameStrategyConfig for CheatingStrategyConfig { } pub struct CheatingStrategy { - player_hands_cheat: Rc>>, + player_hands_cheat: Rc>>, } impl CheatingStrategy { pub fn new() -> CheatingStrategy { CheatingStrategy { - player_hands_cheat: Rc::new(RefCell::new(HashMap::new())), + player_hands_cheat: Rc::new(RefCell::new(FnvHashMap::default())), } } } @@ -56,7 +56,7 @@ impl GameStrategy for CheatingStrategy { } pub struct CheatingPlayerStrategy { - player_hands_cheat: Rc>>, + player_hands_cheat: Rc>>, me: Player, } impl CheatingPlayerStrategy { @@ -120,7 +120,7 @@ impl CheatingPlayerStrategy { } fn find_useless_card(&self, view: &BorrowedGameView, hand: &Cards) -> Option { - let mut set: HashSet = HashSet::new(); + let mut set: FnvHashSet = FnvHashSet::default(); for (i, card) in hand.iter().enumerate() { if view.board.is_dead(card) { diff --git a/src/strategies/information.rs b/src/strategies/information.rs index 171e6b1..0d6ed70 100644 --- a/src/strategies/information.rs +++ b/src/strategies/information.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, HashSet}; +use fnv::{FnvHashMap, FnvHashSet}; use std::cmp::Ordering; use strategy::*; @@ -185,14 +185,14 @@ impl Question for AdditiveComboQuestion { struct CardPossibilityPartition { index: usize, n_partitions: u32, - partition: HashMap, + partition: FnvHashMap, } impl CardPossibilityPartition { fn new( index: usize, max_n_partitions: u32, card_table: &CardPossibilityTable, view: &OwnedGameView ) -> CardPossibilityPartition { let mut cur_block = 0; - let mut partition = HashMap::new(); + let mut partition = FnvHashMap::default(); let mut n_partitions = 0; let has_dead = card_table.probability_is_dead(&view.board) != 0.0; @@ -288,7 +288,7 @@ impl GameStrategy for InformationStrategy { view.board.get_players().map(|player| { let hand_info = HandInfo::new(view.board.hand_size); (player, hand_info) - }).collect::>(); + }).collect::>(); Box::new(InformationPlayerStrategy { me: player, @@ -301,7 +301,7 @@ impl GameStrategy for InformationStrategy { pub struct InformationPlayerStrategy { me: Player, - public_info: HashMap>, + public_info: FnvHashMap>, public_counts: CardCounts, // what any newly drawn card should be last_view: OwnedGameView, // the view on the previous turn } @@ -550,8 +550,8 @@ impl InformationPlayerStrategy { } fn find_useless_cards(&self, view: &OwnedGameView, hand: &HandInfo) -> Vec { - let mut useless: HashSet = HashSet::new(); - let mut seen: HashMap = HashMap::new(); + let mut useless: FnvHashSet = FnvHashSet::default(); + let mut seen: FnvHashMap = FnvHashMap::default(); for (i, card_table) in hand.iter().enumerate() { if card_table.probability_is_dead(view.get_board()) == 1.0 { @@ -792,7 +792,7 @@ impl InformationPlayerStrategy { (0 .. n - 1).into_iter().map(|i| { (player + 1 + i) % n }).collect() } - fn get_best_hint_of_options(&self, hint_player: Player, hint_option_set: HashSet) -> Hinted { + fn get_best_hint_of_options(&self, hint_player: Player, hint_option_set: FnvHashSet) -> Hinted { let view = &self.last_view; // using hint goodness barely helps @@ -864,7 +864,7 @@ impl InformationPlayerStrategy { 2 => { // NOTE: this doesn't do that much better than just hinting // the first thing that doesn't match the hint_card - let mut hint_option_set = HashSet::new(); + let mut hint_option_set = FnvHashSet::default(); for card in hand { if card.color != hint_card.color { hint_option_set.insert(Hinted::Color(card.color)); @@ -889,7 +889,7 @@ impl InformationPlayerStrategy { } 2 => { // Any value hint for a card other than the first - let mut hint_option_set = HashSet::new(); + let mut hint_option_set = FnvHashSet::default(); for card in hand { if card.value != hint_card.value { hint_option_set.insert(Hinted::Value(card.value)); @@ -899,7 +899,7 @@ impl InformationPlayerStrategy { } 3 => { // Any color hint for a card other than the first - let mut hint_option_set = HashSet::new(); + let mut hint_option_set = FnvHashSet::default(); for card in hand { if card.color != hint_card.color { hint_option_set.insert(Hinted::Color(card.color));