Make runs reproducible by replacing HashMaps with FnvHashMaps

This commit is contained in:
Felix Bauckholt 2019-03-07 13:54:30 +01:00
parent e04d242a71
commit ef860fa73b
7 changed files with 43 additions and 36 deletions

7
Cargo.lock generated
View file

@ -3,6 +3,11 @@ name = "crossbeam"
version = "0.2.8" version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "getopts" name = "getopts"
version = "0.2.14" version = "0.2.14"
@ -34,6 +39,7 @@ name = "rust_hanabi"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"crossbeam 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "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)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
@ -41,6 +47,7 @@ dependencies = [
[metadata] [metadata]
"checksum crossbeam 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "348228ce9f93d20ffc30c18e575f82fa41b9c8bf064806c65d41eba4771595a0" "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 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 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" "checksum log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "038b5d13189a14e5b6ac384fdb7c691a45ef0885f6d2dddbf422e6c3506b8234"

View file

@ -7,4 +7,5 @@ authors = ["Jeff Wu <wuthefwasthat@gmail.com>"]
rand = "*" rand = "*"
log = "*" log = "*"
getopts = "*" getopts = "*"
fnv = "*"
crossbeam = "0.2.5" crossbeam = "0.2.5"

View file

@ -1,4 +1,4 @@
use std::collections::HashMap; use fnv::FnvHashMap;
use std::fmt; use std::fmt;
use std::ops::Range; use std::ops::Range;
@ -46,11 +46,11 @@ impl fmt::Debug for Card {
#[derive(Debug,Clone)] #[derive(Debug,Clone)]
pub struct CardCounts { pub struct CardCounts {
counts: HashMap<Card, u32>, counts: FnvHashMap<Card, u32>,
} }
impl CardCounts { impl CardCounts {
pub fn new() -> CardCounts { pub fn new() -> CardCounts {
let mut counts = HashMap::new(); let mut counts = FnvHashMap::default();
for &color in COLORS.iter() { for &color in COLORS.iter() {
for &value in VALUES.iter() { for &value in VALUES.iter() {
counts.insert(Card::new(color, value), 0); counts.insert(Card::new(color, value), 0);
@ -248,7 +248,7 @@ pub struct BoardState {
pub deck_size: u32, pub deck_size: u32,
pub total_cards: u32, pub total_cards: u32,
pub discard: Discard, pub discard: Discard,
pub fireworks: HashMap<Color, Firework>, pub fireworks: FnvHashMap<Color, Firework>,
pub num_players: u32, pub num_players: u32,
@ -271,7 +271,7 @@ impl BoardState {
pub fn new(opts: &GameOptions, deck_size: u32) -> BoardState { pub fn new(opts: &GameOptions, deck_size: u32) -> BoardState {
let fireworks = COLORS.iter().map(|&color| { let fireworks = COLORS.iter().map(|&color| {
(color, Firework::new(color)) (color, Firework::new(color))
}).collect::<HashMap<_, _>>(); }).collect::<FnvHashMap<_, _>>();
BoardState { BoardState {
deck_size: deck_size, deck_size: deck_size,
@ -479,7 +479,7 @@ pub struct BorrowedGameView<'a> {
pub player: Player, pub player: Player,
pub hand_size: usize, pub hand_size: usize,
// the cards of the other players, as well as the information they have // the cards of the other players, as well as the information they have
pub other_hands: HashMap<Player, &'a Cards>, pub other_hands: FnvHashMap<Player, &'a Cards>,
// board state // board state
pub board: &'a BoardState, pub board: &'a BoardState,
} }
@ -506,7 +506,7 @@ pub struct OwnedGameView {
pub player: Player, pub player: Player,
pub hand_size: usize, pub hand_size: usize,
// the cards of the other players, as well as the information they have // the cards of the other players, as well as the information they have
pub other_hands: HashMap<Player, Cards>, pub other_hands: FnvHashMap<Player, Cards>,
// board state // board state
pub board: BoardState, pub board: BoardState,
} }
@ -515,7 +515,7 @@ impl OwnedGameView {
let other_hands = borrowed_view.other_hands.iter() let other_hands = borrowed_view.other_hands.iter()
.map(|(&other_player, &player_state)| { .map(|(&other_player, &player_state)| {
(other_player, player_state.clone()) (other_player, player_state.clone())
}).collect::<HashMap<_, _>>(); }).collect::<FnvHashMap<_, _>>();
OwnedGameView { OwnedGameView {
player: borrowed_view.player.clone(), player: borrowed_view.player.clone(),
@ -544,7 +544,7 @@ impl GameView for OwnedGameView {
// complete game state (known to nobody!) // complete game state (known to nobody!)
#[derive(Debug)] #[derive(Debug)]
pub struct GameState { pub struct GameState {
pub hands: HashMap<Player, Cards>, pub hands: FnvHashMap<Player, Cards>,
pub board: BoardState, pub board: BoardState,
pub deck: Cards, pub deck: Cards,
} }
@ -582,7 +582,7 @@ impl GameState {
deck.pop().unwrap() deck.pop().unwrap()
}).collect::<Vec<_>>(); }).collect::<Vec<_>>();
(player, hand) (player, hand)
}).collect::<HashMap<_, _>>(); }).collect::<FnvHashMap<_, _>>();
GameState { GameState {
hands: hands, hands: hands,
@ -605,7 +605,7 @@ impl GameState {
// get the game state view of a particular player // get the game state view of a particular player
pub fn get_view(&self, player: Player) -> BorrowedGameView { 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 { for (&other_player, hand) in &self.hands {
if player != other_player { if player != other_player {
other_hands.insert(other_player, hand); other_hands.insert(other_player, hand);

View file

@ -3,6 +3,7 @@ extern crate getopts;
extern crate log; extern crate log;
extern crate rand; extern crate rand;
extern crate crossbeam; extern crate crossbeam;
extern crate fnv;
mod helpers; mod helpers;
mod game; mod game;

View file

@ -1,5 +1,5 @@
use rand::{self, Rng, SeedableRng}; use rand::{self, Rng, SeedableRng};
use std::collections::HashMap; use fnv::FnvHashMap;
use std::fmt; use std::fmt;
use crossbeam; use crossbeam;
@ -25,17 +25,15 @@ fn new_deck(seed: u32) -> Cards {
pub fn simulate_once( pub fn simulate_once(
opts: &GameOptions, opts: &GameOptions,
game_strategy: Box<GameStrategy>, game_strategy: Box<GameStrategy>,
seed_opt: Option<u32>, seed: u32,
) -> GameState { ) -> GameState {
let seed = seed_opt.unwrap_or(rand::thread_rng().next_u32());
let deck = new_deck(seed); let deck = new_deck(seed);
let mut game = GameState::new(opts, deck); let mut game = GameState::new(opts, deck);
let mut strategies = game.get_players().map(|player| { let mut strategies = game.get_players().map(|player| {
(player, game_strategy.initialize(player, &game.get_view(player))) (player, game_strategy.initialize(player, &game.get_view(player)))
}).collect::<HashMap<Player, Box<PlayerStrategy>>>(); }).collect::<FnvHashMap<Player, Box<PlayerStrategy>>>();
while !game.is_over() { while !game.is_over() {
let player = game.board.player; let player = game.board.player;
@ -69,14 +67,14 @@ pub fn simulate_once(
#[derive(Debug)] #[derive(Debug)]
struct Histogram { struct Histogram {
pub hist: HashMap<Score, u32>, pub hist: FnvHashMap<Score, u32>,
pub sum: Score, pub sum: Score,
pub total_count: u32, pub total_count: u32,
} }
impl Histogram { impl Histogram {
pub fn new() -> Histogram { pub fn new() -> Histogram {
Histogram { Histogram {
hist: HashMap::new(), hist: FnvHashMap::default(),
sum: 0, sum: 0,
total_count: 0, total_count: 0,
} }
@ -127,7 +125,7 @@ pub fn simulate<T: ?Sized>(
progress_info: Option<u32>, progress_info: Option<u32>,
) where T: GameStrategyConfig + Sync { ) 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; let strat_config_ref = &strat_config;
crossbeam::scope(|scope| { crossbeam::scope(|scope| {
@ -154,7 +152,7 @@ pub fn simulate<T: ?Sized>(
); );
} }
} }
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(); let score = game.score();
lives_histogram.insert(game.board.lives_remaining); lives_histogram.insert(game.board.lives_remaining);
score_histogram.insert(score); score_histogram.insert(score);

View file

@ -1,6 +1,6 @@
use std::rc::Rc; use std::rc::Rc;
use std::cell::{RefCell}; use std::cell::{RefCell};
use std::collections::{HashMap, HashSet}; use fnv::{FnvHashMap, FnvHashSet};
use strategy::*; use strategy::*;
use game::*; use game::*;
@ -31,13 +31,13 @@ impl GameStrategyConfig for CheatingStrategyConfig {
} }
pub struct CheatingStrategy { pub struct CheatingStrategy {
player_hands_cheat: Rc<RefCell<HashMap<Player, Cards>>>, player_hands_cheat: Rc<RefCell<FnvHashMap<Player, Cards>>>,
} }
impl CheatingStrategy { impl CheatingStrategy {
pub fn new() -> CheatingStrategy { pub fn new() -> CheatingStrategy {
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 { pub struct CheatingPlayerStrategy {
player_hands_cheat: Rc<RefCell<HashMap<Player, Cards>>>, player_hands_cheat: Rc<RefCell<FnvHashMap<Player, Cards>>>,
me: Player, me: Player,
} }
impl CheatingPlayerStrategy { impl CheatingPlayerStrategy {
@ -120,7 +120,7 @@ impl CheatingPlayerStrategy {
} }
fn find_useless_card(&self, view: &BorrowedGameView, hand: &Cards) -> Option<usize> { fn find_useless_card(&self, view: &BorrowedGameView, hand: &Cards) -> Option<usize> {
let mut set: HashSet<Card> = HashSet::new(); let mut set: FnvHashSet<Card> = FnvHashSet::default();
for (i, card) in hand.iter().enumerate() { for (i, card) in hand.iter().enumerate() {
if view.board.is_dead(card) { if view.board.is_dead(card) {

View file

@ -1,4 +1,4 @@
use std::collections::{HashMap, HashSet}; use fnv::{FnvHashMap, FnvHashSet};
use std::cmp::Ordering; use std::cmp::Ordering;
use strategy::*; use strategy::*;
@ -185,14 +185,14 @@ impl Question for AdditiveComboQuestion {
struct CardPossibilityPartition { struct CardPossibilityPartition {
index: usize, index: usize,
n_partitions: u32, n_partitions: u32,
partition: HashMap<Card, u32>, partition: FnvHashMap<Card, u32>,
} }
impl CardPossibilityPartition { impl CardPossibilityPartition {
fn new( fn new(
index: usize, max_n_partitions: u32, card_table: &CardPossibilityTable, view: &OwnedGameView index: usize, max_n_partitions: u32, card_table: &CardPossibilityTable, view: &OwnedGameView
) -> CardPossibilityPartition { ) -> CardPossibilityPartition {
let mut cur_block = 0; let mut cur_block = 0;
let mut partition = HashMap::new(); let mut partition = FnvHashMap::default();
let mut n_partitions = 0; let mut n_partitions = 0;
let has_dead = card_table.probability_is_dead(&view.board) != 0.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| { view.board.get_players().map(|player| {
let hand_info = HandInfo::new(view.board.hand_size); let hand_info = HandInfo::new(view.board.hand_size);
(player, hand_info) (player, hand_info)
}).collect::<HashMap<_,_>>(); }).collect::<FnvHashMap<_,_>>();
Box::new(InformationPlayerStrategy { Box::new(InformationPlayerStrategy {
me: player, me: player,
@ -301,7 +301,7 @@ impl GameStrategy for InformationStrategy {
pub struct InformationPlayerStrategy { pub struct InformationPlayerStrategy {
me: Player, me: Player,
public_info: HashMap<Player, HandInfo<CardPossibilityTable>>, public_info: FnvHashMap<Player, HandInfo<CardPossibilityTable>>,
public_counts: CardCounts, // what any newly drawn card should be public_counts: CardCounts, // what any newly drawn card should be
last_view: OwnedGameView, // the view on the previous turn last_view: OwnedGameView, // the view on the previous turn
} }
@ -550,8 +550,8 @@ impl InformationPlayerStrategy {
} }
fn find_useless_cards(&self, view: &OwnedGameView, hand: &HandInfo<CardPossibilityTable>) -> Vec<usize> { fn find_useless_cards(&self, view: &OwnedGameView, hand: &HandInfo<CardPossibilityTable>) -> Vec<usize> {
let mut useless: HashSet<usize> = HashSet::new(); let mut useless: FnvHashSet<usize> = FnvHashSet::default();
let mut seen: HashMap<Card, usize> = HashMap::new(); let mut seen: FnvHashMap<Card, usize> = FnvHashMap::default();
for (i, card_table) in hand.iter().enumerate() { for (i, card_table) in hand.iter().enumerate() {
if card_table.probability_is_dead(view.get_board()) == 1.0 { 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() (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>) -> Hinted { fn get_best_hint_of_options(&self, hint_player: Player, hint_option_set: FnvHashSet<Hinted>) -> Hinted {
let view = &self.last_view; let view = &self.last_view;
// using hint goodness barely helps // using hint goodness barely helps
@ -864,7 +864,7 @@ impl InformationPlayerStrategy {
2 => { 2 => {
// NOTE: this doesn't do that much better than just hinting // NOTE: this doesn't do that much better than just hinting
// the first thing that doesn't match the hint_card // 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 { for card in hand {
if card.color != hint_card.color { if card.color != hint_card.color {
hint_option_set.insert(Hinted::Color(card.color)); hint_option_set.insert(Hinted::Color(card.color));
@ -889,7 +889,7 @@ impl InformationPlayerStrategy {
} }
2 => { 2 => {
// Any value hint for a card other than the first // 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 { for card in hand {
if card.value != hint_card.value { if card.value != hint_card.value {
hint_option_set.insert(Hinted::Value(card.value)); hint_option_set.insert(Hinted::Value(card.value));
@ -899,7 +899,7 @@ impl InformationPlayerStrategy {
} }
3 => { 3 => {
// Any color hint for a card other than the first // 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 { for card in hand {
if card.color != hint_card.color { if card.color != hint_card.color {
hint_option_set.insert(Hinted::Color(card.color)); hint_option_set.insert(Hinted::Color(card.color));