Merge pull request #2 from tjhance/use-4-hints-per-player

in some situations, allow 4 possible hints for some players
This commit is contained in:
Jeff Wu 2016-06-22 08:17:52 -07:00 committed by GitHub
commit 9214006ccb
2 changed files with 165 additions and 53 deletions

View file

@ -306,6 +306,14 @@ impl CardPossibilityTable {
.collect::<HashSet<_>>() .collect::<HashSet<_>>()
.len() == 1 .len() == 1
} }
pub fn can_be_color(&self, color: Color) -> bool {
self.get_possibilities().into_iter().any(|card| card.color == color)
}
pub fn can_be_value(&self, value: Value) -> bool {
self.get_possibilities().into_iter().any(|card| card.value == value)
}
} }
impl <'a> From<&'a CardCounts> for CardPossibilityTable { impl <'a> From<&'a CardCounts> for CardPossibilityTable {
fn from(counts: &'a CardCounts) -> CardPossibilityTable { fn from(counts: &'a CardCounts) -> CardPossibilityTable {

View file

@ -621,6 +621,57 @@ impl InformationPlayerStrategy {
goodness goodness
} }
// Returns the number of ways to hint the player.
fn get_info_per_player(&self, player: Player) -> u32 {
let info = self.get_player_public_info(&player);
let may_be_all_one_color = COLORS.iter().any(|color| {
info.iter().all(|card| {
card.can_be_color(*color)
})
});
let may_be_all_one_number = VALUES.iter().any(|value| {
info.iter().all(|card| {
card.can_be_value(*value)
})
});
return if !may_be_all_one_color && !may_be_all_one_number { 4 } else { 3 }
// Determine if both:
// - it is public that there are at least two colors
// - it is public that there are at least two numbers
}
fn get_other_players_starting_after(&self, player: Player) -> Vec<Player> {
let view = &self.last_view;
let n = view.board.num_players;
(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 {
let view = &self.last_view;
// using hint goodness barely helps
let mut hint_options = hint_option_set.into_iter().map(|hinted| {
(self.hint_goodness(&hinted, &hint_player, view), hinted)
}).collect::<Vec<_>>();
hint_options.sort_by(|h1, h2| {
h2.0.partial_cmp(&h1.0).unwrap_or(Ordering::Equal)
});
if hint_options.len() == 0 {
// NOTE: Technically possible, but never happens
} else {
if hint_options.len() > 1 {
debug!("Choosing amongst hint options: {:?}", hint_options);
}
}
hint_options.remove(0).1
}
fn get_hint(&self) -> TurnChoice { fn get_hint(&self) -> TurnChoice {
let view = &self.last_view; let view = &self.last_view;
@ -629,25 +680,39 @@ impl InformationPlayerStrategy {
// 0. a value hint on card i // 0. a value hint on card i
// 1. a color hint on card i // 1. a color hint on card i
// 2. any hint not involving card i // 2. any hint not involving card i
// However, if it is public info that the player has at least two colors
// and at least two numbers, then instead we do
// 2. any color hint not involving i
// 3. any color hint not involving i
// TODO: make it so space of hints is larger when there is // TODO: make it so space of hints is larger when there is
// knowledge about the cards? // knowledge about the cards?
let total_info = 3 * (view.board.num_players - 1); let info_per_player: Vec<Player> = self.get_other_players_starting_after(self.me).iter().map(
|player| { self.get_info_per_player(*player) }
).collect();
let total_info = info_per_player.iter().fold(0, |a, b| a + b);
let hint_info = self.get_hint_sum_info(total_info, view); let hint_info = self.get_hint_sum_info(total_info, view);
let hint_type = hint_info.value % 3; //let hint_type = hint_info.value % 3;
let player_amt = (hint_info.value - hint_type) / 3; //let player_amt = (hint_info.value - hint_type) / 3;
let mut hint_type = hint_info.value;
let mut player_amt = 0;
while hint_type >= info_per_player[player_amt] {
hint_type -= info_per_player[player_amt];
player_amt += 1;
}
let hint_info_we_can_give_to_this_player = info_per_player[player_amt];
let hint_player = (self.me + 1 + player_amt) % view.board.num_players; let hint_player = (self.me + 1 + (player_amt as u32)) % view.board.num_players;
let hand = view.get_hand(&hint_player); let hand = view.get_hand(&hint_player);
let card_index = self.get_index_for_hint(self.get_player_public_info(&hint_player), view); let card_index = self.get_index_for_hint(self.get_player_public_info(&hint_player), view);
let hint_card = &hand[card_index]; let hint_card = &hand[card_index];
let hinted = if hint_info_we_can_give_to_this_player == 3 {
let hinted = match hint_type { match hint_type {
0 => { 0 => {
Hinted::Value(hint_card.value) Hinted::Value(hint_card.value)
} }
@ -666,28 +731,44 @@ impl InformationPlayerStrategy {
hint_option_set.insert(Hinted::Value(card.value)); hint_option_set.insert(Hinted::Value(card.value));
} }
} }
// using hint goodness barely helps self.get_best_hint_of_options(hint_player, hint_option_set)
let mut hint_options = hint_option_set.into_iter().map(|hinted| {
(self.hint_goodness(&hinted, &hint_player, view), hinted)
}).collect::<Vec<_>>();
hint_options.sort_by(|h1, h2| {
h2.0.partial_cmp(&h1.0).unwrap_or(Ordering::Equal)
});
if hint_options.len() == 0 {
// NOTE: Technically possible, but never happens
Hinted::Color(hint_card.color)
} else {
if hint_options.len() > 1 {
debug!("Choosing amongst hint options: {:?}", hint_options);
}
hint_options.remove(0).1
}
} }
_ => { _ => {
panic!("Invalid hint type") panic!("Invalid hint type")
} }
}
} else {
match hint_type {
0 => {
Hinted::Value(hint_card.value)
}
1 => {
Hinted::Color(hint_card.color)
}
2 => {
// Any value hint for a card other than the first
let mut hint_option_set = HashSet::new();
for card in hand {
if card.value != hint_card.value {
hint_option_set.insert(Hinted::Value(card.value));
}
}
self.get_best_hint_of_options(hint_player, hint_option_set)
}
3 => {
// Any color hint for a card other than the first
let mut hint_option_set = HashSet::new();
for card in hand {
if card.color != hint_card.color {
hint_option_set.insert(Hinted::Color(card.color));
}
}
self.get_best_hint_of_options(hint_player, hint_option_set)
}
_ => {
panic!("Invalid hint type")
}
}
}; };
TurnChoice::Hint(Hint { TurnChoice::Hint(Hint {
@ -697,23 +778,46 @@ impl InformationPlayerStrategy {
} }
fn infer_from_hint(&mut self, hint: &Hint, result: &Vec<bool>) { fn infer_from_hint(&mut self, hint: &Hint, result: &Vec<bool>) {
let n = self.last_view.board.num_players;
let total_info = 3 * (n - 1);
let hinter = self.last_view.board.player; let hinter = self.last_view.board.player;
let info_per_player: Vec<Player> = self.get_other_players_starting_after(hinter).iter().map(
|player| { self.get_info_per_player(*player) }
).collect();
let total_info = info_per_player.iter().fold(0, |a, b| a + b);
let n = self.last_view.board.num_players;
let player_amt = (n + hint.player - hinter - 1) % n; let player_amt = (n + hint.player - hinter - 1) % n;
let amt_from_prev_players = info_per_player.iter().take(player_amt as usize).fold(0, |a, b| a + b);
let hint_info_we_can_give_to_this_player = info_per_player[player_amt as usize];
let card_index = self.get_index_for_hint(self.get_player_public_info(&hint.player), &self.last_view); let card_index = self.get_index_for_hint(self.get_player_public_info(&hint.player), &self.last_view);
let hint_type = if result[card_index] { let hint_type =
if hint_info_we_can_give_to_this_player == 3 {
if result[card_index] {
match hint.hinted { match hint.hinted {
Hinted::Value(_) => 0, Hinted::Value(_) => 0,
Hinted::Color(_) => 1, Hinted::Color(_) => 1,
} }
} else { } else {
2 2
}
} else {
if result[card_index] {
match hint.hinted {
Hinted::Value(_) => 0,
Hinted::Color(_) => 1,
}
} else {
match hint.hinted {
Hinted::Value(_) => 2,
Hinted::Color(_) => 3,
}
}
}; };
let hint_value = player_amt * 3 + hint_type; let hint_value = amt_from_prev_players + hint_type;
let mod_info = ModulusInformation::new(total_info, hint_value); let mod_info = ModulusInformation::new(total_info, hint_value);