Skip to content

Instantly share code, notes, and snippets.

@vincesp
Created November 15, 2015 21:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vincesp/fe1e1e6671fa176522db to your computer and use it in GitHub Desktop.
Save vincesp/fe1e1e6671fa176522db to your computer and use it in GitHub Desktop.
//
// https://www.linkedin.com/grp/post/86782-6068598162933764098
//
#include <iostream>
#include <iomanip>
#include <vector>
#include <deque>
#include <memory>
#include <array>
const int ATTR_COUNT = 4;
const int SET_SIZE = 3;
using Card = std::array<int, ATTR_COUNT>;
enum Attribute {shape, fill, color, n};
enum Shape {square, circle, triangle};
enum Fill {empty, hashed, solid};
enum Color {red, green, blue};
const char* attributeLabels[][4] {
{"square", "circle", "triangle"},
{"empty", "hashed", "solid"},
{"red", "green", "blue"},
{"", "1", "2", "3"}
};
class Set {
const Card& firstCard;
int attributeIndex;
bool checkThreeOfAKind(const Card& card) {
bool match = true;
for (int i = 0; i < ATTR_COUNT; ++i) if (i != attributeIndex) {
if (firstCard[i] != card[i]) {
match = false;
break;
}
}
return match;
}
bool checkOneOfAKind(const Card& card) {
if (card[attributeIndex] != firstCard[attributeIndex]) return false;
bool match = true;
for (int i = 0; match && i < ATTR_COUNT; ++i) if (i != attributeIndex) {
for (std::vector<const Card>::iterator iCard = cards.begin(); iCard != cards.end(); ++iCard) {
if ((*iCard)[i] == card[i]) {
match = false;
break;
}
}
}
return match;
}
bool (Set::*pCheck) (const Card&) = nullptr;
public:
Set (const Card& firstCard) : firstCard(firstCard) {
cards.push_back(firstCard);
}
std::vector<Card> cards;
bool matchCard(const Card& card) {
bool match = false;
if (cards.size() == 1) {
int misMatchCount = 0;
int misMatchIndex;
int matchCount = 0;
int matchIndex;
for (int i = 0; i < ATTR_COUNT; ++i) {
if (firstCard[i] == card[i]) {
++matchCount;
matchIndex = i;
} else {
++misMatchCount;
misMatchIndex = i;
};
}
if (misMatchCount == 1) {
attributeIndex = misMatchIndex;
pCheck = &Set::checkThreeOfAKind;
match = true;
} else if (matchCount == 1) {
attributeIndex = matchIndex;
pCheck = &Set::checkOneOfAKind;
match = true;
}
} else {
match = (this->*pCheck)(card);
}
if (match) {
cards.push_back(card);
}
return match;
}
inline bool isComplete() {
return cards.size() == SET_SIZE;
}
friend std::ostream& operator<<(std::ostream& os, const Set& set) {
if (set.pCheck == &Set::checkThreeOfAKind) os << "=== Three of a Kind";
if (set.pCheck == &Set::checkOneOfAKind) os << "=== One of a Kind";
os << " (" << set.attributeIndex + 1 << ')' << std::endl;
for (std::vector<const Card>::iterator iCard = set.cards.begin(); iCard != set.cards.end(); ++iCard) {
for (int i = 0; i < ATTR_COUNT; ++i) {
os << std::setw(7) << std::left << attributeLabels[i][(*iCard)[i]] << ' ';
}
os << std::endl;
}
return os;
}
};
using PSet = std::unique_ptr<Set>;
class SetFinder {
std::deque<PSet> sets;
inline bool checkCard(const Card& card) {
bool setFound = false;
bool cardUsed = false;
for (std::deque<PSet>::iterator iSet = sets.begin(); iSet != sets.end(); ++iSet) {
if ((*iSet)->matchCard(card)) {
cardUsed = true;
if ((*iSet)->isComplete()) {
std::cout << **iSet;
foundSets.push_back(std::move(*iSet));
iSet = sets.erase(iSet);
setFound = true;
}
break;
}
}
if (!cardUsed) sets.push_back(PSet(new Set(card)));
return setFound;
}
bool recheck() {
bool setFound = false;
size_t setsSize = sets.size();
for (int i = 0; i < setsSize && !setFound; ++i) {
PSet pSet = std::move(sets.front());
sets.pop_front();
for (std::vector<const Card>::iterator iCard = pSet->cards.begin(); iCard != pSet->cards.end(); ++iCard) {
if (checkCard(*iCard)) {
setFound = true;
}
}
}
return setFound;
}
public:
std::vector<PSet> foundSets;
template<size_t N>
void addCards(Card (&cards)[N]) {
for (int i = 0; i < N; ++i) {
checkCard(cards[i]);
}
//we might have started an incomplete set above, so
//keep rechecking until no further sets are found:
while (recheck()) {}
}
};
int main(int argc, const char * argv[]) {
Card cards[5] = {
{square, empty, blue, 2},
{square, empty, red, 2},
{square, solid, blue, 1},
{square, hashed, green, 3},
{square, empty, blue, 1},
};
SetFinder setFinder;
setFinder.addCards(cards);
std::cout << "Total sets found: " << setFinder.foundSets.size() << std::endl;
Card deal2[1] = {{square, empty, blue, 3}};
setFinder.addCards(deal2);
std::cout << "Total sets found: " << setFinder.foundSets.size() << std::endl;
return 0;
}
@vincesp
Copy link
Author

vincesp commented Nov 16, 2015

There is one step still missing: To find the maximum number of possible sets for a given stack of cards, the cards in an already existing set also have to be considered for building up new sets.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment