예제 #1
0
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;
}
예제 #2
0
/**
 * 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
}