in csrc/InfoBot.cc [1368:1479]
void pleaseMakeMove(Hanabi::Server& server) override
{
// we already stored the view
OwnedGameView view(this->last_view, server);
auto private_info = this->get_private_info(server);
// Maybe play the best playable card...
double best_score = -1.0;
int best_index = -1;
for (int i=0; i < private_info.size(); ++i) {
const auto& card_table = private_info[i];
if (card_table.probability_is_playable(view) == 1.0) {
double score = this->get_average_play_score(view, card_table);
if (score > best_score) {
best_score = score;
best_index = i;
}
}
}
if (best_index >= 0) {
return server.pleasePlay(best_index);
}
// Maybe make a risky play...
const int discard_threshold =
view.total_cards
- (Hanabi::NUMCOLORS * 5)
- (view.num_players * view.hand_size);
if (view.lives_remaining > 1 && view.discard_size <= discard_threshold) {
for (int i=0; i < private_info.size(); ++i) {
const auto& card_table = private_info[i];
// Consider cards that are definitely either playable or dead; don't risk
// throwing away a card that we will want later in the game.
if (card_table.probability_of_predicate([&](Card card) {
return view.is_playable(card) || view.is_dead(card);
}) != 1.0) {
continue;
}
double p = card_table.probability_is_playable(view);
if (p > 0.75 && p > best_score) {
best_score = p;
best_index = i;
}
}
if (best_index >= 0) {
return server.pleasePlay(best_index);
}
}
if (!server.discardingIsAllowed()) {
return this->get_hint(server);
}
auto public_useless_indices = this->find_useless_cards(view, this->public_info[this->me]);
auto useless_indices = this->find_useless_cards(view, private_info);
if (view.discard_size <= discard_threshold) {
// if anything is totally useless, discard it
if (public_useless_indices.size() > 1) {
auto info = this->get_hint_sum_info(public_useless_indices.size(), view);
return server.pleaseDiscard(public_useless_indices[info.value]);
} else if (!useless_indices.empty()) {
// TODO: have opponents infer that i knew a card was useless
// TODO: after that, potentially prefer useless indices that arent public
return server.pleaseDiscard(useless_indices[0]);
}
}
// hinting is better than discarding dead cards
// (probably because it stalls the deck-drawing).
if (view.hints_remaining > 0) {
if (view.someone_else_can_play()) {
return this->get_hint(server);
}
}
// if anything is totally useless, discard it
if (public_useless_indices.size() > 1) {
auto info = this->get_hint_sum_info(public_useless_indices.size(), view);
return server.pleaseDiscard(public_useless_indices[info.value]);
} else if (!useless_indices.empty()) {
return server.pleaseDiscard(useless_indices[0]);
}
// NOTE: the only conditions under which we would discard a potentially useful card:
// - we have no known useless cards
// - there are no hints remaining OR nobody else can play
// Play the best discardable card
for (int i=0; i < private_info.size(); ++i) {
const auto& card_table = private_info[i];
double probability_is_seen = card_table.probability_of_predicate([&](Card card) {
return view.can_see(card);
});
double my_compval =
20.0 * probability_is_seen
+ 10.0 * card_table.probability_is_dispensable(view)
+ card_table.average_value();
if (my_compval > best_score) {
best_score = my_compval;
best_index = i;
}
}
if (best_index >= 0) {
return server.pleaseDiscard(best_index);
} else {
return server.pleaseDiscard(0);
}
}