Camicia

Camicia

Moyen

Introduction

Par un après-midi pluvieux, tu es assis à la table de la cuisine et tu joues aux cartes avec ta grand-mère. Le jeu est sa version personnelle de Camicia.

Au début, cela ressemble à une simple partie amicale : des cartes abattues, des rires autour de la table, et de temps en temps un sourire victorieux de Nonna. Mais à mesure que la partie s'éternise, quelque chose d'étrange se produit. Les mêmes cartes ne cessent de revenir. Tu joues carte après carte, et pourtant la fin ne semble jamais arriver.

Tu commences à te demander. Cette partie va-t-elle un jour se terminer ? Ou pourrait-on jouer ainsi pour toujours ?

Plus tard, poussé par la curiosité, tu fais des recherches en ligne et, à ta grande surprise, tu découvres que ce qui s'est passé n'était pas qu'une question de malchance. Toi et ta grand-mère êtes peut-être tombés sur l'une des plus longues séquences possibles ! Soudain, te voilà accro. Ce qui avait commencé comme une simple partie s'est transformé en quête : combien de temps un tel jeu peut-il vraiment durer ? Peux-tu trouver une séquence encore plus longue que celle que tu as jouée à la table de la cuisine ? Peut-être même assez longue pour établir un nouveau record du monde ?

Et donc, armé de rien d'autre qu'un jeu de cartes et d'un peu d'ingéniosité algorithmique, tu décides de mener l'enquête...

Instructions

