TP5: jeu de la vie (POO)🔗
On se propose de programmer l'automate cellulaire le plus célèbre, le Jeu de la vie de John Horton Conway (1937–2020), qui simule, à partir de règles simples parfaitement déterministes, l'apparition et l'évolution de systèmes arbitrairement complexes (il est démontré que le Jeu de la Vie est Turing-complet).
Il s'agit donc ici de coder un programme représentant la grille 2D dans laquelle évoluent les cellules (qui naissent, vivent et meurent) et les quelques règles d'évolution d'une configuration à la suivante.
Puisque l'on ne peut pas en pratique travailler sur un monde infini, on prend le parti initial de considérer un monde toroïdal avec des conditions de bord périodiques, p.ex. où la voisine à gauche de la cellule la plus à gauche du monde est la cellule la plus à droite (c'est clair?).
Créer une classe
Life, contenant un tableau 2D (liste de listes, ou tableaunumpy.ndarray) dehlignes etwcolonnes de booléens (True/Falsepour désigner une cellule vivante/morte) dans un attributworld. La classe est initialisée (__init__) par les entiershetw, et le tableauworldest initialement intégralementFalse(toutes les cellules sont mortes).Écrire une méthode
init_random(frac=0.5)permettant d'initialiser la grilleworldaléatoirement à l'aide de la fonctionrandom.random(), de sorte que chaque cellule a une probabilité initialefracd'être vivante.Écrire la méthode
__str__affichant l'état courant du monde, en représentant les cellules vivantes par le caractère#et celles mortes par., p.ex.:...O..O.....OO....... .....OOO............. O........O........... .....O...O........... ................OO... .....O.O......OO..O.. ..............OO.OO.. ..............OO.OO.. ................O....
Écrire une méthode
get(i, j)permettant d'obtenir l'état de la cellule à la position(i, j), avecietjquelconques (on n'a pas nécessairement \(0 \leq i \leq w - 1\) ni \(0 \leq j \leq h - 1\)), en implémentant les conditions de bord périodiques.En utilisant la méthode précédente, écrire une méthode
count_neighbors(i, j)permettant de compter le nombres de cellules vivantes autour de la position(i, j)(il y en a un maximum de 8).L'état d'une cellule est entièrement déterminé par l'état de ses huit cellules voisines, selon les règles suivantes:
une cellule morte possédant exactement trois cellules voisines vivantes devient vivante,
une cellule vivante ne possédant pas exactement deux ou trois cellules voisines vivantes meurt.
Écrire une méthode
evolve_cell(i, j)retournant l'état futur de la cellule à la position(i, j).Écrire une méthode
evolve()permettant de faire évoluer le monde entier d'une seule itération.Écrire une méthode
show(nstep=100)permettant de faire évoluer le monde surnstepitérations, et de l'afficher à chaque itération (nstep=0devra réaliser une boucle infinie). Pour que l'affichage soit agréable à l'oeil, marquez des pauses entre l'affichage de chaque itération grâce à la fonctiontime.sleep().
Pour aller plus loin
Écrire deux méthodes
dump(filename)etload(filename)permettant de sauvegarder/charger l'état du monde dans le format standardisé plaintext.Écrire une fonction externe
load_pattern(filename)permettant de lire une structure prédéfinie, p.ex. leGosper glider gundans une structure ad hoc. Compléter la classeLifepar une méthodeinsert(pattern, i=0, j=0)permettant d'insérer une structure prédéfinie dans le monde à la position(i, j).