Comme promis, on va reprendre notre jeu pour le transformer en jeu solo. Maintenant on sélectionnera un monstre et l’ordinateur en choisira un autre. À chaque tour, il sélectionnera aussi quelle attaque il souhaite nous infliger.
L'aléatoire à la rescousse !
On va donc intégrer quelques doses d’aléatoire dans notre jeu, à différents niveaux :
- Pour le choix de monstre, l’ordinateur réalisera un choix aléatoire, de même pour son nombre de PV.
- Pour connaître l’ordre d’attaque entre les deux monstres, on pourra faire un tirage aléatoire (savoir qui commence).
- À chaque tour, l’ordinateur sélectionnera une attaque aléatoire pour son monstre. Ce choix pourra être pondéré selon les dégâts infligés par chaque attaque.
Pour plus de généricité, on aimerait ne pas avoir à gérer l’ordinateur comme un cas spécifique, et donc ne pas faire de différence de traitement entre nos deux joueurs.
Pour cela, je vous propose de modifier la structure des joueurs (le dictionnaire tel que renvoyé par la fonction get_player
) pour y ajouter une fonction (un callback) associée à la clé 'chose_attack_func'
, qui pourra être appelée depuis la boucle de jeu pour demander au joueur de sélectionner une attaque.
Dans le cas d’un joueur humain, cette fonction fera appel à input
, et dans le cas de l’ordinateur elle opérera une sélection aléatoire. Mais la boucle de jeu n’en saura rien, ce sera totalement abstrait pour elle.
attack = player['chose_attack_func'](player)
apply_attack(player, opponent)
En bonus, on pourrait ajouter un choix pour permettre au 2ème joueur d’être un humain ou un robot, voire que les deux joueurs soient des ordinateurs pour les observer combattre.
Solution
Je vous propose la solution suivante, n’hésitez pas à regarder plus en détails le mécanisme de callback.
J’ai aussi utilisé une interface commune entre les modules players
et ia
, avec une fonction get_player
prenant un identifiant en argument et renvoyant un dictionnaire décrivant le joueur.
Pour la sélection du nombre de PV par l’ordinateur, j’ai utilisé une distribution normale, mais tout autre tirage serait correct.
Enfin, pour alléger le code, j’ai supprimé ce qui était relatif à la sauvegarde du jeu car ça n’est plus utile ici.
Animations
Un tout petit exercice avant de finir.
L’ordinateur est un peu trop rapide à jouer, on a à peine le temps de voir ce qui se passe.
On serait alors tenté d’ajouter un simple time.sleep(1)
pour ralentir l’exécution, mais on se demanderait alors ce qui se passe.
Une autre idée serait d’ajouter des animations pendant les choix de l’ordinateur, afin de voir qu’il se passe quelque chose sans que ça ne se passe trop vite.
Et pour cela, on va simplement utiliser les fonctions print
et time.sleep
.
Par exemple comment représenter une barre de progression en animation ?
On peut afficher un caractère, attendre, afficher un autre caractère, etc.
Pour cela, on va appeler print
avec l’argument end=''
(pour ne pas afficher de retour à la ligne) dans une boucle.
En sortie de boucle, on s’occupera de revenir à la ligne pour finaliser la barre.
import time
for _ in range(10):
print('-', end='')
time.sleep(0.1)
print()
Si vous exécutez ce code, vous verrez probablement la barre complète s’afficher d’un seul coup au bout d’une seconde, sans aucune animation.
Cela est dû au mécanisme de flush (mémoire tampon) dont je vous avais parlé : en l’absence de retour à la ligne, print
a simplement placé le texte en mémoire tampon mais n’a rien écrit réellement sur le terminal.
On corrige ça an ajoutant l’argument flush=True
à l’appel.
import time
for _ in range(10):
print('-', end='', flush=True)
time.sleep(0.1)
print()
Pour aller plus loin, on peut aussi utiliser le caractère spécial \b
qui permet de revenir en arrière sur la ligne et donc d’effacer le dernier caractère imprimé.
Solution
Rien de bien méchant, je présente ici le fichier tp/ia.py
uniquement qui est le seul à changer.
Si les animations dans le terminal vous intéressent et que vous souhaitez aller plus loin, je vous conseille de regarder du côté du module curses
de Python, qui permet de dessiner dans le terminal plus simplement qu’avec print
et '\b'
.
La bibliothèque tierce prompt_toolkit
peut aussi être un bon point d’entrée.