Dans cet exercice, tu vas simuler un jeu très proche du jeu de cartes classique Camicia. Ton programme recevra la configuration initiale des paquets de deux joueurs et devra simuler la partie jusqu'à ce qu'elle se termine (ou détecter qu'elle ne se terminera jamais).

Règles

  • Le paquet est réparti entre deux joueurs. Les cartes d'un joueur se lisent de gauche à droite, la carte la plus à gauche étant le dessus du paquet.
  • Un tour consiste, pour les deux joueurs, à jouer au moins une carte.
  • Les joueurs placent à tour de rôle la carte du dessus de leur paquet sur un tas central.
  • Si la carte est une carte numérotée (2-10), le jeu passe simplement à l'autre joueur.
  • Si la carte est une carte de paiement, une pénalité doit être payée :
    • J → l'adversaire doit payer 1 carte
    • Q → l'adversaire doit payer 2 cartes
    • K → l'adversaire doit payer 3 cartes
    • A → l'adversaire doit payer 4 cartes
  • Si le joueur qui paie une pénalité révèle une autre carte de paiement, ce joueur arrête de payer la pénalité. L'autre joueur doit alors payer une pénalité en fonction de la nouvelle carte de paiement.
  • Si la pénalité est entièrement payée sans interruption, le joueur qui a posé la dernière carte de paiement ramasse le tas central et le place sous son paquet. C'est alors lui qui commence le tour suivant.
  • Si un joueur n'a plus de cartes et ne peut pas en jouer une (que ce soit pendant qu'il paie une pénalité ou lorsque c'est à son tour), l'autre joueur ramasse le tas central.
  • Le moment où un joueur ramasse les cartes du tas central s'appelle un pli.
  • Si un joueur possède toutes les cartes après un pli, la partie se termine.
  • La partie entre dans une boucle dès que les paquets sont identiques à ce qu'ils étaient plus tôt dans la partie, sans compter les cartes numérotées !

Exemples

Un petit exemple de partie qui se termine.

Tour Joueur A Joueur B Tas Pénalité à payer
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 Joueur B : 4
1 7 8 Q 10 5 6 K 9 J 2 3 A 4 Joueur B : 3
1 7 8 Q 10 6 K 9 J 2 3 A 4 5 Joueur B : 2
1 7 8 Q 10 K 9 J 2 3 A 4 5 6 Joueur B : 1
1 7 8 Q 10 9 J 2 3 A 4 5 6 K Joueur A : 3
1 8 Q 10 9 J 2 3 A 4 5 6 K 7 Joueur A : 2
1 Q 10 9 J 2 3 A 4 5 6 K 7 8 Joueur A : 1
1 10 9 J 2 3 A 4 5 6 K 7 8 Q Joueur B : 2
1 10 J 2 3 A 4 5 6 K 7 8 Q 9 Joueur B : 1
1 10 - 2 3 A 4 5 6 K 7 8 Q 9 J Joueur 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

Voici un petit exemple de partie qui boucle.

Tour Joueur A Joueur B Tas Pénalité à payer
1 J 2 3 4 J 5 - -
1 2 3 4 J 5 J Joueur 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 Joueur 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 Joueur B : 1
3 4 J 3 5 J 2 -
4 4 5 J 2 J 3 - -

Le début du tour 4 est identique au début du tour 2. Rappel : la valeur des cartes numérotées n'a pas d'importance.

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

Ta tâche

  • À partir de l'entrée, simule la partie en suivant les règles ci-dessus.
  • Détermine les informations suivantes concernant la partie :
    • Status : "finished" ou "loop"
    • Cards : nombre total de cartes jouées pendant toute la partie
    • Tricks : nombre de fois où le tas central a été ramassé
Advanced

Pour celles et ceux qui veulent s'attaquer à un défi plus palpitant, la chasse aux autres records de la plus longue partie qui se termine est encore ouverte. Il y a 653 534 134 886 878 245 000 possibilités (environ 654 trillions), et on ne les a pas encore toutes calculées !

Les avantages et les inconvénients de from_quote

La fonction Card.from_quote analyse une string pour en faire une Card. Cela permet d'écrire du code comme celui-ci :

card : Card
card = "Q"

Ce code appelle implicitement Card.from_quote("Q") à la compilation. Exécuter du code à la compilation avec cette approche (ou avec les autres méthodes décrites ci-dessous) peut potentiellement accélérer le programme, puisqu'il n'a alors plus besoin de gérer l'analyse et la validation des données à l'exécution. De plus, cela garantit que les données invalides (comme un format de date incorrect) sont rejetées à la compilation plutôt qu'à l'exécution, ce qui rend ton application plus robuste. Enfin, cela rend le code agréable à lire. Par exemple, voici l'un des tests de cet exercice :

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

Cependant, une personne qui lit ce code pourrait supposer que la fonction simulate_game prend un enregistrement contenant deux List(Str), alors qu'en réalité il s'agit d'un enregistrement contenant deux List(Card). Cela peut prêter à confusion et provoquer des erreurs. Pour cette raison, tu préféreras peut-être la syntaxe un peu plus longue mais plus explicite : "Q".Card, qui appelle elle aussi from_quote à la compilation.

Dans tes propres programmes, tu préféreras peut-être utiliser from_str à la place :

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

Tu peux aussi appeler from_str (ou n'importe quelle autre fonction pure) à la compilation, comme ceci :

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

Remarque : placer cette définition au niveau supérieur fait qu'elle s'exécute à la compilation (puisque la fonction est pure et que son entrée est connue à la compilation). À l'intérieur d'une fonction, tu peux utiliser from_str à l'exécution.

Indices

Afficher les indices

Utilise match avec [] et [card, .. as rest] pour distinguer un paquet vide de sa carte suivante et des cartes restantes. Les mises à jour d'enregistrement telles que { ..state, pile: new_pile } permettent de garder des transitions de jeu explicites, sans état mutable. Un Set peut stocker des tuples ou des tableaux comme clés lorsqu'on suit les états de jeu déjà rencontrés.

Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet
Roc Exercism

Prêt à commencer Camicia ?

Inscris-toi sur Exercism pour apprendre et maîtriser Roc avec 135 exercices, et un vrai mentorat humain, le tout gratuitement.