no empty hints, by default
This commit is contained in:
parent
964602d03d
commit
e36700d93f
6 changed files with 95 additions and 65 deletions
79
src/game.rs
79
src/game.rs
|
@ -19,7 +19,7 @@ pub type Value = u32;
|
||||||
pub const VALUES : [Value; 5] = [1, 2, 3, 4, 5];
|
pub const VALUES : [Value; 5] = [1, 2, 3, 4, 5];
|
||||||
pub const FINAL_VALUE : Value = 5;
|
pub const FINAL_VALUE : Value = 5;
|
||||||
|
|
||||||
pub fn get_count_for_value(value: &Value) -> usize {
|
pub fn get_count_for_value(value: &Value) -> u32 {
|
||||||
match *value {
|
match *value {
|
||||||
1 => 3,
|
1 => 3,
|
||||||
2 | 3 | 4 => 2,
|
2 | 3 | 4 => 2,
|
||||||
|
@ -49,10 +49,10 @@ impl fmt::Display for Card {
|
||||||
pub type Cards = Vec<Card>;
|
pub type Cards = Vec<Card>;
|
||||||
pub type CardsInfo = Vec<CardInfo>;
|
pub type CardsInfo = Vec<CardInfo>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug,Clone)]
|
||||||
pub struct Firework {
|
pub struct Firework {
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
top: Value,
|
pub top: Value,
|
||||||
}
|
}
|
||||||
impl Firework {
|
impl Firework {
|
||||||
fn new(color: Color) -> Firework {
|
fn new(color: Color) -> Firework {
|
||||||
|
@ -66,8 +66,8 @@ impl Firework {
|
||||||
if self.complete() { None } else { Some(self.top + 1) }
|
if self.complete() { None } else { Some(self.top + 1) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn score(&self) -> usize {
|
fn score(&self) -> Score {
|
||||||
(self.top as usize)
|
self.top
|
||||||
}
|
}
|
||||||
|
|
||||||
fn complete(&self) -> bool {
|
fn complete(&self) -> bool {
|
||||||
|
@ -83,7 +83,6 @@ impl Firework {
|
||||||
Some(card.value) == self.desired_value(),
|
Some(card.value) == self.desired_value(),
|
||||||
"Attempted to place card of wrong value on firework!"
|
"Attempted to place card of wrong value on firework!"
|
||||||
);
|
);
|
||||||
|
|
||||||
self.top = card.value;
|
self.top = card.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,7 +99,7 @@ impl fmt::Display for Firework {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Discard {
|
pub struct Discard {
|
||||||
pub cards: Cards,
|
pub cards: Cards,
|
||||||
counts: HashMap<Color, HashMap<Value, usize>>,
|
counts: HashMap<Color, HashMap<Value, u32>>,
|
||||||
}
|
}
|
||||||
impl Discard {
|
impl Discard {
|
||||||
fn new() -> Discard {
|
fn new() -> Discard {
|
||||||
|
@ -118,7 +117,7 @@ impl Discard {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_count(&self, card: &Card) -> usize {
|
fn get_count(&self, card: &Card) -> u32 {
|
||||||
let color_count = self.counts.get(card.color).unwrap();
|
let color_count = self.counts.get(card.color).unwrap();
|
||||||
color_count.get(&card.value).unwrap().clone()
|
color_count.get(&card.value).unwrap().clone()
|
||||||
}
|
}
|
||||||
|
@ -127,7 +126,7 @@ impl Discard {
|
||||||
self.remaining(card) == 0
|
self.remaining(card) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remaining(&self, card: &Card) -> usize {
|
fn remaining(&self, card: &Card) -> u32 {
|
||||||
let count = self.get_count(&card);
|
let count = self.get_count(&card);
|
||||||
get_count_for_value(&card.value) - count
|
get_count_for_value(&card.value) - count
|
||||||
}
|
}
|
||||||
|
@ -195,13 +194,13 @@ pub enum TurnChoice {
|
||||||
// represents what happened in a turn
|
// represents what happened in a turn
|
||||||
#[derive(Debug,Clone)]
|
#[derive(Debug,Clone)]
|
||||||
pub enum TurnResult {
|
pub enum TurnResult {
|
||||||
Hint(Vec<usize>),
|
Hint(Vec<usize>), // indices revealed
|
||||||
Discard(Card),
|
Discard(Card), // card discarded
|
||||||
Play(Card, bool),
|
Play(Card, bool), // card played, whether it succeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
// represents a turn taken in the game
|
// represents a turn taken in the game
|
||||||
#[derive(Debug)]
|
#[derive(Debug,Clone)]
|
||||||
pub struct Turn {
|
pub struct Turn {
|
||||||
pub player: Player,
|
pub player: Player,
|
||||||
pub choice: TurnChoice,
|
pub choice: TurnChoice,
|
||||||
|
@ -216,8 +215,8 @@ pub struct GameOptions {
|
||||||
pub num_hints: u32,
|
pub num_hints: u32,
|
||||||
// when hits 0, you lose
|
// when hits 0, you lose
|
||||||
pub num_lives: u32,
|
pub num_lives: u32,
|
||||||
// TODO:
|
// whether to allow hints that reveal no cards
|
||||||
// pub allow_empty_hints: bool,
|
pub allow_empty_hints: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// The state of a given player: all other players may see this
|
// The state of a given player: all other players may see this
|
||||||
|
@ -269,22 +268,18 @@ impl PlayerState {
|
||||||
&Hinted::Color(ref color) => {
|
&Hinted::Color(ref color) => {
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
for card in &self.hand {
|
for card in &self.hand {
|
||||||
self.info[i].color_info.mark(
|
let matches = card.color == *color;
|
||||||
color,
|
self.info[i].color_info.mark(color, matches);
|
||||||
card.color == *color
|
if matches { indices.push(i); }
|
||||||
);
|
|
||||||
indices.push(i);
|
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&Hinted::Value(ref value) => {
|
&Hinted::Value(ref value) => {
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
for card in &self.hand {
|
for card in &self.hand {
|
||||||
self.info[i].value_info.mark(
|
let matches = card.value == *value;
|
||||||
value,
|
self.info[i].value_info.mark(value, matches);
|
||||||
card.value == *value
|
if matches { indices.push(i); }
|
||||||
);
|
|
||||||
indices.push(i);
|
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -317,6 +312,7 @@ fn new_deck(seed: u32) -> Cards {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BoardState {
|
pub struct BoardState {
|
||||||
deck: Cards,
|
deck: Cards,
|
||||||
|
pub total_cards: u32,
|
||||||
pub discard: Discard,
|
pub discard: Discard,
|
||||||
pub fireworks: HashMap<Color, Firework>,
|
pub fireworks: HashMap<Color, Firework>,
|
||||||
|
|
||||||
|
@ -330,6 +326,7 @@ pub struct BoardState {
|
||||||
|
|
||||||
pub hints_total: u32,
|
pub hints_total: u32,
|
||||||
pub hints_remaining: u32,
|
pub hints_remaining: u32,
|
||||||
|
pub allow_empty_hints: bool,
|
||||||
pub lives_total: u32,
|
pub lives_total: u32,
|
||||||
pub lives_remaining: u32,
|
pub lives_remaining: u32,
|
||||||
pub turn_history: Vec<Turn>,
|
pub turn_history: Vec<Turn>,
|
||||||
|
@ -342,15 +339,19 @@ impl BoardState {
|
||||||
for color in COLORS.iter() {
|
for color in COLORS.iter() {
|
||||||
fireworks.insert(color, Firework::new(color));
|
fireworks.insert(color, Firework::new(color));
|
||||||
}
|
}
|
||||||
|
let deck = new_deck(seed);
|
||||||
|
let total_cards = deck.len() as u32;
|
||||||
|
|
||||||
BoardState {
|
BoardState {
|
||||||
deck: new_deck(seed),
|
deck: deck,
|
||||||
|
total_cards: total_cards,
|
||||||
fireworks: fireworks,
|
fireworks: fireworks,
|
||||||
discard: Discard::new(),
|
discard: Discard::new(),
|
||||||
num_players: opts.num_players,
|
num_players: opts.num_players,
|
||||||
hand_size: opts.hand_size,
|
hand_size: opts.hand_size,
|
||||||
player: 0,
|
player: 0,
|
||||||
turn: 1,
|
turn: 1,
|
||||||
|
allow_empty_hints: opts.allow_empty_hints,
|
||||||
hints_total: opts.num_hints,
|
hints_total: opts.num_hints,
|
||||||
hints_remaining: opts.num_hints,
|
hints_remaining: opts.num_hints,
|
||||||
lives_total: opts.num_lives,
|
lives_total: opts.num_lives,
|
||||||
|
@ -458,8 +459,8 @@ impl BoardState {
|
||||||
score as u32
|
score as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deck_size(&self) -> usize {
|
pub fn deck_size(&self) -> u32 {
|
||||||
self.deck.len()
|
self.deck.len() as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn player_to_left(&self, player: &Player) -> Player {
|
pub fn player_to_left(&self, player: &Player) -> Player {
|
||||||
|
@ -556,16 +557,17 @@ pub struct GameState {
|
||||||
}
|
}
|
||||||
impl fmt::Display for GameState {
|
impl fmt::Display for GameState {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
try!(f.write_str("==========================\n"));
|
try!(f.write_str("\n"));
|
||||||
|
try!(f.write_str("======\n"));
|
||||||
try!(f.write_str("Hands:\n"));
|
try!(f.write_str("Hands:\n"));
|
||||||
try!(f.write_str("==========================\n"));
|
try!(f.write_str("======\n"));
|
||||||
for player in self.board.get_players() {
|
for player in self.board.get_players() {
|
||||||
let state = &self.player_states.get(&player).unwrap();
|
let state = &self.player_states.get(&player).unwrap();
|
||||||
try!(f.write_str(&format!("player {} {}\n", player, state)));
|
try!(f.write_str(&format!("player {} {}\n", player, state)));
|
||||||
}
|
}
|
||||||
try!(f.write_str("==========================\n"));
|
try!(f.write_str("======\n"));
|
||||||
try!(f.write_str("Board:\n"));
|
try!(f.write_str("Board:\n"));
|
||||||
try!(f.write_str("==========================\n"));
|
try!(f.write_str("======\n"));
|
||||||
try!(f.write_str(&format!("{}", self.board)));
|
try!(f.write_str(&format!("{}", self.board)));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -599,7 +601,6 @@ impl GameState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_over(&self) -> bool {
|
pub fn is_over(&self) -> bool {
|
||||||
// TODO: add condition that fireworks cannot be further completed?
|
|
||||||
self.board.is_over()
|
self.board.is_over()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -640,8 +641,7 @@ impl GameState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_choice(&mut self, choice: TurnChoice) -> TurnResult {
|
pub fn process_choice(&mut self, choice: TurnChoice) -> Turn {
|
||||||
debug!("Player {}'s move", self.board.player);
|
|
||||||
let turn_result = {
|
let turn_result = {
|
||||||
match choice {
|
match choice {
|
||||||
TurnChoice::Hint(ref hint) => {
|
TurnChoice::Hint(ref hint) => {
|
||||||
|
@ -655,6 +655,9 @@ impl GameState {
|
||||||
|
|
||||||
let ref mut state = self.player_states.get_mut(&hint.player).unwrap();
|
let ref mut state = self.player_states.get_mut(&hint.player).unwrap();
|
||||||
let indices = state.reveal(&hint.hinted);
|
let indices = state.reveal(&hint.hinted);
|
||||||
|
if (!self.board.allow_empty_hints) && (indices.len() == 0) {
|
||||||
|
panic!("Tried hinting an empty hint");
|
||||||
|
}
|
||||||
TurnResult::Hint(indices)
|
TurnResult::Hint(indices)
|
||||||
}
|
}
|
||||||
TurnChoice::Discard(index) => {
|
TurnChoice::Discard(index) => {
|
||||||
|
@ -697,10 +700,10 @@ impl GameState {
|
||||||
};
|
};
|
||||||
let turn = Turn {
|
let turn = Turn {
|
||||||
player: self.board.player.clone(),
|
player: self.board.player.clone(),
|
||||||
result: turn_result.clone(),
|
result: turn_result,
|
||||||
choice: choice,
|
choice: choice,
|
||||||
};
|
};
|
||||||
self.board.turn_history.push(turn);
|
self.board.turn_history.push(turn.clone());
|
||||||
|
|
||||||
self.replenish_hand();
|
self.replenish_hand();
|
||||||
|
|
||||||
|
@ -714,6 +717,6 @@ impl GameState {
|
||||||
};
|
};
|
||||||
assert_eq!((self.board.turn - 1) % self.board.num_players, self.board.player);
|
assert_eq!((self.board.turn - 1) % self.board.num_players, self.board.player);
|
||||||
|
|
||||||
turn_result
|
turn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::hash::Hash;
|
||||||
|
|
||||||
use game::*;
|
use game::*;
|
||||||
|
|
||||||
// Represents information about possible values of type T
|
// Represents hinted information about possible values of type T
|
||||||
pub trait Info<T> where T: Hash + Eq + Clone {
|
pub trait Info<T> where T: Hash + Eq + Clone {
|
||||||
// get all a-priori possibilities
|
// get all a-priori possibilities
|
||||||
fn get_all_possibilities() -> Vec<T>;
|
fn get_all_possibilities() -> Vec<T>;
|
||||||
|
|
21
src/main.rs
21
src/main.rs
|
@ -43,6 +43,7 @@ fn main() {
|
||||||
opts.optopt("n", "ntrials", "Number of games to simulate", "NTRIALS");
|
opts.optopt("n", "ntrials", "Number of games to simulate", "NTRIALS");
|
||||||
opts.optopt("t", "nthreads", "Number of threads to use for simulation", "NTHREADS");
|
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 (can only be used with n=1)", "SEED");
|
||||||
|
opts.optopt("p", "nplayers", "Number of players", "NPLAYERS");
|
||||||
opts.optflag("h", "help", "Print this help menu");
|
opts.optflag("h", "help", "Print this help menu");
|
||||||
let matches = match opts.parse(&args[1..]) {
|
let matches = match opts.parse(&args[1..]) {
|
||||||
Ok(m) => { m }
|
Ok(m) => { m }
|
||||||
|
@ -65,7 +66,10 @@ fn main() {
|
||||||
"info" => { log::LogLevelFilter::Info }
|
"info" => { log::LogLevelFilter::Info }
|
||||||
"warn" => { log::LogLevelFilter::Warn }
|
"warn" => { log::LogLevelFilter::Warn }
|
||||||
"error" => { log::LogLevelFilter::Error }
|
"error" => { log::LogLevelFilter::Error }
|
||||||
_ => { panic!("Unexpected log level argument {}", log_level_str); }
|
_ => {
|
||||||
|
print_usage(&program, opts);
|
||||||
|
panic!("Unexpected log level argument {}", log_level_str);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
log::set_logger(|max_log_level| {
|
log::set_logger(|max_log_level| {
|
||||||
|
@ -79,11 +83,22 @@ fn main() {
|
||||||
|
|
||||||
let n_threads = u32::from_str(&matches.opt_str("t").unwrap_or("1".to_string())).unwrap();
|
let n_threads = u32::from_str(&matches.opt_str("t").unwrap_or("1".to_string())).unwrap();
|
||||||
|
|
||||||
|
let n_players = u32::from_str(&matches.opt_str("p").unwrap_or("4".to_string())).unwrap();
|
||||||
|
let hand_size = match n_players {
|
||||||
|
2 => 5,
|
||||||
|
3 => 5,
|
||||||
|
4 => 4,
|
||||||
|
5 => 4,
|
||||||
|
_ => { panic!("There should be 2 to 5 players, not {}", n_players); }
|
||||||
|
};
|
||||||
|
|
||||||
let opts = game::GameOptions {
|
let opts = game::GameOptions {
|
||||||
num_players: 5,
|
num_players: n_players,
|
||||||
hand_size: 4,
|
hand_size: hand_size,
|
||||||
num_hints: 8,
|
num_hints: 8,
|
||||||
num_lives: 3,
|
num_lives: 3,
|
||||||
|
// hanabi rules are a bit ambiguous about whether you can give hints that match 0 cards
|
||||||
|
allow_empty_hints: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: make this configurable
|
// TODO: make this configurable
|
||||||
|
|
|
@ -40,40 +40,41 @@ pub fn simulate_once(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("Initial state:\n{}", game);
|
|
||||||
|
|
||||||
while !game.is_over() {
|
while !game.is_over() {
|
||||||
debug!("Turn {}", game.board.turn);
|
|
||||||
let player = game.board.player;
|
let player = game.board.player;
|
||||||
|
|
||||||
|
debug!("");
|
||||||
|
debug!("=======================================================");
|
||||||
|
debug!("Turn {}, Player {} to go", game.board.turn, player);
|
||||||
|
debug!("=======================================================");
|
||||||
|
debug!("{}", game);
|
||||||
|
|
||||||
|
|
||||||
let choice = {
|
let choice = {
|
||||||
let mut strategy = strategies.get_mut(&player).unwrap();
|
let mut strategy = strategies.get_mut(&player).unwrap();
|
||||||
strategy.decide(&game.get_view(player))
|
strategy.decide(&game.get_view(player))
|
||||||
};
|
};
|
||||||
|
|
||||||
let turn_result = game.process_choice(choice.clone());
|
let turn = game.process_choice(choice);
|
||||||
|
|
||||||
let turn = Turn {
|
|
||||||
player: player,
|
|
||||||
choice: choice,
|
|
||||||
result: turn_result,
|
|
||||||
};
|
|
||||||
|
|
||||||
for player in game.get_players() {
|
for player in game.get_players() {
|
||||||
let mut strategy = strategies.get_mut(&player).unwrap();
|
let mut strategy = strategies.get_mut(&player).unwrap();
|
||||||
strategy.update(&turn, &game.get_view(player));
|
strategy.update(&turn, &game.get_view(player));
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("State:\n{}", game);
|
|
||||||
}
|
}
|
||||||
|
debug!("");
|
||||||
|
debug!("=======================================================");
|
||||||
|
debug!("Final state:\n{}", game);
|
||||||
let score = game.score();
|
let score = game.score();
|
||||||
debug!("SCORED: {:?}", score);
|
debug!("SCORED: {:?}", score);
|
||||||
score
|
score
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Histogram {
|
struct Histogram {
|
||||||
pub hist: HashMap<Score, usize>,
|
pub hist: HashMap<Score, u32>,
|
||||||
pub sum: Score,
|
pub sum: Score,
|
||||||
pub total_count: usize,
|
pub total_count: u32,
|
||||||
}
|
}
|
||||||
impl Histogram {
|
impl Histogram {
|
||||||
pub fn new() -> Histogram {
|
pub fn new() -> Histogram {
|
||||||
|
@ -83,7 +84,7 @@ impl Histogram {
|
||||||
total_count: 0,
|
total_count: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn insert_many(&mut self, val: Score, count: usize) {
|
fn insert_many(&mut self, val: Score, count: u32) {
|
||||||
let new_count = self.get_count(&val) + count;
|
let new_count = self.get_count(&val) + count;
|
||||||
self.hist.insert(val, new_count);
|
self.hist.insert(val, new_count);
|
||||||
self.sum += val * (count as u32);
|
self.sum += val * (count as u32);
|
||||||
|
@ -92,7 +93,7 @@ impl Histogram {
|
||||||
pub fn insert(&mut self, val: Score) {
|
pub fn insert(&mut self, val: Score) {
|
||||||
self.insert_many(val, 1);
|
self.insert_many(val, 1);
|
||||||
}
|
}
|
||||||
pub fn get_count(&self, val: &Score) -> usize {
|
pub fn get_count(&self, val: &Score) -> u32 {
|
||||||
*self.hist.get(&val).unwrap_or(&0)
|
*self.hist.get(&val).unwrap_or(&0)
|
||||||
}
|
}
|
||||||
pub fn average(&self) -> f32 {
|
pub fn average(&self) -> f32 {
|
||||||
|
|
|
@ -71,14 +71,16 @@ impl CheatingPlayerStrategy {
|
||||||
|
|
||||||
// give a throwaway hint - we only do this when we have nothing to do
|
// give a throwaway hint - we only do this when we have nothing to do
|
||||||
fn throwaway_hint(&self, view: &GameStateView) -> TurnChoice {
|
fn throwaway_hint(&self, view: &GameStateView) -> TurnChoice {
|
||||||
|
let hint_player = view.board.player_to_left(&self.me);
|
||||||
|
let hint_card = &view.get_hand(&hint_player).first().unwrap();
|
||||||
TurnChoice::Hint(Hint {
|
TurnChoice::Hint(Hint {
|
||||||
player: view.board.player_to_left(&self.me),
|
player: hint_player,
|
||||||
hinted: Hinted::Value(1)
|
hinted: Hinted::Value(hint_card.value)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// given a hand of cards, represents how badly it will need to play things
|
// given a hand of cards, represents how badly it will need to play things
|
||||||
fn hand_play_value(&self, view: &GameStateView, hand: &Cards/*, all_viewable: HashMap<Color, <Value, usize>> */) -> u32 {
|
fn hand_play_value(&self, view: &GameStateView, hand: &Cards/*, all_viewable: HashMap<Color, <Value, u32>> */) -> u32 {
|
||||||
// dead = 0 points
|
// dead = 0 points
|
||||||
// indispensible = 5 + (5 - value) points
|
// indispensible = 5 + (5 - value) points
|
||||||
// playable, not in another hand = 2 point
|
// playable, not in another hand = 2 point
|
||||||
|
@ -188,8 +190,15 @@ impl PlayerStrategy for CheatingPlayerStrategy {
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
TurnChoice::Play(index)
|
TurnChoice::Play(index)
|
||||||
} else {
|
} else {
|
||||||
// 50 total, 25 to play, 20 in hand
|
// discard threshold is how many cards we're willing to discard
|
||||||
if view.board.discard.cards.len() < 6 {
|
// such that if we only played,
|
||||||
|
// 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 {
|
||||||
// if anything is totally useless, discard it
|
// if anything is totally useless, discard it
|
||||||
if let Some(i) = self.find_useless_card(view, my_cards) {
|
if let Some(i) = self.find_useless_card(view, my_cards) {
|
||||||
return TurnChoice::Discard(i);
|
return TurnChoice::Discard(i);
|
||||||
|
|
|
@ -44,16 +44,18 @@ impl PlayerStrategy for RandomStrategyPlayer {
|
||||||
let p = rand::random::<f64>();
|
let p = rand::random::<f64>();
|
||||||
if p < self.hint_probability {
|
if p < self.hint_probability {
|
||||||
if view.board.hints_remaining > 0 {
|
if view.board.hints_remaining > 0 {
|
||||||
|
let hint_player = view.board.player_to_left(&self.me);
|
||||||
|
let hint_card = rand::thread_rng().choose(&view.get_hand(&hint_player)).unwrap();
|
||||||
let hinted = {
|
let hinted = {
|
||||||
if rand::random() {
|
if rand::random() {
|
||||||
// hint a color
|
// hint a color
|
||||||
Hinted::Color(rand::thread_rng().choose(&COLORS).unwrap())
|
Hinted::Color(hint_card.color)
|
||||||
} else {
|
} else {
|
||||||
Hinted::Value(*rand::thread_rng().choose(&VALUES).unwrap())
|
Hinted::Value(hint_card.value)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
TurnChoice::Hint(Hint {
|
TurnChoice::Hint(Hint {
|
||||||
player: view.board.player_to_left(&self.me),
|
player: hint_player,
|
||||||
hinted: hinted,
|
hinted: hinted,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue