# -*- encoding:utf-8 -*-
from copy import deepcopy
[docs]class SuperDieu(object):
"""Classe optimisée permettant l'évolution des cellules."""
def __init__(self, etat, size=None, mode="infini", regle =((3, ),(2,3))):
"""Initialise la classe SuperDieu
:param etat: Set de coordonnée des cellules vivantes
:type etat: set
:param size: couple de nombre définissant la largeur et la hauteur
du rectangle défini au préalable
:type size: tuple
:param mode: mode de jeu influant sur le calcule des voisins
:type mode: str
:param regle: defini les règles
:type regle: tuple
"""
self.regle = regle
self.mode = mode
self.size = size
self.vivantes = etat
self.n_voisines = {}
self.mortes_actives = set()
for (x,y) in self.vivantes:
# Tout les vivants
for (v_x, v_y) in self.coord_voisins((x,y)):
# Voisins de (x,y)
if not (v_x,v_y) in self.n_voisines:
self.n_voisines[(v_x,v_y)] = 0
# On initialise le voisins à zéro
self.n_voisines[(v_x,v_y)] += 1
# Nombres de voisins augmente de 1
for (x,y) in self.n_voisines:
# Pour toutes les cellules vivantes et leur voisins
if not(x,y) in self.vivantes:
# Selectionne tout les voisins morts existants
self.mortes_actives.add((x,y))
# Tadaa ! Nous avons tout les voisins morts !
[docs] def coord_voisins(self, c):
"""Calcule les coordonnées des voisins d'une cellule.
:param c: Couple de coordonnées.
:type c: Tuple.
:return: Les voisins de c.
:rtype: Set.
"""
voisins_infini = {(c[0]-1, c[1]-1),
(c[0]-1, c[1]),
(c[0]-1, c[1]+1),
(c[0], c[1]-1),
(c[0], c[1]+1),
(c[0]+1, c[1]-1),
(c[0]+1, c[1]),
(c[0]+1, c[1]+1)}
if self.mode == "infini":
return voisins_infini
if self.mode == "snake":
return {(x%self.size[0], y%self.size[1]) for (x,y) in voisins_infini}
[docs] def doit_mourir(self, c):
"""Défini les cellules qui mourront à la prochaine génération.
:param c: Couple de coordonnées.
:type c: tuple
:return: Doit-elle mourir ?
:rtype: bool
"""
if c in self.n_voisines:
return not (self.n_voisines[c] in self.regle[1])
else:
return True
[docs] def doit_naitre(self, c):
"""Défini les cellules qui naîtront à la prochaine génération.
:param c: Couple de coordonnées.
:type c: tuple
:return: Doit-elle survivre ?
:rtype: bool
"""
return self.n_voisines[c] in self.regle[0]
[docs] def generation_suivante(self):
"""Applique l'algorithme d'évolution en mode ``infini``.
- Calcule les coordonnées des cellules nées et mortes suivants la règle
du JEU.
- Met à jour ``self.n_voisines``, ``self.mortes_actives``,
``self.vivantes``.
:return: Les cellules nées et mortes.
:rtype: bool
"""
# ajoute les celulles qui vont naître au prochain tour
nees = {c for c in self.mortes_actives if self.doit_naitre(c)}
# ajoute les celulles qui vont mourir au prochain tour
mortes = {c for c in self.vivantes if self.doit_mourir(c)}
for (x,y) in nees:
self.vivantes.add((x,y))
self.mortes_actives.remove((x,y))
for (v_x,v_y) in self.coord_voisins((x,y)):
if not (v_x,v_y) in self.n_voisines:
self.n_voisines[(v_x, v_y)] = 0
self.mortes_actives.add((v_x, v_y))
self.n_voisines[(v_x,v_y)] += 1
for (x,y) in mortes:
self.vivantes.remove((x,y))
if (x,y) in self.n_voisines:
if self.n_voisines[(x,y)] >= 1:
self.mortes_actives.add((x,y))
for (v_x,v_y) in self.coord_voisins((x,y)):
if self.n_voisines[(v_x,v_y)] == 1:
if (v_x,v_y) in self.mortes_actives:
self.mortes_actives.remove((v_x,v_y))
del self.n_voisines[(v_x,v_y)]
else:
self.n_voisines[(v_x,v_y)] -= 1
return nees, mortes
[docs]class Dieu(object):
"""Classe gérant la vie et la mort des cellules."""
def __init__(self, etat):
"""Initialise la classe Dieu.
:param etat: Liste de coordonnée des cellules vivantes.
:type etat: liste
""" # liste de liste composée de 0 et 1 (0: cellule morte)
self.etat = etat
self.size = (len(etat), len(etat[0]))
[docs] def generation_suivante(self, mode='snake'):
"""Calcule la prochaine génération avec le bon mode assigné.
"""
getattr(self, 'generation_suivante_'+mode)()
[docs] def generation_suivante_snake(self):
"""Applique l'algorithme d'évolution en mode ``snake``.
- Fait une copie de ``self.etat``.
- Prends tout les voisins de chaque cellules et applique les deux règles.
- Met à jour ``self.etat``.
"""
# nouvel_etat est une copie de l'état courant
nouvel_etat = deepcopy(self.etat)
for x in range(self.size[0]):
for y in range(self.size[1]):
# n: nombre de voisins vivant, initialisé à -self.etat[x][y] car
# sinon on compterais aussi la cellule en cours dans les
# boucles.
n = -self.etat[x][y]
for v_x in (x-1, x, x+1):
for v_y in (y-1, y, y+1):
# "%self.size" permet de definir un cellule situé à l'opposé
# dans le cas ou la cellule est or du "carré"
if self.etat[v_x % self.size[0]][v_y % self.size[1]] == 1:
# le voisin est vivant
n += 1
# regle 1: naissance
if self.etat[x][y] == 0:
if n == 3:
nouvel_etat[x][y] = 1
# regle 2: mort
else:
if not (2 <= n <= 3):
nouvel_etat[x][y] = 0
# on sauvegarde nouvel_etat dans `self.etat`
self.etat = nouvel_etat
[docs] def generation_suivante_mur(self):
"""Applique l'algorithme d'évolution en mode ``mur``.
- Fait une copie de ``self.etat``.
- Prends tout les voisins de chaque cellules
et applique les deux règles.
- Met à jour ``self.etat``.
"""
# nouvel_etat est une copie de l'état courant # nouvel_etat est une copie de l'état courant
nouvel_etat = deepcopy(self.etat)
for x in range(self.size):
for y in range(self.size):
# n: nombre de voisins vivant, initialisé à -self.etat[x][y] car
# sinon on compterais aussi la cellule en cours dans les
# boucles.
n = -self.etat[x][y]
for v_x in (x-1, x, x+1):
for v_y in (y-1, y, y+1):
if -1 < v_x < self.size and -1 < v_y < self.size:
if self.etat[v_x][v_y] == 1:
# le voisin est vivant
n += 1
# regle 1: naissance
if self.etat[x][y] == 0:
if n == 3:
nouvel_etat[x][y] = 1
# regle 2: mort
else:
if not (2 <= n <= 3):
nouvel_etat[x][y] = 0
# on sauvegarde nouvel_etat dans `self.etat`
self.etat = nouvel_etat
nouvel_etat = deepcopy(self.etat)
[docs] def print_etat(self):
"""Affiche l'etat de manière comprehensible sur la console (debug).
"""
for ligne in self.etat:
print(" ".join(str(i) for i in ligne))