Jeu de la vie
Prérequis
- structures de contrôle ;
- boucles ;
- tableaux à deux dimensions ;
- fonctions ;
- modules ;
- Programmation orientée objet.
Présentation
Je vous propose un mini projet individuel visant à reproduire le jeu de la vie proposé par Conway en 1970. Le jeu de la vie est ce qu'on appelle un automate cellulaire : c'est un « tableau » dont l'état de chaque case ou cellule dépent de l'état des cases voisines. À chaque « tour » de jeu, on met à jour l'ensemble des cases en fonction de leurs voisines et on recommence.
Dans le jeu de la vie, chaque cellule peut avoir deux états :
- vivante ;
- morte.
L'état d'une cellule au tour suivant dépend de l'état de ses huit voisins directs. Il y a deux règles simples qui s'appliquent :
- une cellule morte possédant exactement trois voisines vivantes devient vivante : elle naît ;
- une cellule vivante possédant deux ou trois voisines vivantes le reste, sinon elle meurt.
Si vous voulez des précisions ou en savoir plus allez sur la page Wikipédia du jeu de la vie ou regardez la vidéo d'EGO sur le jeu de la vie :
Travail demandé
Vous allez donc coder le jeu de la vie en utilisant la programmation orientée objet. Comme c'est le premier projet de l'année, je vais beaucoup vous guider.
Classe JeuDeLaVie
Vous allez créer une classe JeuDeLaVie avec un attribut tableau. Nous auront besoin de quelques modules, voici donc comment devra commencer votre programme :
import os
import time
import copy
class JeuDeLaVie:
Constructeur
Le constructeur prendra en argument un tableau à deux dimensions de taille quelconque rempli de 0
et de 1
représentant les cellules.
Un 0
pour une cellule morte et un 1
pour une cellule vivante.
Voici les caractéristiques attendues du constructeur :
def __init__(self, tableau):
"""
Affecte un tableau à deux dimensions à l’attribut tableau
:param tableau: tableau à deux dimensions
"""
Méthode affiche()
Cette méthode doit permettre d'afficher le tableau dans le shell.
On n'utilisera pas d'interface graphique pour simplifier le programme.
Attention, il faudra effacer le contenu du shell avant chaque affichage pour donner l'effet d'une vidéo.
Après avoir importé le module os
il faut utiliser l'une des commandes suivantes en fonction de votre système d'exploitation :
- pour Windows :
os.system('cls')
- pour Linux :
os.system('clear')
Autre méthodes
Voici la liste des autres méthodes de la classe JeuDeLaVie avec leurs docstrings qui font office de cahier des charges.
def run(self, nombre_tours, delai):
"""
Méthode principale du jeu.
Fait tourner le jeu de la vie pendant nombre_tours.
Elle rafraichit l’affichage à chaque tour
et attend delai entre chaque tour.
:param nombre_tours: nombre de tours à effectuer
:param delai: temps d’attente en secondes entre chaque tour
"""
def tour(self):
"""
Met à jour toute les cellules du tableau en respectant les règles
du jeu de la vie.
"""
def valeur_case(self, i, j):
"""
Renvoie la valeur de la case [i][j] ou 0 si la case n’existe pas.
"""
def total_voisins(self, i, j):
"""Renvoie la somme des valeurs des voisins de la case [i][j]."""
def resultat(self, valeur_case, total_voisins):
"""
Renvoie la valeur suivante d’une la cellule.
:param valeur_case: la valeur de la cellule (0 ou 1)
:param total_voisins: la somme des valeurs des voisins
:return: la valeur de la cellule au tour suivant
>>> a = JeuDeLaVie([])
>>> a.resultat(0, 3)
1
>>> a = JeuDeLaVie([])
>>> a.resultat(0, 1)
0
>>> a = JeuDeLaVie([])
>>> a.resultat(0, 4)
0
>>> a = JeuDeLaVie([])
>>> a.resultat(1, 2)
1
>>> a = JeuDeLaVie([])
>>> a.resultat(1, 3)
1
>>> a = JeuDeLaVie([])
>>> a.resultat(1, 1)
0
>>> a = JeuDeLaVie([])
>>> a.resultat(1, 4)
0
"""
Utilisation du programme
Pour instancier le jeux de la vie et la lancer il faudra alors ajouter ces commandes à la fin de votre fichier :
tableau = [[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]
mon_jeu = JeuDeLaVie(tableau)
mon_jeu.run(100, 0.1)
Et pour avoir un affichage correct il sera surement nécessaire de lancer le programme à partir de la ligne de commande :
python3 JeuDeLaVie.py
L'état initial donné ici devrait faire apparaitre un motif qui se déplace en diagonale. Voici un autre état initial qui provoque une expension des cellules vivantes :
tableau = [[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]
À vous d'essayer différentes situations initiales pour voir ce qu'il se passe.
Aides et conseils
Délais
Pour faire attendre le programme nous avons besoin du module time
avec la méthode time.sleep()
.
Copier un tableau
À un moment donné, vous allez avoir besoin de copier l'attribut tableau de la classe.
Pour avoir une copie indépendante de l'original il vous faudra utiliser la méthode copy.deepcopy()
du module copy
.
Améliorations
Si vous êtes rapide, vous pouvez apporter des améliorations au programme. Voici quelques suggestions :
- détecter s'il n'y a plus de changements dans le tableau entre deux tours et arrêter alors le programme ;
- permettre de choisir le symbole représentant une cellule vivante ;
- proposer de choisir parmi des configurations initiales pré-enregistrées ;
- ajouter une interface graphique (pour ceux qui savent le faire). ;
- toute autre idée pour améliorer le programme…
Tableau du barème
Voilà le barème complet sur 14 pour ce projet.
Tâche | Barème |
---|---|
Affichage basique | 1 point |
Affichage amélioré | 1 point |
Valeur case | 1 point |
Total voisins | 1 point |
Tour | 1 point |
Run | 1 point |
Arrêt automatique | 1 point |
Configuration pré-enregistrées | 1 point |
Interface graphique [Bonus] | 1 point bonus |
Code propre | 2 points |
Code optimisé | 2 points |
Commentaires | 2 pointss |
Totals | 14 |