Puzzle: Find a deck of c cards, each containing p pictures, such that no two pictures match on a given card, and exactly 1 picture on each card matches exactly 1 picture on each of the other cards, where the number of unique pictures in the deck is minimized.
Criteria: Fastest code for c = 31 and p = 6.
Inspiration: This puzzle was inspired by wondering how the makers of the card game Spot It! came up with a deck of cards that differ from each other by only one picture. If it helps, the game's instructions say it contains 31 cards with at least 30 unique pictures.
Brute-force reference implementation: (Entirely impractical for use at the scale of the contest judging criteria, but perhaps useful for thinking about the problem.)
class Program
{
const int CardCount = 31, PicturesPerCard = 6;
static void Main()
{
for (int pictureLimit = PicturesPerCard; ; ++pictureLimit)
new Solver(pictureLimit).FindValidCards(0, 0);
}
class Solver
{
readonly byte[,] cards = new byte[CardCount, PicturesPerCard];
readonly int pictureLimit;
public Solver(int pictureLimit)
{
this.pictureLimit = pictureLimit;
}
public void FindValidCards(int c, int p)
{
for (; c < CardCount; ++c) {
for (; p < PicturesPerCard; ++p) {
for (int picture = p == 0 ? 0 : cards[c, p - 1] + 1; picture < pictureLimit; ++picture) { // Only consider pictures in numerically increasing sequence, since sequence order is irrelevant.
cards[c, p] = (byte)picture;
if (p < PicturesPerCard - 1) {
FindValidCards(c, p + 1);
} else if (c < CardCount - 1) {
FindValidCards(c + 1, 0);
} else if (AreCardsValid()) {
WriteCards();
System.Environment.Exit(0);
}
}
}
}
}
bool AreCardsValid()
{
for (int c = 0; c < CardCount; ++c)
for (int otherC = c + 1; otherC < CardCount; ++otherC) {
int matchCount = 0;
for (int p = 0; p < PicturesPerCard; ++p)
for (int otherP = 0; otherP < PicturesPerCard; ++otherP)
if (cards[c, p] == cards[otherC, otherP])
++matchCount;
if (matchCount != 1)
return false;
}
return true;
}
/// <summary>
/// Writes to the console all the cards with each unique picture placed in its own column for easier comparison between cards.
/// </summary>
void WriteCards()
{
for (int c = 0; c < CardCount; ++c) {
string cardString = string.Empty;
for (int picture = 0; picture < pictureLimit; ++picture)
for (int p = 0; ; ++p) {
if (p == PicturesPerCard) {
cardString += ' ';
break;
}
if (cards[c, p] == picture) {
cardString += (char)(picture < 26 ? 'A' + picture : 'a' + (picture - 26));
break;
}
}
System.Console.WriteLine(cardString);
}
}
}
}
Edits: Reworded puzzle to correctly articulate the intent that the goal is to find a deck that matches the constraints. Added the missing constraint that no two pictures on a card may match. Changed contest type from code golf to speed.