Created
November 15, 2015 21:08
-
-
Save vincesp/fe1e1e6671fa176522db to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.