#!/usr/bin/python

#####################################
### Implantation du jeu de la vie ###
#####################################

from itertools import product
from tkinter import *
from random import randint
from copy import deepcopy
from time import time

__date__=(2017, 4,2)
__author__=('Bérénice', 'Delcroix-Oger')

#Benchmark (pour 100 itérations) sans threading
    # jeu de taille 10x10 : 0.07195043563842773
    # jeu de taille 100x10 : 1.0633792877197266
    # jeu de taille 100x50 : 8.196194410324097 
    # jeu de taille 100x100 : 17.04249143600464
    # jeu de taille 100x1000 : 167.45983719825745

# en faisant sur une moyenne de 100 tableaux différents :
    # jeu de taille 10x10 : 0.26488584995269776
    # jeu de taille 100x10 :  3.19277556180954
    # jeu de taille 100x50 : 16.73183496952057 
    # jeu de taille 100x100 : 33.776540544033054
    # jeu de taille 100x1000 : ?

#https://www.wolframscience.com/nksonline/page-964f-text
    
    
#=======Variables globales========
nb_line=50 # nombre de lignes du plateau
nb_col=50  # nombre de colonnes du plateau
cc=10     # taille d'une case du jeu
fin=False # pour stopper le jeu
plateau=[]#plateau du jeu
platJeu={}#plateau affiché

#======= Définition de la classe du plateau =====
class Cell:

    def __init__(self,x,y):
        global can1    
        self.etat=0     # état de la cellule : 0 si elle est morte, 1 sinon
        self.carre=can1.create_rectangle(x*cc+cc/2, y*cc+cc/2, (x+1)*cc+cc/2-cc/32, (y+1)*cc+cc/2-cc/32, fill="white", outline="white")
        self.vois=0     #somme des états des voisines

    def chgSt(self,v): #v=0 si la cellule est morte, 1 sinon
        self.etat=v
        if self.etat==0:
            can1.itemconfig(self.carre, fill='white')
        else:
            can1.itemconfig(self.carre, fill='black')

#plateau=tableau de cellules
class Plateau():
    def __init__(self):
        self.hauteur=nb_line
        self.largeur=nb_col
        self.t=[[Cell(j,i) for j in range(nb_col)] for i in range(nb_line)]
#tableau fait pour avoir une bordure de cases mortes (prendre les indices
# entre 0 et nb_col-1
        self.b=[] #stocke les coordonnées des cellules qui naissent
        self.d=[] #stocke les coordonnées des cellules qui meurent

    def copie(self):
        aux=Plateau()
        for i in range(self.hauteur):
            for j in range(self.largeur):
                aux[i,j]=Plateau(i,j)
        

#======= Initialisation du plateau ==========
def initGame():
    """ Fonction qui initialise le plateau de jeu"""
    global nb_line, nb_col, fen1, can1
    nb_line=5
    nb_col=7
    #====== Création de la fenêtre =====
    fen1=Tk()
    fen1.title("Le jeu de la vie")
    fen1.configure(width=nb_col*cc+cc, height=nb_line*cc+cc)
    can1=Canvas(fen1, bg='white', height=nb_line*cc+cc, width=nb_col*cc+cc)
    can1.pack(padx=5,pady=5)
    #===== Création du plateau
    plat=Plateau()
    for i in range(3):
        plat.t[i+1][3].chgSt(1)
        for c in around(i+1,3):
            plat.t[c[0]][c[1]].vois+=1
#    for i in range(nb_line):
#        for j in range(nb_col):
#            plat.t[i][j].chgSt(1)
    return plat    
##            [[0,0,0,0,0,0,0],\
##            [0,0,0,1,0,0,0],\
##            [0,0,0,1,0,0,0],\
##            [0,0,0,1,0,0,0],\
##            [0,0,0,0,0,0,0]]

