void pleaseMakeMove()

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);
        }
    }