coeur = "♥"
treffle = "♣"
pique = "♠"
carreau = "♦"
couleurs = [coeur, treffle, pique, carreau]
class Carte():
def __init__(self, valeur, couleur):
assert 1 <= valeur <= 13, "Erreur : valeur doit etre entre 1 et 13."
self.valeur = valeur
assert couleur in couleurs, "Erreur : couleur doit etre dans la liste {}.".format(couleurs)
self.couleur = couleur
def __str__(self):
val = str(self.valeur)
if self.valeur > 10:
val = {11: "V" , 12: "Q" , 13: "K"}[self.valeur]
return "{:>2}{}".format(val, self.couleur)
__repr__ = __str__
def val(self):
return self.valeur
def valeur_main(liste_carte):
return sum(carte.val() for carte in liste_carte)
import random
def nouveau_jeu():
jeu = [
Carte(valeur, couleur)
for valeur in range(1, 13+1)
for couleur in couleurs
]
random.shuffle(jeu)
return jeu
nouveau_jeu()[:5]
valeur_main(_)
[ 7♣, 7♥, Q♦, 8♣, 1♠]
35
Pour représenter la fin du jeu :
class FinDuneManche(Exception):
pass
class FinDunePartie(Exception):
pass
Pour représenter une action choisie par une personne :
class action():
def __init__(self, typeAction="piocher", choix=None):
assert typeAction in ["piocher", "choisir", "Jap Jap !"]
self.typeAction = typeAction
assert choix is None or choix in [0, 1, 2, 3, 4]
self.choix = choix
def __str__(self):
if self.est_piocher(): return "Piocher"
elif self.est_japjap(): return "Jap Jap !"
elif self.est_choisir(): return "Choisir #{}".format(self.choix)
def est_piocher(self):
return self.typeAction == "piocher"
def est_choisir(self):
return self.typeAction == "choisir"
def est_japjap(self):
return self.typeAction == "Jap Jap !"
action_piocher = action("piocher")
action_japjap = action("Jap Jap !")
action_choisir0 = action("choisir", 0)
action_choisir1 = action("choisir", 1)
action_choisir2 = action("choisir", 2)
action_choisir3 = action("choisir", 3)
action_choisir4 = action("choisir", 4)
Pour savoir si une suite de valeurs est bien continue :
def suite_valeurs_est_continue(valeurs):
vs = sorted(valeurs)
differences = [ vs[i + 1] - vs[i] for i in range(len(vs) - 1) ]
return all([d == 1 for d in differences])
suite_valeurs_est_continue([5, 6, 7])
suite_valeurs_est_continue([5, 7, 8])
True
False
Pour valider un coup choisie par une personne :
def valide_le_coup(jetees):
if jetees is None or not (1 <= len(jetees) <= 5):
return False
# coup valide si une seule carte !
elif len(jetees) == 1:
return True
# si plus d'une carte
elif len(jetees) >= 2:
couleurs_jetees = [carte.couleur for carte in jetees]
valeurs_jetees = sorted([carte.valeur for carte in jetees])
# coup valide si une seule couleur et une suite de valeurs croissantes et continues
if len(set(couleurs_jetees)) == 1:
return suite_valeurs_est_continue(valeurs_jetees)
# coup valide si une seule valeur et différentes couleurs
elif len(set(valeurs_jetees)) == 1:
return len(set(couleurs_jetees)) == len(couleurs_jetees)
return False
Exemples de coups valides :
valide_le_coup([Carte(4, coeur)])
True
valide_le_coup([Carte(4, coeur), Carte(5, coeur)])
True
valide_le_coup([Carte(4, coeur), Carte(5, coeur), Carte(3, coeur)])
True
valide_le_coup([Carte(4, coeur), Carte(5, coeur), Carte(3, coeur), Carte(2, coeur), Carte(6, coeur)])
True
valide_le_coup([Carte(4, coeur), Carte(4, carreau)])
True
valide_le_coup([Carte(4, coeur), Carte(4, carreau), Carte(4, pique)])
True
valide_le_coup([Carte(4, coeur), Carte(4, carreau), Carte(4, pique), Carte(4, treffle)])
True
Exemples de coups pas valides :
valide_le_coup([Carte(4, coeur), Carte(9, coeur)])
False
valide_le_coup([Carte(4, coeur), Carte(4, coeur), Carte(3, coeur)])
False
valide_le_coup([Carte(4, coeur), Carte(12, carreau)])
False
valide_le_coup([Carte(4, coeur), Carte(4, carreau), Carte(4, pique)])
True
valide_le_coup([Carte(4, coeur), Carte(4, carreau), Carte(4, pique), Carte(4, treffle)])
True
On va utiliser les widgets ipython pour construire le jeu interactif !
# Voir https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Asynchronous.html#Waiting-for-user-interaction
%gui asyncio
import asyncio
def wait_for_change(widget, value):
future = asyncio.Future()
def getvalue(change):
# make the new value available
future.set_result(change.new)
widget.unobserve(getvalue, value)
widget.observe(getvalue, value)
return future
import ipywidgets as widgets
from IPython.display import display
style = {
'description_width': 'initial',
}
style2boutons = {
'description_width': 'initial',
'button_width': '50vw',
}
style3boutons = {
'description_width': 'initial',
'button_width': '33vw',
}
style4boutons = {
'description_width': 'initial',
'button_width': '25vw',
}
style5boutons = {
'description_width': 'initial',
'button_width': '20vw',
}
Pour savoir quoi jouer :
def piocher_ou_choisir_une_carte_visible():
return widgets.ToggleButtons(
options=["Une carte dans la pioche ", "Une carte du sommet de la défausse "],
index=0,
tooltips=["invisible", "visibles"],
icons=["question", "list-ol"],
description="Action ?",
style=style4boutons,
)
bouton = piocher_ou_choisir_une_carte_visible()
display(bouton)
print("Choix :", bouton.index)
ToggleButtons(description='Action ?', icons=('question', 'list-ol'), options=('Une carte dans la pioche ', 'Un…
Choix : 0
Pour savoir quoi jeter :
exemple_de_main = [Carte(10, coeur), Carte(11, coeur), Carte(11, pique)]
exemple_de_main
[10♥, V♥, V♠]
def faire_japjap(main):
return widgets.ToggleButton(
value=False,
description="Jap Jap ? ({})".format(valeur_main(main)),
button_style="success",
tooltip="Votre main vaut moins de 5 points, donc vous pouvez terminer la partie !",
icon="check",
style=style,
)
b = faire_japjap(exemple_de_main)
display(b)
print("Choix :", b.value)
ToggleButton(value=False, button_style='success', description='Jap Jap ? (32)', icon='check', style=Descriptio…
Choix : False
def quoi_jeter(main):
return widgets.SelectMultiple(
options=main,
index=[0],
description="Quoi jeter ?",
style=style,
)
b = quoi_jeter(exemple_de_main)
display(b)
print("Choix :", b.index)
SelectMultiple(description='Quoi jeter ?', index=(0,), options=(10♥, V♥, V♠), style=DescriptionStyle(descrip…
Choix : (0,)
from IPython.display import display
def valider_action():
return widgets.ToggleButton(description="Valider l'action ?")
Pour savoir quoi piocher :
exemple_de_visibles = [Carte(11, pique), Carte(10, treffle)]
exemple_de_visibles
[ V♠, 10♣]
def quoi_prendre(visibles):
return widgets.ToggleButtons(
options=visibles,
#index=0,
description="Prendre quelle carte du sommet ?",
style=style,
)
quoi_prendre(exemple_de_visibles)
ToggleButtons(description='Prendre quelle carte du sommet ?', options=( V♠, 10♣), style=ToggleButtonsStyle(des…
On va tricher et afficher les cartes avec display(Markdown(...))
plutôt que print
, pour les avoir en couleurs.
from IPython.display import Markdown
print("[Alice] Cartes en main : [ 6♦, 5♠, V♣, V♠, 1♣]")
# avec de la couleurs
display(Markdown("[Alice] Cartes en main : [ 6♦, 5♠, V♣, V♠, 1♣]"))
[Alice] Cartes en main : [ 6♦, 5♠, V♣, V♠, 1♣]
[Alice] Cartes en main : [ 6♦, 5♠, V♣, V♠, 1♣]
Maintenant on peut tout combiner.
async def demander_action(visibles=None, main=None, stockResultat=None):
display(Markdown("- Main : {} (valeur = {})".format(main, valeur_main(main))))
display(Markdown("- Sommet de la défausse {}".format(visibles)))
fait_japjap = False
# 1.a. si on peut faire jap jap, demander si on le fait ?
if valeur_main(main) <= 5:
print("Vous pouvez faire Jap Jap !")
bouton3 = faire_japjap(main)
validation = valider_action()
display(widgets.VBox([bouton3, validation]))
await wait_for_change(validation, 'value')
# print(" ==> Choix :", bouton3.value)
if bouton3.value:
fait_japjap = True
typeAction = "Jap Jap !"
jetees = None
# 1.b. quoi jouer
if not fait_japjap:
bouton1 = piocher_ou_choisir_une_carte_visible()
validation = valider_action()
display(widgets.VBox([bouton1, validation]))
await wait_for_change(validation, 'value')
piocher = bouton1.index == 0
# print(" ==> Choix :", bouton1.value)
# 2.a. si piocher, rien à faire pour savoir quoi piocher
if piocher:
print("Okay, vous piochez.")
typeAction = "piocher"
choix = None
# 2.b. si choisir carte
else:
typeAction = "choisir"
print("Okay, vous choisissez dans le sommet de la défausse.")
if len(visibles) > 1:
bouton2 = quoi_prendre(visibles)
validation = valider_action()
display(widgets.VBox([bouton2, validation]))
await wait_for_change(validation, 'value')
# print(" ==> Choix :", bouton2.index)
choix = bouton2.index
else:
choix = 0
# 3. choisir quoi jeter
if typeAction != "Jap Jap !":
if len(main) > 1:
pas_encore_de_coup = True
jetees = None
bouton4 = quoi_jeter(main)
validation = valider_action()
display(widgets.VBox([bouton4, validation]))
while pas_encore_de_coup or not valide_le_coup(jetees):
await wait_for_change(validation, 'value')
# print(" ==> Choix :", bouton4.index)
jetees = bouton4.index
pas_encore_de_coup = False
if not valide_le_coup(jetees):
print("ERREUR ce coup n'est pas valide, on ne peut pas se débarasser de cet ensemble de cartes {}.".format(jetees))
else:
jetees = 0
action_choisie = action(typeAction=typeAction, choix=choix)
if stockResultat is not None:
stockResultat["action_choisie"] = action_choisie
stockResultat["jetees"] = jetees
return action_choisie, jetees
if False:
stockResultat = {"action_choisie": None, "jetees": None}
asyncio.ensure_future(
demander_action(
visibles=exemple_de_visibles,
main=exemple_de_main,
stockResultat=stockResultat,
)
)
stockResultat
def demander_action_et_donne_resultat(visibles=None, main=None):
stockResultat = {"action_choisie": None, "jetees": None}
asyncio.ensure_future(
demander_action(
visibles=visibles,
main=main,
stockResultat=stockResultat,
)
)
return stockResultat["action_choisie"], stockResultat["jetees"]
Maintenant on peut représenter un état du jeu.
# on peut changer ici pour jouer moins longtemps !
scoreMax = 90
scoreMax = 10
class EtatJeu():
def __init__(self, nbPersonnes=2, nomsPersonnes=None,
scoreMax=scoreMax, malusContreJapJap=25, nbCartesMax=5):
assert 2 <= nbPersonnes <= 5, "Le nombre de personnes pouvant jouer doit etre entre 2 et 5."
self.nbPersonnes = nbPersonnes
self.nomsPersonnes = nomsPersonnes
self.scoreMax = scoreMax
self.malusContreJapJap = malusContreJapJap
self.nbCartesMax = nbCartesMax
# on initialise le stockage interne
self.personnes = [personne for personne in range(nbPersonnes)]
self.scores = [
0 for personne in self.personnes
]
self.mains = [
[ ] for personne in self.personnes
]
self.visibles = []
self.jeu = nouveau_jeu()
def montrer_information_visibles(self):
print("- Nombre de carte dans la pioche :", len(self.jeu))
print("- Cartes visibles au sommet de la défausse :", len(self.visibles))
for personne in self.personnes:
nom = self.nomsPersonnes[personne] if self.nomsPersonnes is not None else personne
main = self.mains[personne]
score = self.scores[personne]
print(" + Personne {} a {} carte{} en main, et un score de {}.".format(
nom, len(main), "s" if len(main) > 1 else "", score)
)
def montrer_information_privee(self, personne=0):
main = self.mains[personne]
nom = self.nomsPersonnes[personne] if self.nomsPersonnes is not None else personne
display(Markdown("[{}] Carte{} en main : {}".format(nom, "s" if len(main) > 1 else "", main)))
# --- Mécanique de pioche et distribution initiale
def prendre_une_carte_pioche(self):
if len(self.jeu) <= 0:
raise FinDuneManche
premiere_carte = self.jeu.pop(0)
return premiere_carte
def debut_jeu(self):
self.distribuer_mains()
premiere_carte = self.prendre_une_carte_pioche()
self.visibles = [premiere_carte]
def donner_une_carte(self, personne=0):
premiere_carte = self.prendre_une_carte_pioche()
self.mains[personne].append(premiere_carte)
def distribuer_mains(self):
self.mains = [
[ ] for personne in self.personnes
]
premiere_personne = random.choice(self.personnes)
self.personnes = self.personnes[premiere_personne:] + self.personnes[:premiere_personne]
for nb_carte in range(self.nbCartesMax):
for personne in self.personnes:
self.donner_une_carte(personne)
# --- Fin d'une manche
def fin_dune_manche(self):
self.jeu = nouveau_jeu()
self.debut_jeu()
# --- Enchainer les tours de jeux
async def enchainer_les_tours(self):
try:
indice_actuel = 0
while len(self.jeu) > 0:
# dans la même manche, on joue chaque tour, pour la personne actuelle
personne_actuelle = self.personnes[indice_actuel]
# 1. on affiche ce qui est public, et privé
self.montrer_information_visibles()
self.montrer_information_privee(personne_actuelle)
# 2. on demande l'action choisie par la personne
# action_choisie, jetees = demander_action_et_donne_resultat(
action_choisie, jetees = await demander_action(
visibles = self.visibles,
main = self.mains[personne_actuelle],
)
# 3. on joue l'action
self.jouer(
personne = personne_actuelle,
action = action_choisie,
indices = jetees,
)
# personne suivante
indice_actuel = (indice_actuel + 1) % self.nbPersonnes
if len(self.jeu) <= 0:
print("\nIl n'y a plus de cartes dans la pioche, fin de la manche sans personne qui gagne.")
raise FinDuneManche
except FinDuneManche:
print("\nFin d'une manche.")
fin_dune_manche()
except FinDunePartie:
print("\n\nFin d'une partie.")
# --- Un tour de jeu
def jouer(self, personne=0, action=action_piocher, indices=None):
print(" ? Personne {} joue l'action {} avec les indices {} ...".format(personne, action, indices)) # DEBUG
if indices is not None:
jetees = [ self.mains[personne][indice] for indice in indices ]
assert valide_le_coup(jetees)
# et on en prend une nouvelle
if action.est_piocher():
# soit celle face cachée en sommet de pioche
premiere_carte = self.prendre_une_carte_pioche()
display(Markdown("=> Vous piochez la carte {}.".format(premiere_carte)))
self.mains[personne].append(premiere_carte)
if action.est_choisir():
# soit une des cartes précedemment visibles
choix = action.choix
carte_choisie = self.visibles.pop(choix)
display(Markdown("=> Vous récupérez la carte {}.".format(carte_choisie)))
self.mains[personne].append(carte_choisie)
if action.est_japjap():
# on vérifie que cette personne a bien un Jap Jap !
valeur_du_premier_japjap = valeur_main(self.mains[personne])
assert 1 <= valeur_du_premier_japjap <= 5
gagnante = personne
display(Markdown("=> Vous faites un Jap Jap, valant {} point{}.".format(valeur_du_premier_japjap, "s" if valeur_du_premier_japjap > 1 else "")))
contre_JapJap = False
# on vérifie les valeurs des mains des autres personnes
valeurs_mains = [valeur_main(main) for main in self.mains]
plus_petite_valeur = min([valeurs_mains[autre_personne] for autre_personne in [ p for p in personnes if p != gagnante ]])
if plus_petite_valeur < valeur_du_premier_japjap:
print("CONTRE JAP JAP !")
# si une personne a un jap jap plus petit, la personne ne gagne pas
contre_JapJap = True
# la personne gagnante est la première (ordre du jeu) à obtenir le jap jap
# de valeur minimale, et en cas d'égalité c'est la personne obtenant
# cette valeur en le nombre minimal de cartes !
gagnantes = [ p for p in personnes if valeurs_mains[p] == plus_petite_valeur ]
print("Les autres personnes ayant un Jap Jap de plus petite valeur sont {} !".format(gagnantes))
nombre_min_carte = min([len(self.mains[p]) for p in gagnantes])
gagnante = min([p for p in gagnantes if len(self.mains[p]) == nombre_min_carte])
print("La personne gagnant la manche est {}.".format(gagnante))
# on marque les scores
print("\nOn marque les scores !")
print("==> La personne {} a gagné ! Avec un Jap Jap de valeur {} !".format(gagnante, plus_petite_valeur))
for autre_personne in [ p for p in personnes if p != gagnante ]:
marque_point = valeur_main(self.mains[autre_personne])
print("- La personne {} n'a pas gagné, et marque {} points".format(autre_personne, marque_point))
self.scores[autre_personne] += marque_point
# si la personne s'est prise un contre jap jap, elle marque +25 et pas son total de cartes en main
print("- La personne {} n'a pas gagné et a subi un CONTRE JAP JAP ! Elle marque +25 points.")
if contre_JapJap:
self.scores[personne] -= valeur_main(self.mains[personne])
self.scores[personne] += self.malusContreJapJap
print("\nA la fin de cette manche :")
for personne in self.personnes:
nom = self.nomsPersonnes[personne] if self.nomsPersonnes is not None else personne
score = self.scores[personne]
print(" + Personne {} a un score de {}.".format(nom, score))
# si un score est >= 90
if max(self.scores) >= self.scoreMax:
# quelqu'un a perdu cette partie !
for personne in personnes:
score = self.scores[personne]
if score == max(self.scores):
nom = self.nomsPersonnes[personne] if self.nomsPersonnes is not None else personne
print("\n==> La personne {} a perdu, avec un score de {}.".format(nom, score))
raise FinDunePartie
raise FinDuneManche
# on pose les cartes jetées
self.visibles = jetees
# et on enlève les cartes jetées de sa main
nouvelle_main = self.mains[personne]
for carte_jetee in jetees:
nouvelle_main.remove(carte_jetee)
# et ça continue
jeu = EtatJeu(nomsPersonnes=["Alice", "Bob"])
jeu.debut_jeu()
import asyncio
asyncio.ensure_future(jeu.enchainer_les_tours())
<Task pending coro=<EtatJeu.enchainer_les_tours() running at <ipython-input-44-ef23017ab532>:72>>
- Nombre de carte dans la pioche : 41 - Cartes visibles au sommet de la défausse : 1 + Personne Bob a 5 cartes en main, et un score de 0. + Personne Alice a 5 cartes en main, et un score de 0.
[Bob] Cartes en main : [ 9♠, 8♠, 1♥, 5♥, K♦]
VBox(children=(ToggleButtons(description='Action ?', icons=('question', 'list-ol'), options=('Une carte dans l…
Okay, vous choisissez dans le sommet de la défausse.
VBox(children=(SelectMultiple(description='Quoi jeter ?', index=(0,), options=( 9♠, 8♠, 1♥, 5♥, K♦), style…
? Personne 1 joue l'action Choisir #0 avec les indices (4,) ...
=> Vous récupérez la carte 3♦.
- Nombre de carte dans la pioche : 41 - Cartes visibles au sommet de la défausse : 1 + Personne Bob a 5 cartes en main, et un score de 0. + Personne Alice a 5 cartes en main, et un score de 0.
[Alice] Cartes en main : [ 9♥, 8♦, Q♣, 9♣, 2♦]
VBox(children=(ToggleButtons(description='Action ?', icons=('question', 'list-ol'), options=('Une carte dans l…
Okay, vous piochez.
VBox(children=(SelectMultiple(description='Quoi jeter ?', index=(0,), options=( 9♥, 8♦, Q♣, 9♣, 2♦), style…
? Personne 0 joue l'action Piocher avec les indices (2,) ...
=> Vous piochez la carte 6♥.
- Nombre de carte dans la pioche : 40 - Cartes visibles au sommet de la défausse : 1 + Personne Bob a 5 cartes en main, et un score de 0. + Personne Alice a 5 cartes en main, et un score de 0.
[Bob] Cartes en main : [ 9♠, 8♠, 1♥, 5♥, 3♦]
VBox(children=(ToggleButtons(description='Action ?', icons=('question', 'list-ol'), options=('Une carte dans l…
Okay, vous piochez.
VBox(children=(SelectMultiple(description='Quoi jeter ?', index=(0,), options=( 9♠, 8♠, 1♥, 5♥, 3♦), style…