4. Partie graphique avec PyGame
Le but est ici de retranscrire notre travail effectué ici, pour l'afficher proprement avec Pygame.
Nous améliorerons également l'ergonomie du logiciel : ici plus rien à taper au clavier, on pourra directement cliquer sur la case choisie avec la souris pour choisir où mettre son X ou son O
4.1 Changements à apporter dans la structure du programme
- La première chose à faire sera de copier votre code (fonctionnel) dans edupython, ce sera votre base de départ. Nous ne continuons pas à coder dans Google Coolab car la librairie Pygame n'est pas compatible (l'environnement Python est sur une machine distante, et Pygame a besin d'accéder à la carte graphique de votre ordinateur, ce qui est impossible).
- Nous allons également faire des changements dans la structure du programme. Nous allons garder notre classe Morpion, mais nous allons créer une 2e classe, intitulée Grille, qui s'occupera uniquement de gérer l'affichage. Et nous créerons une instance de cette classe à l'initilisation de la classe Morpion.
- Dernière chose, n'oubliez pas d'importer la librairire pygame (évidemment) mais également la librairie sys.
Commençons par le commencement ...
4.2 Création de la fenêtre et de la grille
Pour commencer, nous allons donc créer une nouvelle classe nommée Grille
La fonction d'initialisation prendra un argument en entrée (nommé ecran). Nous verrons son utilité plus tard.
Cettte fonction comprendra :
- Un attribut ecran que l'on initialisera avec la valeur ecran passée en paramètre.
- Un attribut lignes qui sera une liste qui contient 4 tuples, avec des coordonnées de points pour tracer les 4 lignes de la grille. Utilisez les valeurs suivantes :[( (200,0),(200,600)),
((400,0),(400,600)), ((0,200),(600,200)), ((0,400),(600,400))]
- Un attribut grille qui sera un tableau de tableau, qui correspond à notre plateau créé ci-dessus. On l'initialisera avec les valeurs None cette fois-ci (et non plus avec les tirets)
On ajoutera également une fonction afficher pour afficher la grille (qui ne prendra pas d'arguments).
On parcourera juste notre attribut lignes créé ci-dessus, pour tracer des lignes en utilisant la méthode draw.line de pygame :
for ligne in self.lignes :
pygame.draw.line(self.ecran,(0,0,0),ligne[0],ligne[1],2)
Maintenant, intéressons nous à notre classe
Morpion
Nous allons modifier la fonction d'initialisation, en créant la fenêtre Pygame (rappelez vous son fonctionnement)
Dans cette fonction (sans argument ), ajouter :
- Un attribut ecran, qui créera la fenêtre Pygame:
pygame.display.set_mode((600,600))
- Un attribut grille, qui sera une instance de la classe Grille (avec en paramètre l'attribut ecran)
Nous allons également modifier la fonction jeu;
Rappelez vous, Pygame fonctionne avec des événements. Il faut donc rajouter une boucle For sur ces événements, et prévoir la sortie :
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
Rajouter également ces 2 instructions à la suite de la boucle for, pour ajouter une couleur et rafraichir l'affichage :
self.ecran.fill((240,240,240))
pygame.display.flip()
Enfin, à la suite de ces 2 lignes, nous allons appeler la méthode
afficher de l'attribut
grille (qui est une instance de la classe Grille, dans laquelle nous avons une méthode afficher)
4.3 Création et affichage des X/O
Nous allons utiliser la souris pour placer nos X et nos O Pour cela, nous utiliserons la méthode mouse.getpos() de pygame. Cette fonction nous renvoie les coordonnées de l'endroit où l'on clique. Or, nos cases font 200 pixels de large et 200 pixels de long.
Comment faire pour convertir facilement ces coordonnées sous forme d'indices qui seront plus facilement utilisables ? Nous allons utiliser la division entière par 200 : nous aurons alors des indices de position.
Rajoutez ces lignes dans la boucle principale de la fonction jeu (nous testons si on a cliqué avec les bouton gauche de la souris):
if event.type == pygame.MOUSEBUTTONDOWN and pygame.mouse.get_pressed()[0]:
position = pygame.mouse.get_pos()
position_x ,position_y = position[0]//200 ,position[1]//200
if self.compteur % 2 == 0 :
self.grille.fixer_la_valeur(position_x, position_y, self.J1)
else:
self.grille.fixer_la_valeur(position_x, position_y, self.J2)
Si vous regardez les dernières lignes, nous testons la parité d'un attribut compteur. Quand il est pair, c'est J1 qui joue, sinon c'est l'inverse.
Nous allons définir la méthode fixer_la_valeur de notre attribut grille juste après.
N'oubliez pas de rajouter également dans la fonction d'initialisation de la classe Morpion l'attribut compteur, qui sera initialisé à 0.
Retour à la classe Grille :
- Rajouter une nouvelle méthode : fixer_la_valeur.
Cette méthode sera utilisée pour modifier une valeur de la grille, avec X ou O. Elle prendra 3 paramètres : x,y, et la valeur
Testez si la valeur de la grille d'abscisse x et d'ordonnée y est égale à None. Si c'est le cas, on modifie cette valeur de la grille avec valeur
- Rajoutez un attribut compteur_on, qui sera initialisé à False, qui va nous permettre de définir si le compteur est actif ou pas
- Mettez le à True quand vous avez modifié une valeur dans fixer_le_valeur
- Mettez le à False dans la boucle principale, en testant si le compteur est actif, à savoir juste après avoir testé la parité du compteur :
if self.compteur % 2 == 0 :#1
self.grille.fixer_la_valeur(position_x, position_y, self.J1)
else:
self.grille.fixer_la_valeur(position_x, position_y, self.J2)
if self.grille.compteur_on: #1
self.compteur += 1
self.grille.compteur_on = False
Enfin, dernière chose avant de tester :
Dans la méthode afficher de la classe Grille, nous affichons déjà les 4 lignes qui font la grille.
Mais il serait bien d'afficher les X et les O également, suivant les valeur de la grille ?
Pour cela, nous allons parcourir les éléments de la grille (double boucle), puis quand nous allons trouver un 'X', nous allons dessiner un X avec la méthode draw.line, et idem pour O avec la méthode draw.circle. Nous utiliserons les fonctions de dessins de Pygame.
Je vous épargne cette recherche fastidieuse qui n'est pas le but du TP ici, et je vous donne les instructions à rajouter :
for y in range(0,len(self.grille)):
for x in range(0,len(self.grille)):
if self.grille[y][x] == 'X' :
pygame.draw.line(self.ecran, (0, 0, 0), (x * 200, y * 200), (200 + (x * 200), 200 + (y * 200)), 3)
pygame.draw.line(self.ecran, (0, 0, 0), ((x * 200), 200 + (y * 200)), (200 + (x * 200), (y * 200)),
3)
elif self.grille[y][x] == 'O' :
pygame.draw.circle(self.ecran, (0, 0, 0), (100 + (x * 200), 100 + (y * 200)), 100, 3)
4.4 Vainqueur et fin de partie
Nous y sommes presque. Il vous reste à réutiliser la méthode test_fin_jeu définie ci-dessus, qui nous permettait de savoir qui était vainqueur.
En effet, nous ne parcourons plus le plateau, mais la grille
Attention cependant si vous remplacez juste plateau par grille, cela risque de ne pas fonctionner et de générer le message d'erreur suivant :
TypeError: 'Grille' object does not support indexing
A vous de voir pourquoi il y a ce message d'erreur, et quelle modification, liée à la nature de l'attribut grille il faut effectuer
Un peu de ménage de code :
N'oubliez pas de supprimer certaines fonctions qui ne sont plus utiles, comme afficher_plateau et jouer de la classe Morpion qui ne sont plus utilisées.
BON JEU !!!
4.5 Aller plus loin
Pour ceux qui ont une version fonctionnelle, vous pouvez faire un menu de démarrage, avec la possiblité de rentrer des noms pour les joueurs, et de recommencer une partie.
Et également, pour ceux qui le souhaitent, programmer une IA qui pourrait jouer contre vous.
Mais ceci est une autre histoire ...