Camicia

Camicia

Μέτριο

Εισαγωγή

Ένα βροχερό απόγευμα, κάθεσαι στο τραπέζι της κουζίνας και παίζεις χαρτιά με τη γιαγιά σου. Το παιχνίδι είναι η δική της εκδοχή του παιχνιδιού Camicia.

Στην αρχή μοιάζει με ένα ακόμη φιλικό παιχνίδι: χαρτιά πέφτουν στο τραπέζι, γέλια γύρω από αυτό, και πού και πού ένα νικητήριο χαμόγελο από τη Nonna. Όσο όμως τραβάει το παιχνίδι, κάτι παράξενο συμβαίνει. Τα ίδια χαρτιά γυρίζουν ξανά και ξανά. Παίζεις χαρτί με το χαρτί, κι όμως το τέλος δεν φαίνεται να έρχεται ποτέ.

Αρχίζεις να αναρωτιέσαι. Θα τελειώσει άραγε ποτέ αυτό το παιχνίδι; Ή μήπως θα μπορούσαμε να παίζουμε για πάντα;

Αργότερα, από περιέργεια, ψάχνεις στο διαδίκτυο και προς έκπληξή σου ανακαλύπτεις ότι αυτό που συνέβη δεν ήταν απλώς κακή τύχη. Εσύ και η γιαγιά σου μπορεί να πέσατε πάνω σε μία από τις μεγαλύτερες δυνατές ακολουθίες! Ξαφνικά, έχεις κολλήσει. Αυτό που ξεκίνησε ως ένα χαλαρό παιχνίδι έχει γίνει αναζήτηση: πόσο μπορεί να κρατήσει στ' αλήθεια ένα τέτοιο παιχνίδι; Μπορείς να βρεις μια ακολουθία ακόμη μεγαλύτερη από αυτή που παίξατε στο τραπέζι της κουζίνας; Ίσως μάλιστα αρκετά μεγάλη για να σημειώσεις νέο παγκόσμιο ρεκόρ;

Κι έτσι, χωρίς τίποτα άλλο παρά μια τράπουλα και λίγη αλγοριθμική ευστροφία, αποφασίζεις να το ερευνήσεις...

Οδηγίες

Σε αυτή την άσκηση, θα προσομοιώσεις ένα παιχνίδι πολύ παρόμοιο με το κλασικό παιχνίδι με κάρτες Camicia. Το πρόγραμμά σου θα λάβει την αρχική διάταξη των τραπουλών δύο παικτών και πρέπει να προσομοιώσει το παιχνίδι μέχρι να τελειώσει (ή να εντοπίσει ότι δε θα τελειώσει ποτέ).

Κανόνες

  • Η τράπουλα μοιράζεται σε δύο παίκτες. Οι κάρτες κάθε παίκτη διαβάζονται από αριστερά προς τα δεξιά, όπου η αριστερότερη κάρτα είναι η κορυφή της τράπουλας.
  • Σε έναν γύρο, και οι δύο παίκτες παίζουν τουλάχιστον μία κάρτα.
  • Οι παίκτες παίζουν εναλλάξ βάζοντας την πάνω κάρτα της τράπουλας τους σε έναν κεντρικό σωρό.
  • Αν η κάρτα είναι αριθμητική κάρτα (2-10), η σειρά περνά απλώς στον άλλο παίκτη.
  • Αν η κάρτα είναι κάρτα πληρωμής, πρέπει να πληρωθεί ποινή:
    • J → ο αντίπαλος πρέπει να πληρώσει 1 κάρτα
    • Q → ο αντίπαλος πρέπει να πληρώσει 2 κάρτες
    • K → ο αντίπαλος πρέπει να πληρώσει 3 κάρτες
    • A → ο αντίπαλος πρέπει να πληρώσει 4 κάρτες
  • Αν ο παίκτης που πληρώνει ποινή αποκαλύψει άλλη κάρτα πληρωμής, σταματά να πληρώνει την ποινή. Ο άλλος παίκτης πρέπει τότε να πληρώσει ποινή με βάση τη νέα κάρτα πληρωμής.
  • Αν η ποινή πληρωθεί πλήρως χωρίς διακοπή, ο παίκτης που έβαλε την τελευταία κάρτα πληρωμής μαζεύει τον κεντρικό σωρό και τον τοποθετεί στο κάτω μέρος της τράπουλας του. Έπειτα, ο παίκτης αυτός ξεκινά τον επόμενο γύρο.
  • Αν ένας παίκτης ξεμείνει από κάρτες και δε μπορεί να παίξει κάρτα (είτε ενώ πληρώνει ποινή είτε όταν είναι η σειρά του), ο άλλος παίκτης μαζεύει τον κεντρικό σωρό.
  • Η στιγμή που ένας παίκτης μαζεύει κάρτες από τον κεντρικό σωρό ονομάζεται τρικ.
  • Αν ένας παίκτης έχει όλες τις κάρτες στην κατοχή του μετά από ένα τρικ, το παιχνίδι τελειώνει.
  • Το παιχνίδι μπαίνει σε βρόχο μόλις οι τράπουλες είναι ίδιες με αυτές που ήταν νωρίτερα κατά τη διάρκεια του παιχνιδιού, χωρίς να μετράμε τις αριθμητικές κάρτες!