def randGame():
    global nb_line, nb_col, fen1, can1
    #====== Création de la fenêtre =====
    fen1=Tk()
    fen1.title("Le jeu de la vie")
    fen1.configure(width=nb_col*cc+cc, height=nb_line*cc+cc)
    can1=Canvas(fen1, bg='white', height=nb_line*cc+cc, width=nb_col*cc+cc)
    can1.pack(padx=5,pady=5)
   #===== Création du plateau
    plat=Plateau()
    for i in range(nb_line):
        for j in range(nb_col):
            r=randint(0,1)
            plat.t[i][j].chgSt(r)
            if r==1:
                for c in around(i,j):
                    plat.t[c[0]][c[1]].vois+=1
    return plat



##def effetGame():
##    print("Quel effet souhaitez-vous?")
##    print("0- un automate stable")
##    print("1- un automate oscillateur")
##    print("2- un automate vaisceau")
##    print("3- un automate Mathusalem")
##    print("4- un automate puffeur")
##    res=input()
##    effet=[stable, oscill,ship,lapin,puff]
##    return effet[int(res)]()
##
##def stable():
##    global nb_line, nb_col
##    nb_line=10
##    nb_col=10
##    return [[0,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,1,0,0,1,0,0,0],\
##            [0,0,1,0,1,1,0,0,0,0],\
##            [0,0,0,0,1,1,0,1,0,0],\
##            [0,0,0,1,0,0,1,0,0,0],\
##            [0,0,0,0,1,0,0,0,0,0],\
##            [0,0,0,0,0,1,0,0,0,0],\
##            [0,0,0,0,1,1,1,0,0,0]]
##
##def oscill():
##    global nb_line, nb_col
##    nb_line=6
##    nb_col=17
##    return [[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,1,1,0,0,0],\
##            [0,0,1,0,0,0,0,1,1,1,0,0,1,0,0,0,0],\
##            [0,0,1,0,0,0,1,1,1,0,0,0,0,0,0,1,0],\
##            [0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0],\
##            [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]
##
##def ship():
##    global nb_line, nb_col
##    nb_line=9
##    nb_col=20
##    return [[0,0,0,0,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,1,0,0,0,0,0,0,0,0],\
##            [0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0],\
##            [0,1,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0],\
##            [0,0,1,1,0,0,0,0,0,1,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]]
##
##def lapin():
##    """Mathusalem"""
##    global nb_line, nb_col
##    nb_line=9
##    nb_col=8
##    return [[0,0,0,0,1,0,1,0],\
##            [1,0,1,0,0,1,0,0],\
##            [0,1,0,0,0,1,0,0],\
##            [0,1,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,1,1,0,0],\
##            [0,0,0,1,1,0,0,0]]
##
##def puff():
##    """Mathusalem"""
##    global nb_line, nb_col
##    nb_line=7
##    nb_col=27
##    return [[0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,0,0],\
##            [0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0],\
##            [0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,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,1,0,0,0],\
##            [0,0,0,1,0,0,0,0,1,1,0,1,0,0,0,1,0,1,1,0,0,0,0,1,0,0,0],\
##            [1,0,0,1,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,1,0,0,1],\
##            [0,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,1,1,1,0]]
##
##
##def persoGame():
##    """Fonction qui permet au joueur d'initialiser le plateau"""
##    global nb_line, nb_col
##    nb_line=int(input("Nombre de lignes souhaitées?"))
##    nb_col=int(input("Nombre de colonnes souhaitées?"))
##    plat=[]
##    print("""Veuillez maintenant rentrer le plateau voulu ligne à ligne (exple pour un jeu à 5 colonnes : 01010, où 0 représente une cellule morte et 1, une cellule vivante)""")
##    for i in range(nb_line):
##        goodLine=False
##        while not(goodLine):
##            newLine=[]
##            res=input()
##            for x in res:
##                newLine.append(int(x))
##            if len(newLine) != nb_col:
##                print("Le nombre d'éléments n'est pas correcte, veuillez rentrer la ligne de nouveau")
##            else:
##                plat.append(newLine)
##                goodLine=True
##    print("Le plateau que vous avait entré est;")
##    for x in plat:
##        for i in x:
##            if i:
##                print("#", end='')
##            else:
##                print("_", end='')
##        print(" ")
##    ok=input("Cela vous convient-il? (o/n)")
##    if ok=="o":
##        return plat
##    else:
##        persoGame()


