Track
/
Roc
Roc
/
Esercizi
/
Camicia
Camicia

Camicia

Medio

Introduzione

Un pomeriggio piovoso, sei seduto al tavolo della cucina a giocare a carte con tua nonna. Il gioco è la sua versione di Camicia.

All'inizio sembra soltanto un'altra partita amichevole: carte calate con forza sul tavolo, risate intorno al tavolo, e ogni tanto un sorriso vittorioso da parte di Nonna. Ma man mano che la partita va avanti, succede qualcosa di strano. Le stesse carte continuano a ripresentarsi. Giochi carta dopo carta, eppure la fine non sembra mai arrivare.

Cominci a chiederti. Questa partita finirà mai? O potremmo continuare a giocare per sempre?

Più tardi, spinto dalla curiosità, cerchi online e con tua sorpresa scopri che quello che è successo non è stata solo sfortuna. Tu e tua nonna potreste esservi imbattuti in una delle sequenze più lunghe possibili! All'improvviso ne sei catturato. Quello che era iniziato come una partita spensierata si è trasformato in una missione: quanto può durare davvero una partita del genere? Riesci a trovare una sequenza ancora più lunga di quella che hai giocato al tavolo della cucina? Magari persino abbastanza lunga da stabilire un nuovo record mondiale?

E così, armato soltanto di un mazzo di carte e di un po' di ingegno algoritmico, decidi di indagare...

Istruzioni

In questo esercizio simulerai una partita molto simile al classico gioco di carte Camicia. Il programma riceverà la configurazione iniziale dei mazzi di due giocatori e dovrà simulare la partita fino alla sua conclusione (o rilevare che non finirà mai).

Regole

  • Il mazzo è diviso tra due giocatori. Le carte dei giocatori si leggono da sinistra a destra: la carta più a sinistra è quella in cima al mazzo.
  • Un round consiste in almeno una carta giocata da ciascuno dei due giocatori.
  • A turno, i giocatori mettono la carta in cima al proprio mazzo su una pila centrale.
  • Se la carta è una carta numerica (2-10), il gioco passa semplicemente all'altro giocatore.
  • Se la carta è una carta di pagamento, si deve pagare una penale:
    • J → l'avversario deve pagare 1 carta
    • Q → l'avversario deve pagare 2 carte
    • K → l'avversario deve pagare 3 carte
    • A → l'avversario deve pagare 4 carte
  • Se il giocatore che sta pagando una penale scopre un'altra carta di pagamento, smette di pagare la penale. A quel punto l'altro giocatore deve pagare una penale in base alla nuova carta di pagamento.
  • Se la penale viene pagata per intero senza interruzioni, il giocatore che ha messo l'ultima carta di pagamento raccoglie la pila centrale e la mette in fondo al proprio mazzo. Sarà quel giocatore a iniziare il round successivo.
  • Se un giocatore resta senza carte e non riesce a giocarne una (mentre sta pagando una penale o quando è il suo turno), l'altro giocatore raccoglie la pila centrale.
  • Il momento in cui un giocatore raccoglie le carte dalla pila centrale si chiama trick.
  • Se dopo un trick un giocatore ha in mano tutte le carte, la partita finisce.
  • La partita entra in un loop non appena i mazzi sono identici a come erano prima nel corso della partita, senza contare le carte numeriche!

Esempi

Un piccolo esempio di partita che finisce.

Round Giocatore A Giocatore B Pila Penale dovuta
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 Giocatore B: 4
1 7 8 Q 10 5 6 K 9 J 2 3 A 4 Giocatore B: 3
1 7 8 Q 10 6 K 9 J 2 3 A 4 5 Giocatore B: 2
1 7 8 Q 10 K 9 J 2 3 A 4 5 6 Giocatore B: 1
1 7 8 Q 10 9 J 2 3 A 4 5 6 K Giocatore A: 3
1 8 Q 10 9 J 2 3 A 4 5 6 K 7 Giocatore A: 2
1 Q 10 9 J 2 3 A 4 5 6 K 7 8 Giocatore A: 1
1 10 9 J 2 3 A 4 5 6 K 7 8 Q Giocatore B: 2
1 10 J 2 3 A 4 5 6 K 7 8 Q 9 Giocatore B: 1
1 10 - 2 3 A 4 5 6 K 7 8 Q 9 J Giocatore 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

Questo è un piccolo esempio di partita che entra in un loop.

Round Giocatore A Giocatore B Pila Penale dovuta
1 J 2 3 4 J 5 - -
1 2 3 4 J 5 J Giocatore 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 Giocatore 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 Giocatore B: 1
3 4 J 3 5 J 2 -
4 4 5 J 2 J 3 - -

L'inizio del round 4 coincide con l'inizio del round 2. Ricorda: il valore delle carte numeriche non conta.

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

Il tuo compito

  • Usando l'input, simula la partita seguendo le regole qui sopra.
  • Determina le seguenti informazioni sulla partita:
    • Status: "finished" o "loop"
    • Cards: numero totale di carte giocate durante la partita
    • Tricks: numero di volte in cui la pila centrale è stata raccolta
Advanced

Per chi vuole affrontare una sfida più entusiasmante, la caccia ad altri record per la partita più lunga che finisce è ancora aperta. Ci sono 653.534.134.886.878.245.000 (circa 654 trilioni) possibilità e non le abbiamo ancora calcolate tutte!

Pro e contro di from_quote

La funzione Card.from_quote esegue il parsing di una stringa in una Card. Questo permette di scrivere codice come questo:

card : Card
card = "Q"

Questo codice chiama implicitamente Card.from_quote("Q") in fase di compilazione. Eseguire codice in fase di compilazione con questo approccio (o con altri metodi descritti qui sotto) può potenzialmente velocizzare il programma, dato che non deve gestire il parsing e la validazione dei dati a runtime. Inoltre, garantisce che i dati non validi (come un formato di data errato) vengano rifiutati in fase di compilazione invece che a runtime, rendendo l'app più robusta. Infine, rende il codice bello e leggibile. Per esempio, ecco uno dei test di questo esercizio:

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

Tuttavia, chi legge questo codice potrebbe pensare che la funzione simulate_game accetti un record contenente due List(Str), quando in realtà è un record contenente due List(Card). Questo può portare a confusione ed errori. Per questo motivo, potresti preferire il formato leggermente più lungo ma più esplicito: "Q".Card, che chiama anch'esso from_quote in fase di compilazione.

Nei tuoi programmi, potresti preferire usare from_str:

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

Puoi anche chiamare from_str (o qualsiasi altra funzione pura) in fase di compilazione, come qui:

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

Nota: mettere questa definizione al livello superiore la fa eseguire in fase di compilazione (dato che la funzione è pura e il suo input è noto in fase di compilazione). All'interno di una funzione, puoi usare from_str a runtime.

Suggerimenti

Mostra i suggerimenti

Usa match con [] e [card, .. as rest] per distinguere un mazzo vuoto dalla sua prossima carta e dalle carte rimanenti. Gli aggiornamenti dei record come { ..state, pile: new_pile } possono mantenere esplicite le transizioni di gioco senza stato mutabile. Un Set può memorizzare tuple o liste come chiavi quando si tiene traccia degli stati di gioco già visti.

Modifica tramite GitHub Il link si apre in una nuova finestra o scheda
Roc Exercism

Vuoi iniziare Camicia?

Iscriviti a Exercism per imparare e padroneggiare Roc con 135 esercizi e il mentoring di persone reali, tutto gratis.