minor cleanups

This commit is contained in:
Jeff Wu 2016-04-04 00:26:42 -07:00
parent f2de390e0e
commit cab576f883
8 changed files with 71 additions and 70 deletions

View File

@ -38,7 +38,7 @@ cargo run -- -s 222 -p 5 -g info -l debug | less
## Strategies
To write a strategy, you simply [implement a few traits](src/simulator.rs).
To write a strategy, you simply [implement a few traits](src/strategy.rs).
The framework is designed to take advantage of Rust's ownership system
so that you *can't cheat*, without using stuff like `Cell` or `Arc` or `Mutex`.

View File

@ -23,7 +23,7 @@ pub fn get_count_for_value(value: Value) -> u32 {
}
}
#[derive(Debug,Clone,PartialEq,Eq,Hash,Ord,PartialOrd)]
#[derive(Clone,PartialEq,Eq,Hash,Ord,PartialOrd)]
pub struct Card {
pub color: Color,
pub value: Value,
@ -38,6 +38,11 @@ impl fmt::Display for Card {
write!(f, "{}{}", self.color, self.value)
}
}
impl fmt::Debug for Card {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{}", self.color, self.value)
}
}
#[derive(Debug,Clone)]
pub struct CardCounts {
@ -217,11 +222,12 @@ pub enum TurnResult {
// represents a turn taken in the game
#[derive(Debug,Clone)]
pub struct Turn {
pub struct TurnRecord {
pub player: Player,
pub choice: TurnChoice,
pub result: TurnResult,
}
pub type TurnHistory = Vec<TurnRecord>;
// represents possible settings for the game
pub struct GameOptions {
@ -248,6 +254,7 @@ pub struct BoardState {
// which turn is it?
pub turn: u32,
pub turn_history: TurnHistory,
// // whose turn is it?
pub player: Player,
pub hand_size: u32,
@ -257,7 +264,6 @@ pub struct BoardState {
pub allow_empty_hints: bool,
pub lives_total: u32,
pub lives_remaining: u32,
pub turn_history: Vec<Turn>,
// only relevant when deck runs out
pub deckless_turns_remaining: u32,
}
@ -628,7 +634,7 @@ impl GameState {
}
}
pub fn process_choice(&mut self, choice: TurnChoice) -> Turn {
pub fn process_choice(&mut self, choice: TurnChoice) -> TurnRecord {
let turn_result = {
match choice {
TurnChoice::Hint(ref hint) => {
@ -649,8 +655,9 @@ impl GameState {
hand.iter().map(|card| { card.value == value }).collect::<Vec<_>>()
}
};
if (!self.board.allow_empty_hints) && (results.iter().all(|matched| !matched)) {
panic!("Tried hinting an empty hint");
if !self.board.allow_empty_hints {
assert!(results.iter().any(|matched| *matched),
"Tried hinting an empty hint");
}
TurnResult::Hint(results)
@ -693,12 +700,12 @@ impl GameState {
}
}
};
let turn = Turn {
let turn_record = TurnRecord {
player: self.board.player.clone(),
result: turn_result,
choice: choice,
};
self.board.turn_history.push(turn.clone());
self.board.turn_history.push(turn_record.clone());
self.replenish_hand();
@ -712,6 +719,6 @@ impl GameState {
};
assert_eq!((self.board.turn - 1) % self.board.num_players, self.board.player);
turn
turn_record
}
}

View File

@ -7,6 +7,7 @@ extern crate crossbeam;
mod helpers;
mod game;
mod simulator;
mod strategy;
mod strategies {
pub mod examples;
pub mod cheating;
@ -122,20 +123,20 @@ fn main() {
};
let strategy_str : &str = &matches.opt_str("g").unwrap_or("cheat".to_string());
let strategy_config : Box<simulator::GameStrategyConfig + Sync> = match strategy_str {
let strategy_config : Box<strategy::GameStrategyConfig + Sync> = match strategy_str {
"random" => {
Box::new(strategies::examples::RandomStrategyConfig {
hint_probability: 0.4,
play_probability: 0.2,
}) as Box<simulator::GameStrategyConfig + Sync>
}) as Box<strategy::GameStrategyConfig + Sync>
},
"cheat" => {
Box::new(strategies::cheating::CheatingStrategyConfig::new())
as Box<simulator::GameStrategyConfig + Sync>
as Box<strategy::GameStrategyConfig + Sync>
},
"info" => {
Box::new(strategies::information::InformationStrategyConfig::new())
as Box<simulator::GameStrategyConfig + Sync>
as Box<strategy::GameStrategyConfig + Sync>
},
_ => {
print_usage(&program, opts);

View File

@ -4,46 +4,21 @@ use std::fmt;
use crossbeam;
use game::*;
// Traits to implement for any valid Hanabi strategy
// Represents the strategy of a given player
pub trait PlayerStrategy {
// A function to decide what to do on the player's turn.
// Given a BorrowedGameView, outputs their choice.
fn decide(&mut self, &BorrowedGameView) -> TurnChoice;
// A function to update internal state after other players' turns.
// Given what happened last turn, and the new state.
fn update(&mut self, &Turn, &BorrowedGameView);
}
// Represents the overall strategy for a game
// Shouldn't do much, except store configuration parameters and
// possibility initialize some shared randomness between players
pub trait GameStrategy {
fn initialize(&self, Player, &BorrowedGameView) -> Box<PlayerStrategy>;
}
// Represents configuration for a strategy.
// Acts as a factory for game strategies, so we can play many rounds
pub trait GameStrategyConfig {
fn initialize(&self, &GameOptions) -> Box<GameStrategy>;
}
use strategy::*;
fn new_deck(seed: u32) -> Cards {
let mut deck: Cards = Cards::new();
for &color in COLORS.iter() {
for &value in VALUES.iter() {
let count = get_count_for_value(value);
for _ in 0..count {
for _ in 0..get_count_for_value(value) {
deck.push(Card::new(color, value));
}
}
};
rand::ChaChaRng::from_seed(&[seed]).shuffle(&mut deck[..]);
trace!("Created deck: {:?}", deck);
debug!("Deck: {:?}", deck);
deck
}
@ -59,7 +34,7 @@ pub fn simulate_once(
let mut game = GameState::new(opts, deck);
let mut strategies = game.get_players().map(|player| {
(player, game_strategy.initialize(player.clone(), &game.get_view(player)))
(player, game_strategy.initialize(player, &game.get_view(player)))
}).collect::<HashMap<Player, Box<PlayerStrategy>>>();
while !game.is_over() {

View File

@ -2,7 +2,7 @@ use std::rc::Rc;
use std::cell::{RefCell};
use std::collections::{HashMap, HashSet};
use simulator::*;
use strategy::*;
use game::*;
// strategy that explicitly cheats by using Rc/RefCell
@ -208,6 +208,6 @@ impl PlayerStrategy for CheatingPlayerStrategy {
}
TurnChoice::Discard(index)
}
fn update(&mut self, _: &Turn, _: &BorrowedGameView) {
fn update(&mut self, _: &TurnRecord, _: &BorrowedGameView) {
}
}

View File

@ -1,4 +1,4 @@
use simulator::*;
use strategy::*;
use game::*;
use rand::{self, Rng};
@ -66,6 +66,6 @@ impl PlayerStrategy for RandomStrategyPlayer {
TurnChoice::Discard(0)
}
}
fn update(&mut self, _: &Turn, _: &BorrowedGameView) {
fn update(&mut self, _: &TurnRecord, _: &BorrowedGameView) {
}
}

View File

@ -1,12 +1,14 @@
use std::collections::{HashMap, HashSet};
use std::cmp::Ordering;
use simulator::*;
use strategy::*;
use game::*;
use helpers::*;
// TODO: use random extra information - i.e. when casting up and down,
// we sometimes have 2 choices of value to choose
// TODO: guess very aggressively at very end of game (first, see whether
// situation ever occurs)
#[derive(Debug,Clone)]
struct ModulusInformation {
@ -39,8 +41,7 @@ impl ModulusInformation {
self.modulus = self.modulus / modulus;
let value = self.value / self.modulus;
self.value = self.value - value * self.modulus;
trace!("orig value {}, orig modulus {}, self.value {}, self.modulus {}, value {}, modulus {}",
original_value, original_modulus, self.value, self.modulus, value, modulus);
assert!(original_modulus == modulus * self.modulus);
assert!(original_value == value * self.modulus + self.value);
Self::new(modulus, value)
}
@ -303,8 +304,6 @@ impl InformationPlayerStrategy {
return questions;
}
}
} else {
debug!("Something known playable");
}
let mut ask_partition = augmented_hand_info.iter()
@ -354,10 +353,8 @@ impl InformationPlayerStrategy {
assert!(player != &self.me);
let hand_info = self.get_player_public_info(player);
let questions = Self::get_questions(total_info, view, hand_info);
trace!("Getting hint for player {}, questions {:?}", player, questions.len());
let mut answer = Self::answer_questions(&questions, view.get_hand(player), view);
answer.cast_up(total_info);
trace!("Resulting answer {:?}", answer);
answer
}
@ -379,11 +376,8 @@ impl InformationPlayerStrategy {
let hand_info = self.get_my_public_info();
Self::get_questions(hint.modulus, view, hand_info)
};
trace!("{}: Questions {:?}", self.me, questions.len());
let product = questions.iter().fold(1, |a, ref b| a * b.info_amount());
trace!("{}: Product {}, hint: {:?}", self.me, product, hint);
hint.cast_down(product);
trace!("{}: Inferred for myself {:?}", self.me, hint);
let me = self.me.clone();
let mut hand_info = self.take_public_info(&me);
@ -404,14 +398,12 @@ impl InformationPlayerStrategy {
let view = &self.last_view;
view.board.get_players()
};
trace!("{}: inferring for myself, starting with {:?}", self.me, hint);
for player in players {
if (player != hinter) && (player != self.me) {
{
let view = &self.last_view;
let hint_info = self.get_hint_info_for_player(&player, hint.modulus, view);
hint.subtract(&hint_info);
trace!("{}: subtracted for {}, now {:?}", self.me, player, hint);
}
// *take* instead of borrowing mutably, because of borrow rules...
@ -856,20 +848,20 @@ impl PlayerStrategy for InformationPlayerStrategy {
TurnChoice::Discard(index)
}
fn update(&mut self, turn: &Turn, view: &BorrowedGameView) {
match turn.choice {
fn update(&mut self, turn_record: &TurnRecord, view: &BorrowedGameView) {
match turn_record.choice {
TurnChoice::Hint(ref hint) => {
if let &TurnResult::Hint(ref matches) = &turn.result {
if let &TurnResult::Hint(ref matches) = &turn_record.result {
self.infer_from_hint(hint, matches);
self.update_public_info_for_hint(hint, matches);
} else {
panic!("Got turn choice {:?}, but turn result {:?}",
turn.choice, turn.result);
turn_record.choice, turn_record.result);
}
}
TurnChoice::Discard(index) => {
let known_useless_indices = self.find_useless_cards(
&self.last_view, &self.get_player_public_info(&turn.player)
&self.last_view, &self.get_player_public_info(&turn_record.player)
);
if known_useless_indices.len() > 1 {
@ -881,19 +873,19 @@ impl PlayerStrategy for InformationPlayerStrategy {
));
}
if let &TurnResult::Discard(ref card) = &turn.result {
self.update_public_info_for_discard_or_play(view, &turn.player, index, card);
if let &TurnResult::Discard(ref card) = &turn_record.result {
self.update_public_info_for_discard_or_play(view, &turn_record.player, index, card);
} else {
panic!("Got turn choice {:?}, but turn result {:?}",
turn.choice, turn.result);
turn_record.choice, turn_record.result);
}
}
TurnChoice::Play(index) => {
if let &TurnResult::Play(ref card, _) = &turn.result {
self.update_public_info_for_discard_or_play(view, &turn.player, index, card);
if let &TurnResult::Play(ref card, _) = &turn_record.result {
self.update_public_info_for_discard_or_play(view, &turn_record.player, index, card);
} else {
panic!("Got turn choice {:?}, but turn result {:?}",
turn.choice, turn.result);
turn_record.choice, turn_record.result);
}
}
}

26
src/strategy.rs Normal file
View File

@ -0,0 +1,26 @@
use game::*;
// Traits to implement for any valid Hanabi strategy
// Represents the strategy of a given player
pub trait PlayerStrategy {
// A function to decide what to do on the player's turn.
// Given a BorrowedGameView, outputs their choice.
fn decide(&mut self, &BorrowedGameView) -> TurnChoice;
// A function to update internal state after other players' turns.
// Given what happened last turn, and the new state.
fn update(&mut self, &TurnRecord, &BorrowedGameView);
}
// Represents the overall strategy for a game
// Shouldn't do much, except store configuration parameters and
// possibility initialize some shared randomness between players
pub trait GameStrategy {
fn initialize(&self, Player, &BorrowedGameView) -> Box<PlayerStrategy>;
}
// Represents configuration for a strategy.
// Acts as a factory for game strategies, so we can play many rounds
pub trait GameStrategyConfig {
fn initialize(&self, &GameOptions) -> Box<GameStrategy>;
}