#======= Evolution d'un pas de temps ====== 

def around(i,j):
    """Fonction qui calcule les positions des cases autour d'une case"""
    global nb_line, nb_col
    aux1=[i+a-1 for a in range(3) if i+a-1>-1 and i+a-1<nb_line]
    aux2=[j+a-1 for a in range(3) if j+a-1>-1 and j+a-1<nb_col]
    u=list(product(aux1,aux2))
    u.remove((i,j))
    return u

# peut-être limiter les cases regardées??
def actualPlat():
    """Procédure qui établit la liste des cellules qui vont mourir ou naître"""
    global plateau
    for i in range(nb_line):
        for j in range(nb_col):
            if plateau.t[i][j].vois>3:
                if plateau.t[i][j].etat==1:
                    plateau.t[i][j].chgSt(0)
                    for c in around(i,j):
                        plateau.t[c[0]][c[1]].vois=plateau.t[c[0]][c[1]].vois-1
            elif plateau.t[i][j].vois>2:
                if plateau.t[i][j].etat==0:
                    plateau.t[i][j].chgSt(1)
                    for c in around(i,j):
                        plateau.t[c[0]][c[1]].vois=plateau.t[c[0]][c[1]].vois+1
            elif plateau.t[i][j].vois<2:
                if plateau.t[i][j].etat==1:
                    plateau.t[i][j].chgSt(0)
                    for c in around(i,j):
                        plateau.t[c[0]][c[1]].vois=plateau.t[c[0]][c[1]].vois-1

    
#====== Affichage du jeu ====== 
def printGrid(plat): #plat de type Plateau
    for x in plat.t:
        for y in x:
            print(y.etat,end=" ")
        print(" ")
    print("- "*20)
    for x in plat.t:
        for y in x:
            print(y.vois,end=" ")
        print(" ")


        
#====== Arrêt du jeu =========
def stop(event=None):
    """Fin du jeu quand on appuie sur espace ou une touche de direction"""
    global fin,fen1, plateau, platJeu
    fin=True
    fen1.quit()

#====== Déroulement du jeu ====

    
def jeuBench(n): # for Benchmark, n=nbr d'itérations
    global fin, plateau, platJeu, t1
    if n>0:
        actualPlat()
        jeuBench(n-1) #for Benchmark

def jeu():
    global fin, plateau, platJeu
    actualPlat()
    if not(fin):
        fen1.after(100,jeu)  # on boucle après 200ms
 
############
### MAIN ###
############

#======Initialisation=============
##print("Souhaitez-vous :")
##print("0- Initialisation aléatoire de l'automate")
##print("1- Initialisation standard de l'automate")
##print("2- Initialisation personnalisée de l'automate")
##print("3- Initialisation à effet de l'automate")
##dbt=int(input())
##
##if dbt==0:
##    plateau=randGame()
##elif dbt==1:
##    plateau=initGame()
##elif dbt==2:
##    plateau=persoGame()
##else:
##    plateau=effetGame()


#====== Déroulement du jeu======

##fen1.bind("<Left>", stop) 
##fen1.bind("<Right>", stop)
##fen1.bind("<Up>", stop) 
##fen1.bind("<Down>", stop) 
##fen1.bind("<Key-space>", stop)    
s3,s4,s5=0,0,0
##nb_line=100
##nb_col=1000
##print(nb_line,nb_col)
##s1=0
##for i in range(100):
##    t1=time()
plateau=randGame()
##    jeuBench(100) #for Benchmark
##    t2=time()
##    s1=s1+t2-t1
##print("résultat moyen : ", s1/100)
jeu()
fen1.mainloop()
fen1.destroy()
