in csrc/InfoBot.cc [1254:1345]
void get_hint(Hanabi::Server& server) const {
OwnedGameView view(this->last_view, server);
// Can give up to 3(n-1) hints
// For any given player with at least 4 cards, and index i, there are at least 3 hints that can be given.
// 0. a value hint on card i
// 1. a color hint on 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
// knowledge about the cards?
fixed_capacity_vector<HintStrategy, MAXPLAYERS> strategies;
int total_info = 0;
for (int i = 0; i < numPlayers - 1; ++i) {
int player = (this->me + 1 + i) % numPlayers;
strategies.emplace_back(this->get_hint_strategy(player));
total_info += strategies.back().get_count();
}
auto hint_info = this->get_hint_sum_info(total_info, view);
int hint_type = hint_info.value;
int player_amt = 0;
while (hint_type >= strategies[player_amt].get_count()) {
hint_type -= strategies[player_amt].get_count();
player_amt += 1;
}
int hint_player = (this->me + 1 + player_amt) % numPlayers;
assert(hint_player != this->me);
Hinted best_hint = Hinted::Dummy();
double best_goodness = -1;
if (true) {
// get post-hint hand_info
const auto& hand = server.handOfPlayer(hint_player);
auto hand_info = this->public_info[hint_player];
int total_info = 3 * (view.num_players - 1);
auto questions = this->get_questions(total_info, view, hand_info);
for (const auto& question : questions) {
auto answer = question.answer(hand, view);
question.acknowledge_answer(answer, hand_info, view);
}
strategies[player_amt].encode_hint(view, hint_player, hint_type, [&](Hinted hinted) {
// How good is it to give this hint to the player?
auto hypothetical_hand_info = hand_info;
double goodness = 1.0;
for (int i=0; i < (int)hand_info.size(); ++i) {
auto& card_table = hypothetical_hand_info[i];
auto card = hand[i];
if (card_table.probability_is_dead(view) == 1.0) {
continue;
}
if (card_table.is_determined()) {
continue;
}
int old_weight = card_table.total_weight();
if (hinted.kind == Hinted::COLOR) {
card_table.mark_color(hinted.color, hinted.color == card.color);
} else if (hinted.kind == Hinted::VALUE) {
card_table.mark_value(hinted.value, hinted.value == card.value);
} else {
assert(false);
}
int new_weight = card_table.total_weight();
assert(new_weight <= old_weight);
double bonus = (
card_table.is_determined() ? 2 :
card_table.probability_is_dead(view) == 1.0 ? 2 :
1
);
goodness *= bonus * double(old_weight) / new_weight;
}
if (goodness > best_goodness) {
best_goodness = goodness;
best_hint = hinted;
}
});
}
if (best_hint.kind == Hinted::COLOR) {
return server.pleaseGiveColorHint(hint_player, best_hint.color);
} else {
return server.pleaseGiveValueHint(hint_player, best_hint.value);
}
}