explicit HintStrategySetPacking()

in csrc/InfoBot.cc [791:895]


    explicit HintStrategySetPacking(const HandInfo& info) {
        // Make a matrix with info.size() rows and NUMCOLORS columns.
        // Set each cell (r,c) to 1 if the r'th card could be of color c.
        // Find the max-cardinality set of disjoint rows; this is the
        // NP-complete "set-cover" problem, so we just do brute force
        // search over the at-most-32 possible row-sets.
        // Each row in the max-cardinality set corresponds to a
        // card in the hand where touching that card with a color hint
        // cannot possibly be confused with any other hint generated
        // by this algorithm.
        //
        int nrows = info.size();
        unsigned rowsetmax = (1u << nrows);
        unsigned largest_disjoint_color_rowset = 1;
        int best_cardinality = 1;
        for (unsigned rowset = 3; rowset < rowsetmax; ++rowset) {
            int cardinality_of_rowset = __builtin_popcount(rowset);
            if (cardinality_of_rowset <= best_cardinality) {
                // Go on only if this rowset has a chance of being a new "best".
                continue;
            }
            bool rowset_is_disjoint = true;
            unsigned kmask = 0;
            for (int i=0; i < nrows; ++i) {
                if (rowset & (1u << i)) {
                    info[i].for_each_possibility([&](Card card) {
                        unsigned km = (1u << int(card.color));
                        if (kmask & km) {
                            rowset_is_disjoint = false;
                        }
                        kmask |= km;
                    });
                }
            }
            if (rowset_is_disjoint) {
                best_cardinality = cardinality_of_rowset;
                largest_disjoint_color_rowset = rowset;
            }
        }

        // Also perform the same operation, but for value hints.
        //
        unsigned largest_disjoint_value_rowset = 1;
        best_cardinality = 1;
        for (unsigned rowset = 3; rowset < rowsetmax; ++rowset) {
            int cardinality_of_rowset = __builtin_popcount(rowset);
            if (cardinality_of_rowset <= best_cardinality) {
                // Go on only if this rowset has a chance of being a new "best".
                continue;
            }
            bool rowset_is_disjoint = true;
            unsigned vmask = 0;
            for (int i=0; i < nrows; ++i) {
                if (rowset & (1u << i)) {
                    info[i].for_each_possibility([&](Card card) {
                        unsigned vm = (1u << int(card.value));
                        if (vmask & vm) {
                            rowset_is_disjoint = false;
                        }
                        vmask |= vm;
                    });
                }
            }
            if (rowset_is_disjoint) {
                best_cardinality = cardinality_of_rowset;
                largest_disjoint_value_rowset = rowset;
            }
        }

        // Now generate our mapping from hints to ints.
        std::fill(color_to_int_, std::end(color_to_int_), -1);
        std::fill(value_to_int_, std::end(value_to_int_), -1);
        count_ = 0;
        for (int i = 0; i < nrows; ++i) {
            if (largest_disjoint_color_rowset & (1u << i)) {
                info[i].for_each_possibility([&](Card card) {
                    color_to_int_[card.color] = count_;
                });
                count_ += 1;
            }
            if (largest_disjoint_value_rowset & (1u << i)) {
                info[i].for_each_possibility([&](Card card) {
                    value_to_int_[card.value] = count_;
                });
                count_ += 1;
            }
        }

        // Some colors and values might not be touched by any of the rows
        // in our largest disjoint rowset. Permit those hints to be given,
        // since their "overt" information might be useful to the hintee.
        int cur_block = 0;
        for (Color k = RED; k <= BLUE; ++k) {
            if (color_to_int_[k] == -1) {
                color_to_int_[k] = cur_block;
                cur_block = (cur_block + 1) % count_;
            }
        }
        for (int v = 1; v <= 5; ++v) {
            if (value_to_int_[v] == -1) {
                value_to_int_[v] = cur_block;
                cur_block = (cur_block + 1) % count_;
            }
        }
    }