import random, time
import pygame
from pygame.locals import *

FPS = 25
(ECRAN_WIDTH, ECRAN_HEIGHT)=(600,600)

VIDE=0
R=1
V=2
B=3
N=4
J=5
COULEURS=[(0,0,0),(155,0,0),(0,155,0),(0,0,155),(155,0,155),(155,155,0)]

fallFreq=.5

## Partie I
def creerGrille(largeur, hauteur):
    return [[VIDE for j in range(hauteur)] for i in range(largeur)]
## Partie II
def grilleLibre(grille,k):
    largeur=len(grille)
    hauteur=len(grille[0])
    i=0
    while i < largeur:
        j=hauteur-k
        while j<hauteur and grille[i][j]==VIDE:
            j+=1
        if j==hauteur:
            return True
        i+=1
    return False

def descente(grille,x,y,k):
    if y!=0 and grille[x][y-1]==VIDE:
        for j in range(y,y+k):
            grille[x][j-1]=grille[x][j]
        grille[x][y+k-1]=VIDE
        y=y-1
    return y # Ne pas faire dans le sujet mais il faut
             # bien savoir que la piece est descendue

def deplacerBarreau(grille,x,y,k,direction):
    largeur=len(grille)
    # On commence par tester si on est pas au bord
    if x+direction<largeur and x+direction >=0:
        j=y
        # On détermine le nombre de cases vide dans la direction voulue
        while j<y+k and grille[x+direction][j]==VIDE:
            j+=1
        # S'il y en a k on peut déplacer tout le monde
        if j == y+k: 
            for j in range(y,y+k):
                grille[x+direction][j]=grille[x][j]
                grille[x][j]=VIDE
            x=x+direction
    return x # idem que pour descente

def permuterBarreau(grille,x,y,k):
    tempo=grille[x][y+k-1]
    for j in range(y+k-1,y,-1):
        grille[x][j]=grille[x][j-1]
    grille[x][y]=tempo

def descenteRapide(grille,x,y,k):
    saut=0
    # Tant qu'on pas en bas et que la case du dessous
    # est vide
    # L'évaluation paresseuse permet d'éviter les erreures
    while y-saut-1>=0 and grille[x][y-saut-1]==VIDE:
        saut+=1
    if saut !=0:
        for j in range(k):
            grille[x][y+j-saut]=grille[x][y+j]
            grille[x][y+j]=VIDE
    return y-saut

## Partie III
def detecteAlignement(rangee):
    n=len(rangee)
    marking=[False for j in range(n)]
    score=0
    couleur=rangee[0]
    lbloc=1
    for i in range(1,n):
        ncoul=rangee[i]
        if ncoul==VIDE or ncoul!=couleur:
            # On est en fin de chaine
            if lbloc>2:
                score+=lbloc-2
                for j in range(lbloc):
                    marking[i-1-j]=True
            lbloc=1
            couleur=ncoul
        else:
            # La chaine continue
            lbloc+=1
    #On n'oubli pas la dernière chaine
    if lbloc>2:
        score+=lbloc-2
        for j in range(lbloc):
            marking[n-1-j]=True
    return (marking, score)
#detecteAlignement([B, R, R, R, R, J, J, J, VIDE, VIDE, VIDE])

def scoreRangee(grille, g, i, j, dx, dy):
    if dx==0 and dy==0:
        return 0 #suite au rapport :(
    else:
        largeur=len(grille)
        hauteur=len(grille[0])
        x,y=i,j
        rangee=[]
        while 0 <= x and x<largeur and 0<=y and y < hauteur:
            rangee.append(grille[x][y])
            x+=dx
            y+=dy
        marking,score=detecteAlignement(rangee)
        n=len(rangee)
        for p in range(n):
            if marking[p]:
                g[i+p*dx][j+p*dy]=VIDE
        return score

def copie(grille):
    largeur=len(grille)
    hauteur=len(grille[0])
    return [[grille[i][j] for j in range(hauteur)] for i in range(largeur)]

