Programmer une génération du jeu de la vie de Conway
Exercice supplémentaire · niveau 2 · NSI (première), chapitre 4 — Les types construits · Les matrices au travail : images et automates
Énoncé
Programmer une génération du jeu de la vie de Conway : une cellule vivante survit avec ou voisins vivants, une cellule morte naît avec exactement voisins. Vérifier sur le « clignotant » et sur le « bloc », puis montrer expérimentalement pourquoi la mise à jour en place est fausse.
Corrigé
def voisins_vivants(g, i, j):
"""Nombre de voisins vivants de la case (i, j), bords compris."""
L, C = len(g), len(g[0])
n = 0
for di in (-1, 0, 1):
for dj in (-1, 0, 1):
if di == 0 and dj == 0:
continue # on ne se compte pas soi-meme
a, b = i + di, j + dj
if 0 <= a < L and 0 <= b < C: # on reste dans la grille
n = n + g[a][b]
return n
def generation_suivante(g):
"""NOUVELLE grille. g n'est pas modifiee : c'est tout l'exercice."""
L, C = len(g), len(g[0])
return [[1 if (g[i][j] == 1 and voisins_vivants(g, i, j) in (2, 3))
or (g[i][j] == 0 and voisins_vivants(g, i, j) == 3) else 0
for j in range(C)] for i in range(L)]
Le test 0 <= a < L and 0 <= b < C est indispensable : sans lui, un indice ne provoquerait aucune erreur en Python et désignerait le bord opposé — la grille deviendrait un tore, sans qu'on l'ait décidé.
Vérification sur deux figures classiques.
clignotant = [[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]]
etape1 = generation_suivante(clignotant)
# les trois cases horizontales deviennent verticales
assert generation_suivante(etape1) == clignotant # periode 2
bloc = [[0, 0, 0, 0], [0, 1, 1, 0], [0, 1, 1, 0], [0, 0, 0, 0]]
assert generation_suivante(bloc) == bloc # figure stable
Le clignotant oscille avec une période de , le bloc ne bouge pas : deux propriétés connues d'avance, donc deux vrais oracles.
La mise à jour en place, et sa faute.
def generation_en_place(g):
for i in range(len(g)):
for j in range(len(g[0])):
v = voisins_vivants(g, i, j) # g a DEJA change en partie
g[i][j] = 1 if (g[i][j] == 1 and v in (2, 3)) \
or (g[i][j] == 0 and v == 3) else 0
return g
Appliquée au clignotant, elle ne rend pas la barre verticale attendue mais une figure en diagonale : les lignes et deviennent [0, 0, 1, 1, 0] et [0, 1, 0, 1, 0]. La raison est la même que pour le flou : au moment de décider du sort d'une cellule, certains de ses voisins appartiennent déjà à la génération suivante. Le résultat dépend de l'ordre de parcours — et un programme dont le résultat dépend de l'ordre dans lequel on l'écrit n'a pas de spécification.
Les autres exercices de ce chapitre Le cours du chapitre
Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.