Παραδείγματα

Μικρό παράδειγμα μιας παρτίδας που τελειώνει.

Γύρος Παίκτης Α Παίκτης Β Σωρός Οφειλόμενη ποινή
1 2 A 7 8 Q 10 3 4 5 6 K 9 J -
1 A 7 8 Q 10 3 4 5 6 K 9 J 2 -
1 A 7 8 Q 10 4 5 6 K 9 J 2 3 -
1 7 8 Q 10 4 5 6 K 9 J 2 3 A Player B: 4
1 7 8 Q 10 5 6 K 9 J 2 3 A 4 Player B: 3
1 7 8 Q 10 6 K 9 J 2 3 A 4 5 Player B: 2
1 7 8 Q 10 K 9 J 2 3 A 4 5 6 Player B: 1
1 7 8 Q 10 9 J 2 3 A 4 5 6 K Player A: 3
1 8 Q 10 9 J 2 3 A 4 5 6 K 7 Player A: 2
1 Q 10 9 J 2 3 A 4 5 6 K 7 8 Player A: 1
1 10 9 J 2 3 A 4 5 6 K 7 8 Q Player B: 2
1 10 J 2 3 A 4 5 6 K 7 8 Q 9 Player B: 1
1 10 - 2 3 A 4 5 6 K 7 8 Q 9 J Player A: 1
1 - - 2 3 A 4 5 6 K 7 8 Q 9 J 10 -
2 - 2 3 A 4 5 6 K 7 8 Q 9 J 10 - -

status: "finished", cards: 13, tricks: 1

Μικρό παράδειγμα μιας παρτίδας που μπαίνει σε βρόχο.

Γύρος Παίκτης Α Παίκτης Β Σωρός Οφειλόμενη ποινή
1 J 2 3 4 J 5 - -
1 2 3 4 J 5 J Player B: 1
1 2 3 J 5 J 4 -
2 2 3 J 4 J 5 - -
2 3 J 4 J 5 2 -
2 3 J 4 5 2 J Player A: 1
2 J 4 5 2 J 3 -
3 J 4 5 2 J 3 - -
3 J 4 2 J 3 5 -
3 4 2 J 3 5 J Player B: 1
3 4 J 3 5 J 2 -
4 4 5 J 2 J 3 - -

Η αρχή του 4ου γύρου ταιριάζει με την αρχή του 2ου γύρου. Θυμήσου, η αξία των αριθμητικών καρτών δεν έχει σημασία.

status: "loop", cards: 8, tricks: 3

Η αποστολή σου

  • Χρησιμοποιώντας την είσοδο, προσομοίωσε το παιχνίδι ακολουθώντας τους παραπάνω κανόνες.
  • Καθόρισε τις ακόλουθες πληροφορίες για το παιχνίδι:
    • Status: "finished" ή "loop"
    • Cards: συνολικός αριθμός καρτών που παίχτηκαν σε όλη τη διάρκεια του παιχνιδιού
    • Tricks: πόσες φορές μαζεύτηκε ο κεντρικός σωρός
Advanced

Για όσους θέλουν να αναλάβουν μια πιο συναρπαστική πρόκληση, η αναζήτηση άλλων ρεκόρ για το μεγαλύτερο παιχνίδι που έχει τέλος είναι ακόμη ανοιχτή. Υπάρχουν 653.534.134.886.878.245.000 (περίπου 654 εκατομμύρια τρισεκατομμύρια) πιθανότητες, και δεν τις έχουμε υπολογίσει όλες ακόμα!