def effaceAlignement(grille):
    g=copie(grille)
    largeur=len(grille)
    hauteur=len(grille[0])
    score=0

    # Horizontal
    dx,dy=1,0
    for j in range(hauteur):
        score+= scoreRangee(grille, g, 0, j, dx, dy)

    # Vertical |
    dx,dy=0,1
    for i in range(largeur):
        score+= scoreRangee(grille, g, i, 0, dx, dy)

    # Diagonal /
    dx,dy=1,1
    for i in range(largeur):
        score+= scoreRangee(grille, g, i, 0, dx, dy)
    for j in range(1,hauteur):
        score+= scoreRangee(grille, g, 0, j, dx, dy)

    # Diagonal \
    dx,dy=1,-1
    for i in range(largeur):
        score+= scoreRangee(grille, g, i, hauteur-1, dx, dy)
    for j in range(hauteur-1):
        score+= scoreRangee(grille, g, 0, j, dx, dy)

    return (g,score)

def tassementGrille(grille):
    largeur=len(grille)
    hauteur=len(grille[0])
    for x in range(largeur):
        saut=0
        for y in range(hauteur):
            if grille[x][y]==VIDE:
                saut+=1
            elif saut !=0:
                grille[x][y-saut]=grille[x][y]
                grille[x][y]=VIDE

def calculScore(grille):
    largeur=len(grille)
    hauteur=len(grille[0])
    score=0
    
    g,nscore=effaceAlignement(grille)
    tassementGrille(g)
    score+=nscore
    # On s'arrete quand le tassement ne fait pas augmenter le score
    while nscore !=0: 
        g,nscore=effaceAlignement(g)
        tassementGrille(g)
        score+=nscore
    # On met a jours la grille
    for x in range(largeur):
        for y in range(hauteur):
            grille[x][y]=g[x][y]
    return score


## Partie IV : Variante
def xDansGrille(grille, x):
    largeur = len(grille)
    return (x >= 0) and (x < largeur)

def yDansGrille(grille, y):
    hauteur = len(grille[0])
    return (y >= 0) and (y < hauteur)

def tailleRegionUnicolore(grille, x, y):
    g=copie(grille)
    return tailleRegionUnicoloreRec(g, x, y)

def tailleRegionUnicoloreRec(grille, x, y):
    nb = 1
    couleur = grille[x][y]
    if couleur == VIDE:
        return 0
    grille[x][y] = VIDE
    for dx in (-1, 1):
        if xDansGrille(grille, x + dx) and grille[x + dx][y] == couleur:
            nb += tailleRegionUnicoloreRec(grille, x + dx, y)
    for dy in (-1, 1):
        if yDansGrille(grille, y + dy) and grille[x][y + dy] == couleur:
            nb += tailleRegionUnicoloreRec(grille, x, y + dy)
    return nb



########## DEBUT ##########


M = [ [J, R, R, N, V, R, VIDE, VIDE, VIDE, VIDE, VIDE, VIDE],
[R, R, B, J, VIDE, R, N, V, VIDE, VIDE, VIDE, VIDE],
[J, N, N, R, VIDE, J, VIDE, VIDE, VIDE, VIDE, VIDE, VIDE],
[J, J, VIDE, R, VIDE, VIDE, VIDE, VIDE, VIDE, VIDE, VIDE, VIDE],
[N, VIDE, R, VIDE, VIDE, R, VIDE, VIDE, VIDE, VIDE, VIDE, VIDE],
[VIDE, V, VIDE, VIDE, VIDE, VIDE, VIDE, VIDE, VIDE, VIDE, VIDE, VIDE]]
M=creerGrille(6,12)
score=0

