bool Codeword::conforming(const Rules &rules) const { if (!rules) return false; // Check pegs. for (int p = 0; p < rules.pegs(); ++p) { if (_digit[p] < 0) return false; } for (int p = rules.pegs(); p < MM_MAX_PEGS; ++p) { if (_digit[p] >= 0) return false; } // Check colors. if (!rules.repeatable()) { for (int c = 0; c < rules.colors(); ++c) { if (_counter[c] > 1) return false; } } for (int c = rules.colors(); c < MM_MAX_COLORS; ++c) { if (_counter[c] > 0) return false; } return true; }
/** * Estimates an obvious lower-bound of the cost of making a non-possible * guess against a set of remaining possibilities. If such a lower-bound * is found, returns the minimum steps, depth, and worst count. If no * lower-bound can be found easily, returns zero. */ StrategyCost estimate_obvious_lowerbound( const Rules &rules, CodewordConstRange possibilities) { // We are only concerned with a handful of remaining possibilities. // If there are too many, we won't make an attempt. int p = rules.pegs(); int n = (int)possibilities.size(); if (n > p*(p+3)/2) return StrategyCost(); // Partition the possibilities by the colors they contain. Only the // size of the partitions matter; the particular colors don't. // @todo Since we will only be processing with MM_MAX_PEGS+1 groups, // we can return -1 if there are more secret groups. const int M = MM_MAX_PEGS * (MM_MAX_PEGS + 3) / 2; bool visited[M] = { false }; int group[M] = { 0 }; int ngroup = 0; for (int i = 0; i < n; ++i) { if (!visited[i]) { for (int j = i + 1; j < n; ++j) { if (!visited[j] && contain_same_colors(possibilities[i], possibilities[j])) { ++group[ngroup]; visited[j] = true; } } ++ngroup; } } // Now we have classified the remaining possibilities into ngroup groups // according to the colors they contain. For any given guess, the secrets // in the same group must have the same number of common colors with // this guess. // We next classify the possible feedback values by the number of common // colors, like below: // 0: 0A0B // 1: 0A1B 1A0B // 2: 0A2B 1A1B 2A0B // 3: 0A3B 1A2B 2A1B 3A0B // 4: 0A4B 1A3B 2B2B - - // Note that 3A1B is impossible and 4A0B is excluded because the guess is // assumed to be outside the remaining possibilities. // Next, we assign each secret group a feedback group. Note that multiple // secret groups may be assigned the same feedback group, but two secrets // in the same secret group may not be split into different feedback groups. // We try to find a simple assignment that is guaranteed to minimize the // total number of steps needed to reveal all secrets. If a simple // assignment cannot be found easily, we give up and return failure. // The algorithm is to assign each secret group, in order of decreasing // size, the largest remaining feedback group if this feedback group is // no larger than the secret group. If this can be done, the resulting // assignment is guaranteed (??? proof needed) to yield a lower bound // of the total number of steps. if (ngroup > p + 1) return StrategyCost(); std::sort(group + 0, group + ngroup, std::greater<int>()); int extra = 0; for (int i = 0; i < ngroup; ++i) { int avail = p - i - ((i < 2)? 1 : 0); if (group[i] >= avail) { extra += (group[i] - avail); } else { return StrategyCost(); } } return StrategyCost( extra + n * 2, // total steps extra > 0 ? 3 : 2, // max depth (unsigned short)(extra > 0 ? extra : n)); // worst count }