Πλεονεκτήματα και μειονεκτήματα της from_quote

Η συνάρτηση Card.from_quote κάνει parse μια συμβολοσειρά σε Card. Αυτό σου επιτρέπει να γράψεις κώδικα σαν τον παρακάτω:

card : Card
card = "Q"

Αυτός ο κώδικας καλεί έμμεσα τη Card.from_quote("Q") σε χρόνο μεταγλώττισης. Η εκτέλεση κώδικα σε χρόνο μεταγλώττισης με αυτή την προσέγγιση (ή με άλλες μεθόδους που περιγράφονται παρακάτω) μπορεί δυνητικά να κάνει το πρόγραμμα πιο γρήγορο, αφού δεν χρειάζεται να χειρίζεται το parsing και την επικύρωση δεδομένων σε χρόνο εκτέλεσης. Επιπλέον, διασφαλίζει ότι τα μη έγκυρα δεδομένα (όπως μια λανθασμένη μορφή ημερομηνίας) απορρίπτονται σε χρόνο μεταγλώττισης αντί σε χρόνο εκτέλεσης, κάνοντας την εφαρμογή σου πιο ανθεκτική. Τέλος, κάνει τον κώδικα ωραίο και ευανάγνωστο. Για παράδειγμα, ορίστε ένα από τα τεστ αυτής της άσκησης:

result = simulate_game({
    player_a: ["2", "A"],
    player_b: ["3", "4", "5", "6", "K"],
})

Ωστόσο, κάποιος που διαβάζει αυτόν τον κώδικα μπορεί να υποθέσει ότι η συνάρτηση simulate_game παίρνει μια καταγραφή που περιέχει δύο List(Str), ενώ στην πραγματικότητα είναι μια καταγραφή που περιέχει δύο List(Card). Αυτό μπορεί να προκαλέσει σύγχυση και λάθη. Για αυτόν τον λόγο, μπορεί να προτιμήσεις τη λίγο μεγαλύτερη αλλά πιο ρητή μορφή: "Q".Card, η οποία καλεί επίσης τη from_quote σε χρόνο μεταγλώττισης.

Στα δικά σου προγράμματα, μπορεί να προτιμήσεις να χρησιμοποιήσεις τη from_str αντ' αυτής:

Card := { ... }.{
    from_str : Str -> Try(Card, [InvalidCard])
    from_str = |card_str| ...
}

Μπορείς επίσης να καλέσεις τη from_str (ή οποιαδήποτε άλλη καθαρή συνάρτηση) σε χρόνο μεταγλώττισης, όπως εδώ:

Ok(card) = Card.from_str("Q")

Σημείωση: Αν τοποθετήσεις αυτόν τον ορισμό στο ανώτατο επίπεδο, εκτελείται σε χρόνο μεταγλώττισης (αφού η συνάρτηση είναι καθαρή και η είσοδός της είναι γνωστή σε χρόνο μεταγλώττισης). Μέσα σε μια συνάρτηση, μπορείς να χρησιμοποιήσεις τη from_str σε χρόνο εκτέλεσης.

Υποδείξεις

Εμφάνισε τις υποδείξεις

Χρησιμοποίησε το match με [] και [card, .. as rest] για να ξεχωρίσεις μια άδεια τράπουλα από την επόμενη κάρτα της και τις υπόλοιπες κάρτες. Ενημερώσεις καταγραφών όπως { ..state, pile: new_pile } μπορούν να κρατήσουν τις μεταβάσεις του παιχνιδιού ρητές, χωρίς κατάσταση που να μεταβάλλεται. Ένα Set μπορεί να αποθηκεύσει πλειάδες ή λίστες ως κλειδιά όταν παρακολουθείς καταστάσεις παιχνιδιού που έχεις ξαναδεί.

Επεξεργασία μέσω GitHub Ο σύνδεσμος ανοίγει σε νέο παράθυρο ή καρτέλα
Roc Exercism

Έτοιμος να ξεκινήσεις την άσκηση Camicia;

Γράψου στο Exercism για να μάθεις και να κατακτήσεις Roc με 135 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.