# Création d'une nouvelle pièce, c'est une version modifiée de
# grilleLibre
def creerPiece(grille,k):
    """grilleLibre a été lancée avant donc pas de problème"""
    largeur=len(grille)
    hauteur=len(grille[0])
    xpossible=[]
    i=0
    while i < largeur:
        j=hauteur-k
        while j<hauteur and grille[i][j]==VIDE:
            j+=1
        if j==hauteur:
            xpossible.append(i)
        i+=1
    x=xpossible[random.randint(0,len(xpossible)-1)]
    y=len(grille[0])-k
    lst_coul=[random.randint(1,5) for l in range(3)]
    for l in range(k):
        M[x][y+l]=lst_coul[l]
    return x,y 

k= 3
x,y = creerPiece(M,k)

    
def affichage_pygame(ecran,M,score):
    # Ou on commence le dessin de la matrice
    debut_x,debut_y=250,400
    # Taille des cases
    larg=25
    
    # On charge une police (64 est la taille)
    police_car = pygame.font.SysFont("None", 64)

    # On vide l'ecran
    ecran.fill((0,0,0))

    # On affiche le plateau de jeu
    for i in range(len(M)):
        for j in range(len(M[i])):
            pygame.draw.rect(ecran, COULEURS[M[i][j]],(debut_x+i*larg,debut_y-j*larg,larg,larg))

    # On affiche la pièce qui tombe
    #_,x,y,k,lst_coul=piece
    #for l in range(k):
    #    pygame.draw.rect(ecran, COULEURS[lst_coul[l]],(debut_x+x*larg,debut_y-(y+l)*larg,larg,larg))
        
    

    
    # On délimite le plateau
    pygame.draw.rect(ecran, (255,255,255),(debut_x-1,debut_y-(len(M[0])-1)*larg-1,len(M)*larg+2,len(M[0])*larg+2),1)


    # On dessine "l'interface", les coordonnees ont ete choisies a la main
    ecran.blit(pygame.font.SysFont("None", 100).render("Tetris Couleur",True,(0,255,255)),(50,10))
    ecran.blit(pygame.font.SysFont("None", 32).render("Utilisez les fleches du clavier",True,(255,0,255)),(150,520))
    ecran.blit(pygame.font.SysFont("None", 32).render("Score :"+str(score),True,(255,255,0)),(450,255))
    pygame.display.flip()



pygame.init()
FPSCLOCK = pygame.time.Clock()
pygame.font.init()
ecran = pygame.display.set_mode((ECRAN_WIDTH, ECRAN_HEIGHT))
pygame.display.set_caption("Tetris Couleur")

police_sys64 = pygame.font.SysFont("None", 64)
police_sys96 = pygame.font.SysFont("None", 128)


lastFallTime = time.time()



#affichage_pygame(ecran,M,score)

en_jeu=True
perdu=False

while en_jeu:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            en_jeu=False
        if event.type == KEYDOWN:
            if event.key == K_ESCAPE:
                en_jeu=False
            if event.key == K_LEFT:
                x=deplacerBarreau(M,x,y,k,-1)
            if event.key == K_RIGHT:
                x=deplacerBarreau(M,x,y,k,1)
            if event.key == K_UP:
                permuterBarreau(M,x,y,k)
            if event.key == K_DOWN:
                y=descenteRapide(M,x,y,k)
                #Forcer une descente 
                lastFallTime=time.time() - fallFreq
    if time.time() - lastFallTime > fallFreq:
        yn=descente(M,x,y,k)
        if yn!=y:
            y=yn
        else:
            score+=calculScore(M)
            if grilleLibre(M,k):
                x,y = creerPiece(M,k)
            else:
                perdu=True
                en_jeu=False
        lastFallTime=time.time()

    affichage_pygame(ecran,M,score)
    FPSCLOCK.tick(FPS)

if perdu:
    ecran.blit(police_sys96.render("Perdu !",True,(255,255,0)),(50,250))
    print("Domage, vous avez perdu, avec un score de "+str(score))
else:
    ecran.blit(police_sys96.render("FIN !",True,(255,0,0)),(50,250))
    print("Pourquoi abondonner ? Vous avec un score de "+str(score))
pygame.display.flip()
pygame.time.wait(500)
pygame.quit()
