On dispose un tas d’allumettes sur la table.
Les deux joueurs ramassent, à leur tour, 2 ou 3 allumettes.
Celui qui prend la dernière allumette a gagné. S’il reste une seule allumette,
il y a égalité.
Comme ce projet ainsi que les suivants qui seront à réaliser dans une certaine autonomie demande de gérer beaucoup d’éléments, il est difficile de savoir par où débuter. Je vous conseille dans ce cas de vous appuyer sur la méthode de Zelle exposée au tout début d’année dans le (1.1).
Analyser le problème : bien comprendre les règles du jeu quitte à réaliser quelques parties.
Spécifier le programme :
Problème: déroulé du jeu de Nim
|---Entrées/paramètres : pions/allumettes (représentation ?, nombre initial ?), plateau de jeu (représentation ?)
|---Sortie : déterminer l'état final du jeu (vainqueur ? égalité ?)
|---Effet de bord : afficher l'état intermédiaire du jeu (à chaque tour, donner le nom du joueur et le nombre d'allumettes, afficher le plateau)
Concevoir le programme
Ce qui suit est un choix personnel (il peut différer sur certains points notamment sur l’affichage de certains éléments, l’ordre d’affichage, …).
Les deux joueurs seront numérotés : 1 et 2.
C’est un choix personnel qui peut évoluer en fonction du développement du projet.
PION = {0: (" ", 0), 1: ("|", 1)}
Choisir un dictionnaire est très souvent judicieux car cela permet de gérer plusieurs paramètres dans le même temps. Pensez au jeu où votre personnage dispose de points de vie, de points de magie et éventuellement d’autres caractéristiques.
Remarque : les personnages partagent souvent un même mode de fonctionnement. Les chanceux qui continueront NSI l’an prochain aborderont la programmation objet. Elle permettra entre autre de gagner un temps phénoménal lors du développement de tels personnages.
a) Réalisez une fonction preparer()
qui prend en paramètre le nombre d’allumettes
(25 par défaut). Cette fonction demande à l’utilisateur le nombre d’allumettes
qu’il veut sur la table en début de partie. Elle affiche ensuite un message de
confirmation. Elle retourne enfin ce nombre d’allumettes.
>>> preparer()
Veuillez indiquer le nombre d allumettes présentes au départ (25 par défaut).
15
Sur le plateau, sont donc diposées 15 allumettes.
b) Écrire une fonction creer_plateau()
qui prend en paramètre le nombre
d’allumettes, qui retourne le plateau de jeu sous la forme d’une liste composée
uniquement de l’élément PION[1]
et qui a pour effet de bord de demander le
nombre d’allumettes désiré.
c) Proposez une fonction afficher_plateau()
qui prend en paramètre le plateau
de jeu, qui ne retourne rien, mais qui a pour effet de bord d’afficher ce
plateau de jeu en cours.
>>> grille = [PION[1] for _ in range(13)]+[PION[0] for _ in range(12)]
>>> afficher_plateau(grille)
||||| ||||| |||
d) Réalisez une fonction jeu_fini()
qui prend en paramètre le nombre
d’allumettes restant sur la table. Elle retourne True
si la partie continue ou
False
si la partie est terminée.
>>> jeu_fini(10)
False
>>> jeu_fini(1)
True
e) Réalisez une fonction changement_joueur()
qui prend en paramètre le numéro du
joueur (1 ou 2). Cette fonction retourne le numéro du joueur suivant.
>>> changement_joueur(1)
2
f) Proposer une fonction nb_allumettes()
qui prend en paramètres le plateau de
jeu et retourne le nombre d’allumettes dans ce plateau de jeu.
>>> grille = [PION[1] for _ in range(13)]+[PION[0] for _ in range(12)]
>>> nb_allumettes(grille)
13
g) Réalisez une fonction possibilites()
qui prend en paramètre le nombre
d’allumettes restant sur la table. Cette fonction retourne la liste de tous les
coups possibles.
>>> possibilites(21)
[2, 3]
>>> possibilites(2)
[2]
h) Réalisez une fonction choix_humain()
qui prend en paramètre le plateau de
jeu. Cette fonction demande au joueur le nombre d’allumettes qu’il veut retirer
de la table tout en contrôlant que ce choix est possible, puis finalement
retourne le nombre effectivement choisi par le joueur.
i) Réalisez une fonction etat_suivant()
qui prend en paramètre le plateau de
jeu, le choix d’allumettes à enlever effectué par un joueur. Cette fonction
retourne le nombre d’allumettes restant sur la table après avoir enlevé celles
choisies par le joueur.
>>> grille1 = [PION[1] for _ in range(17)]+[PION[0] for _ in range(8)]
>>> grille2 = etat_suivant(grille1, 3)
>>> grille2 == [PION[1] for _ in range(14)]+[PION[0] for _ in range(11)]
True
j) Réalisez une fonction etat_bilan()
qui prend en paramètre le nombre
d’allumettes restant sur la table et le numéro du joueur en cours (1 ou 2).
Cette fonction a pour effet de bord d’afficher le nombre d’allumettes restant si
la partie continue, “Egalité” en cas de match nul, le nom du joueur gagnant si
c’est la cas.
>>> etat_bilan(1,1)
Egalité
>>> etat_bilan(17, 1)
Il reste 17 allumette(s) sur la table.
>>> etat_bilan(0, 2)
La partie est remportée par le joueur 2.
k) Réalisez enfin une fonction executer()
qui prend en paramètre le nombre
d’allumettes. Cette fonction fera fonctionner le jeu en utilisant toutes les
fonctions précédentes.
Seulement lorsque la version 1 a été finie et qu’elle a été vérifiée par moi-même !
Voici les fonctions à modifier (preparer()
et executer()
) et nouvelles à
ajouter:
def preparer(allumettes=25):
"""
Retourne le mode de jeu, la profondeur (ici imposée) et la grille dont les
éléments sont les pions (allumettes) au début de la partie
:param allumettes :(int)
:return : (list) la liste donnant le mode, la profondeur et la grille
:CU : allumettes doit être un entier positif
"""
mode = mode_jeu()
prof = profondeur(mode)
grille = creer_plateau(allumettes)
return [mode, prof, grille]
def mode_jeu():
"""
Choix du mode de jeu : seul contre l'ordinateur, ou à deux joueurs
:param : None
:return : (tuple) couple (nb joueur, ordre des joueurs)
"""
# PvP ou PvE ?
print("Voulez-vous jouer à deux joueurs, ou seul contre l'ordinateur ?")
try:
nb_joueur = int(input("Pour jouer à deux, tapez 2 ; pour jouer seul, taper 1 : "))
except ValueError:
return mode_jeu()
if not( nb_joueur in {1, 2} ):
return mode_jeu()
# ordre joueur/ordinateur
if nb_joueur == 1:
ordre = input("Souhaitez-vous commencer ? o/n ?")
while not( ordre in {"o", "n"} ):
ordre = input("Souhaitez-vous commencer ? o/n ?")
else:
ordre = None
return nb_joueur, ordre
def profondeur(mode):
"""
Retourne la profondeur fournie par l'utilisateur
:param mode: (tuple (int, str)) (nb joueur, ordre des joueurs)
:return: (int) la profondeur est fixé à 1
"""
return 1
def evaluation(grille, prof, joueur):
"""
fonction d'évaluation du plateau de jeu pour la méthode minMax
:param etat_actuel : (int) nb d'allumettes sur le plateau de jeu au moment
où doit l'ordinateur va jouer
:param joueur: (int) numéro du joueur (1 ou 2)
:return : (int)
>>> grille = [PION[1] for _ in range(10)]
>>> evaluation(grille, 1, 1)
-1
>>> evaluation(grille, 1, 2)
1
>>> grille = [PION[1] for _ in range(9)]
>>> evaluation(grille, 1, 1)
0
>>> evaluation(grille, 1, 2)
0
>>> grille = [PION[1] for _ in range(8)]
>>> evaluation(grille, 1, 1)
1
>>> evaluation(grille, 1, 2)
-1
"""
allumettes = nb_allumettes(grille)
if allumettes % 5 == 2 or allumettes % 5 == 3:
return (-1)**(joueur+1) #1 pour Max et -1 pour Min
elif allumettes % 5 == 1 or allumettes % 5 == 4:
return 0
else:
return (-1)**joueur #-1 pour Max et 1 pour Min
def min_max(etat_actuel, prof, joueur):
"""
Définit la méthode min_max utilisée par l'ordinateur pour choisir le coup
suivant le plus fort de son point de vue
:param etat_actuel: (list) état du plateau actuel de jeu avant toute action
:param prof: (int) profondeur pour min_max
:param joueur: (int) numéro du joueur (1 ou 2)
:return: (tuple) valeur du plateau de jeu, choix de l'ordinateur associé
"""
autre_joueur = 3 - joueur #a voir sur Nim
if jeu_fini(etat_actuel) or prof == 0:
return evaluation(etat_actuel, prof, joueur) * (-1)**(joueur + 1), 0
else:
if joueur == 1: #Max
valeur, choix = max( [ (min_max(suivant(etat_actuel, choix, joueur),
prof - 1, autre_joueur)[0], choix)
for choix in possibilites(etat_actuel) ] )
else: #Min
valeur, choix = min( [ (min_max(suivant(etat_actuel, choix, joueur),
prof - 1, autre_joueur)[0], choix)
for choix in possibilites(etat_actuel) ] )
return valeur, choix
def executer(liste): #liste=[mode, prof, grille]
"""
Fait tourner le jeu de Nim
:param liste : (list) contient le nombre d'allumettes au début du jeu,
le mode de jeu (seul contre l'ordinateur ou à 2 joueurs),
la profondeur pour la méthode minMax.
:return : jeu
"""
mode, prof, grille = liste[0], liste[1], liste[2]
joueur = 2
afficher_plateau(grille)
while not( jeu_fini(grille) ):
joueur = changement_joueur(joueur)
choix = choisir(mode, prof, grille, joueur)
grille = suivant(grille, choix, joueur)
afficher_choix(choix, joueur)
bilan(grille, joueur)
Ne reste plus qu’à jouer !
objets_initiaux = preparer()
executer(objets_initiaux)