:orphan: .. _TP5_GBM3A: TP5: jeu de la vie (POO) ======================== .. contents:: Table des matières :local: On se propose de programmer l'automate cellulaire le plus célèbre, le :wfr:`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 :wfr:`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?). 1. Créer une classe `Life`, contenant un tableau 2D (liste de listes, ou tableau :class:`numpy.ndarray`) de `h` lignes et `w` colonnes de booléens (`True`/`False` pour désigner une cellule vivante/morte) dans un attribut `world`. La classe est initialisée (`__init__`) par les entiers `h` et `w`, et le tableau `world` est initialement intégralement `False` (toutes les cellules sont mortes). 2. Écrire une méthode `init_random(frac=0.5)` permettant d'initialiser la grille `world` aléatoirement à l'aide de la fonction :func:`random.random`, de sorte que chaque cellule a une probabilité initiale `frac` d'être vivante. 3. É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.... 4. Écrire une méthode `get(i, j)` permettant d'obtenir l'état de la cellule à la position `(i, j)`, avec `i` et `j` *quelconques* (on n'a pas nécessairement :math:`0 \leq i \leq w - 1` ni :math:`0 \leq j \leq h - 1`), en implémentant les conditions de bord périodiques. 5. 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). 6. 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)`. 7. Écrire une méthode `evolve()` permettant de faire évoluer le monde entier d'une seule itération. 8. Écrire une méthode `show(nstep=100)` permettant de faire évoluer le monde sur `nstep` itérations, et de l'afficher à chaque itération (`nstep=0` devra 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 fonction :func:`time.sleep`. .. rubric:: Pour aller plus loin 9. Écrire deux méthodes `dump(filename)` et `load(filename)` permettant de sauvegarder/charger l'état du monde dans le format standardisé `plaintext `__. 10. Écrire une *fonction externe* `load_pattern(filename)` permettant de lire une structure prédéfinie, p.ex. le :download:`Gosper glider gun ` dans une structure *ad hoc*. Compléter la classe `Life` par une méthode `insert(pattern, i=0, j=0)` permettant d'insérer une structure prédéfinie dans le monde à la position `(i, j)`.