#########################################################
###                   Puissance 4                     ###
#########################################################

import random as rd
import numpy as np
import time as t


## Fonction init

def init():
    return [[0 for i in range(6)] for j in range(7)]

## Fonction qui affiche une grille dans le shell

def display(G:list):
    D=['.','x','o']
    for y in range(6):
        s=''
        for x in range(7):
            s+=D[G[x][5-y]]+' '
        print(s)

## Fonction qui renvoie un liste de tous les alignements de 4 pions

def tous_align()->list:
    T=[]
    # Alignement gagnants Horizontaux
    for y in range(6):
        for x in range(4):
            T.append(((x,y),(x+1,y),(x+2,y),(x+3,y)))
    # Alignement gagnants Verticaux
    for x in range(7):
        for y in range(3):
            T.append(((x,y),(x,y+1),(x,y+2),(x,y+3)))
    # Alignements gagnants de bas-gauche vers Haut-droite
    for x in range(4):
        for y in range(3):
            T.append(((x,y),(x+1,y+1),(x+2,y+2),(x+3,y+3)))
    # Alignements gagnants de bas-droite vers Haut-gauche
    for x in range(4):
        for y in range(3,6):
            T.append(((x,y),(x+1,y-1),(x+2,y-2),(x+3,y-3)))
    return T

## On définit la liste des alignements gagants

A_gagnants = tous_align()

## Fonction fini

def fini(G:list)->int:
    for align in A_gagnants:
        if sum(G[x][y]==1 for x,y in align)==4:
            return 1
        if sum(G[x][y]==2 for x,y in align)==4:
            return 2
    return 0

## Fonction coups

def coups(G:list)->list:
    L=[]
    for x in range(7):
        y=6
        while y>0 and G[x][y-1]==0:
            y-=1
        if y<6:
            L.append((x,y))
    return L

## Jeux au hasard (Affichage du résultat et de la grille)

def joue_random()->(int,list):
    joueur=1
    G=init()
    L=coups(G)
    while len(L)!=0:
        tirage=rd.randrange(len(L))
        x,y=L[tirage]
        G[x][y]=joueur
        gagnant=fini(G)
        if gagnant!=0:
            return (gagnant,G)
        joueur=3-joueur
        L=coups(G)
    return (0,G)

def test_random():
    gagnant,Grille=joue_random()
    if gagnant==0:
        print("Match nul")
        display(Grille)
    else:
        print("Le gagnant est le joueur",gagnant)
        display(Grille)

def probabilites_hasard(k:int):
    n1,n2=0,0
    for i in range(k):
        R=joue_random()
        if R[0]==1 : n1+=1
        if R[0]==2 : n2+=1
    print("le joueur 1 a gagné", n1,"fois. Soit",int(n1*10000/k)/100,"% des parties")
    print("le joueur 2 a gagné", n2,"fois. Soit",int(n2*10000/k)/100,"% des parties")
    print("Il y a eu",k-n1-n2,"matchs nuls. Soit",int((k-n1-n2)*10000/k)/100,"% des parties")

## Algorithme MinMax

W1 = [0,1,2,4,np.inf]
W2 = [0,1,10,100,np.inf]

## Fonction calculant de le nombre de points des 2 joueurs dans un alignement gagnant

# def nbr_pions(A:tuple,G:list,joueur:int)->(int,int):








## Fonction renvoyant l'utilité de la grille pour un joueur avec une liste de poids W

# def utilite(G:list,j:int,W:list)->float:









# def minmax(G:list,j:int,W:list,p:int)->(float,tuple):























## Jeu machine machine

def joue_partie(W:list,p1:int,p2:int,affiche:bool)->(int,list):
    joueur=1
    G=init()
    L=coups(G)
    while len(L)!=0:
        if joueur==1:
            Uopt,c=minmax(G,joueur,W,p1)
        if joueur==2:
            Uopt,c=minmax(G,joueur,W,p2)
        if c==None : display(G)
        x,y=c
        G[x][y]=joueur
        if fini(G)!=0:
            if affiche :
                display(G)
                print("Le gagnant est le joueur :",joueur)
            gagnant=joueur
            break
        joueur=3-joueur
        L=coups(G)
    if fini(G)==0:
        if affiche:
            display(G)
            print("Match nul")
        gagnant=0
    return gagnant,G

def mesure_force(W:list,p1:int,p2:int,k:int):
    n1,n2=0,0
    for i in range(k):
        R=joue_partie(W,p1,p2,False)
        if R[0]==1 : n1+=1
        if R[0]==2 : n2+=1
        print(i,end=' ')
    print('\n')
    n=k-(n1+n2)
    print("le joueur 1 a gagné", n1,"fois. Soit",int(n1*10000/k)/100,"% des gains")
    print("le joueur 2 a gagné", n2,"fois. Soit",int(n2*10000/k)/100,"% des gains")
    print("Il y a eu",n,"matchs nuls. Soit",int(n*10000/k)/100,"% des parties")

## Jeu machine humain

def joue_partie_h_m(W:list,p:int):
    joueur=1
    G=init()
    for i in range(42):
        L=coups(G)
        if joueur ==1 :
            L_col,Entete=[],''
            c=0
            for i in range(7):
                if i==L[c][0]:
                    L_col+=[i]
                    Entete+=str(i+1)+' '
                    c+=1
                else:
                    Entete+='  '
            print(Entete)
            display(G)
            col=int(input('Choisissez la colonne pour jouer votre pion : '))-1
            while col not in L_col:
                col=int(input('Celle-ci est complète choisissez une autre colonne : '))
            for couple in L:
                if couple[0]==col:
                    x,y=couple
                    G[x][y]=1
            print('La grille après votre coup :')
            display(G)
            t.sleep(1)
        if joueur==2:
            Uopt,coup=minmax(G,joueur,W,p)
            x,y=coup
            G[x][y]=2
            print('La grille après mon coup :')
            display(G)
            t.sleep(1)
        Resultat=fini(G)
        if Resultat==1:
            print('BRAVO vous avez gagné')
            break
        if Resultat==2:
            print("Désolé mais j'ai gagné")
            break
        joueur=3-joueur
