void get_hint()

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