#!/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')

#=======Variables globales========
nb_line=10 # nombre de lignes du plateau
nb_col=10  # 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é

#======= Initialisation du plateau ==========
def initGame():
    """ Fonction qui initialise le plateau de jeu"""
    global nb_line, nb_col
    nb_line=5
    nb_col=7
    return [[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():
    plat=[]
    for i in range(nb_line):
        plat.append([])
    for i in range(nb_line):
        for j in range(nb_col):
            plat[i].append(randint(0,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


def mortOuPas(i,j):
    """Fonction qui détermine l'état de la cellule (i,j) au pas de temps t+1 """
    global plateau, platJeu
    sumCell=0
    for x in around(i,j):
        sumCell+=int(plateau[x[0]][x[1]])
    if sumCell>3:
        return 0
    elif sumCell>2:
        return 1
    elif sumCell==2:
        return int(plateau[i][j])
    else:
        return 0


def actualPlat():
    global plateau, platJeu
    chg=deepcopy(plateau)
    for i in range(nb_line):
        for j in range(nb_col):
            chg[i][j]=mortOuPas(i,j)
            if chg[i][j]==1 and plateau[i][j]==0:
                platJeu[(i,j)]=can1.create_rectangle(j*cc+cc/2, i*cc+cc/2, (j+1)*cc+cc/4, (i+1)*cc+cc/4, fill="black")
            elif chg[i][j]==0 and plateau[i][j]==1:
                can1.delete(platJeu[(i,j)])
                del platJeu[(i,j)]
    plateau=chg
 
        
#====== Affichage du jeu ======
def convGraph(plat):
    """Convertir un plateau de jeu en petit carrés à afficher"""
    aux={}
    for i in range(nb_line):
        for j in range(nb_col):
            if plat[i][j]:
                aux[(i,j)]=can1.create_rectangle(j*cc+cc/2, i*cc+cc/2, (j+1)*cc+cc/4, (i+1)*cc+cc/4, fill="black")
    return aux

def printGrid(plat):
    for x in plat:
        for y in x:
            print(y,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 ====
#Benchmark (pour 100 itérations) en cherchant dans un dictionnaire
    # jeu de taille 10x10 : 0.54348015
    # jeu de taille 100x10 : 5.453520059585571
    # jeu de taille 100x50 : 28.911434412002563
    # jeu de taille 100x100 : 57.32755661010742
    # jeu de taille 100x1000 : 582.0550246238708

# Avec la première amélioration en regardant la case plutôt que le dictionnaire
    # jeu de taille 10x10 : 0.48311424255371094
    # jeu de taille 100x10 :  5.270522832870483
    # jeu de taille 100x50 : 26.67779564857483
    # jeu de taille 100x100 : 53.33436393737793
    # jeu de taille 100x1000 : 561.8931813240051
    
def jeu(n): #n : itération de jeu
    global fin, plateau, platJeu, t1
    if n>0:
        actualPlat()
   # if not(fin):
   #     fen1.after(100,jeu)  # on boucle après 500ms
        jeu(n-1)
    else:
        t2=time()
        print("durée d'exécution pour 100 itérations", t2-t1)

############
### 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()
    

#====== Création du plateau =====
fen1=Tk()
fen1.title("Le jeu de la vie")
fen1.configure(width=nb_col*cc+2*cc, height=nb_line*cc+2*cc)
can1=Canvas(fen1, bg='white', height=nb_line*cc+cc, width=nb_col*cc+cc)
can1.pack(padx=5,pady=5)


platJeu=convGraph(plateau)

#====== 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)    
t1=time()
jeu(100)
fen1.mainloop()
fen1.destroy()
