Στο παιγνίδι Risk κάποιες φορές τραβάς μια κάρτα. Οι πιθανές κάρτες είναι Άλογο (H), Κανόνι ( C ), Στρατιώτης (S) ή Μπαλαντέρ (J) που μπορεί να γίνει οποιαδήποτε άλλη κάρτα.
Ένα σύνολο από 3 κάρτες είναι έγκυρο, όταν οι κάρτες είναι όλες είτε ίδιες, είτε διαφορετικές. Για παράδειγμα
Έγκυρα σύνολα
Μη έγκυρα σύνολα
{ Η , Η , Η } 3 ίδια
{ H , H , C }
{ C , S , H } 3 διαφορετικά
{ H , C , H }
{ S , S , J } 3 ίδια, με μπαλαντέρ σαν S
{ H , S , J } 3 διαφορετικά, με μπαλαντέρ σαν C
{ H , J , J } με 2 μπαλαντέρ !
Φτιάξτε μια συνάρτηση που να λέει αν ένα σύνολο από 3 κάρτες είναι έγκυρο ή όχι
Φτιάξτε μια συνάρτηση που να λέει αν υπάρχει ένα έγκυρο σύνολο σε μια συλλογή από κάρτες.
def simplevalid(x):
if 'j' in x: return True
if x[0] == x[1]:
if x[0] == x[2]: return True
else: return False
if x[2] == x[0] or x[2] == x[1]: return False
else: return True
def sortedvalid(x):
y = [i for i in x if i!='j']
if len(y) < 3: return True
y.sort()
if y[0] == y[-1]: return True
for a,b in zip(y,y[1:]):
if a == b: return False
return True
def validinset(X, valid=simplevalid):
for x in X:
if valid(x): return True
return False
έφτιαξα και την sortedvalid που δουλεύει και σε άλλες περιπτώσεις αν έχουμε περισσότερες κάρτες
def valid(x):
if len(x) < 3: return False
if len(x) > 4: return True #προεραιτικό!
if 'J' in x: return True
freq = x.count('H'), x.count('C'), x.count('S')
for n in freq:
if n >= 3: return True
if 0 in freq: return False
return True
def valid(x):
l = len(x)
if l < 3: return False
if l > 4: return True
if 'J' in x: return True
cards = 'HCS'
freq = (x.count(i) for i in cards)
allcards = True
for n in freq:
if n >= 3: return True
if not n: allcards = False
return allcards
Μιας και δεν υπήρξαν ακόμα πολλές συμμετοχές δεν θα αναπτύξω το σκεπτικό, και ελεύθερα οποίος θέλει το κάνει. Δεν χρειάζεται καν να ξέρεις μια γλώσσα προγραμματισμού για να το κάνεις αυτό
auto isValid(const vector<card_t> &hand) {
if (hand.size() < 3) return false;
if (hand.size() > 4) return true;
if (contains(hand, Jocker))
return true; // 3 cards. Did vector contains 'J' ?
if ((hand.size() == 4) && (!eqDistr(hand))) return true;
auto crd = getCardinality(hand);
if (crd >= 3) return true; // different cards
if (crd == 1) return true; // all cards the same
return false; // X-Y-Y, no jocker case
}
Μαζί με κάποιες βοηθητικές μικρές συναρτήσεις. Βγάζοντας τες έξω κάνω την λογική του αλγόριθμου πολύ ποιο εμφανής. Το ότι είχα τα asserts, που πρώτα έγραφα ένα τέτοιο και μετά προσπαθούσα να το κάνω να περάσει, βοήθησε πολύ σε αυτή την διαδικασία (την οποία λέμε refactoring). Τεσπα, οι βοηθητικές αυτές συναρτήσεις είναι :
// Check if vector contains element
template <typename T>
bool contains(const vector<T> &hand, T card) {
return find(hand.begin(), hand.end(), card) != hand.end();
}
// Get vector cardinality
template <typename T>
auto getCardinality(vector<T> hand) {
// set<T> set(begin(hand), end(hand));
sort(hand.begin(), hand.end());
hand.erase(unique(hand.begin(), hand.end()), hand.end());
return hand.size();
}
// Is equal distribution ?
bool eqDistr(const vector<card_t> &hand) {
assert(hand.size() == 4);
// is it X-X-Y-Y or X-Y-Y-Y ?
auto cnt = count_if(hand.begin(), hand.end(),
[&](char c) { return c == hand[0]; });
return cnt == 2;
}
Θα με συγχωρέσεις που δεν έχω βγάλει ακόμα την ολοκληρωμένη λύση μου σε πρόγραμμα τερματικού (με βάση την προηγούμενη άσκηση), αλλά υπάρχει μία έλλειψη χρόνου.