import numpy as np
import random as rd

## domineering
## les coups qu'un joueur peut faire
def coupspos(T : np.ndarray, J : int) -> list :
    """Prend en argument un tableau T correspondant à
    un état du jeu et un numéro J correspondant au
    joueur qui peut jouer et renvoie la liste des coups
    que peut jouer le joueur J
    """
    L = []
    if J == 2 : # le joureur 1 déterminer la liste des
        # positions des dominos horizontaux qu'il peut placer
        nl, nc = len(T), len(T[0])-1
        for i in range(nl) :
            for j in range(nc) :
                if T[i,j] == T[i,j+1] == 0 :
                    L.append((i,j))
    else : # le joureur 2 déterminer la liste des
        # positions des dominos verticaux qu'il peut placer
        nl, nc = len(T)-1, len(T[0])
        for i in range(nl) :
            for j in range(nc) :
                if T[i,j] == T[i+1,j] == 0 :
                    L.append((i,j))
    return L

## Jouer et annuler un coup
def jouer(T : np.ndarray, J : int, l : tuple) -> None :
    """ Prend en argument un tableau T correspondant à un 
    état du jeu, entier J correspondant au joueur et un 
    couple correspondant à la coordonnée du domino et 
    modifie le tableau pour qu'il soit dans l'état après 
    avoir joué un domino à cette position
    """
    if J == 1 :
        T[l[0],l[1]], T[l[0]+1,l[1]] = 1, 1
    else :
        T[l[0],l[1]], T[l[0],l[1]+1] = 2, 2
    
def annuler(T : np.ndarray, J : int, l : tuple) -> None :
    """ Prend en argument un tableau T correspondant à un 
    état du jeu, entier J correspondant au joueur et un 
    couple correspondant à la coordonnée du domino et modifie 
    le tableau pour qu'il soit dans l'état avant avoir 
    joué un domino à cette position
    """
    if J == 1 :
        T[l[0],l[1]], T[l[0]+1,l[1]] = 0, 0
    else :
        T[l[0],l[1]], T[l[0],l[1]+1] = 0, 0

## heuristique
def heuristique(T : np.ndarray) -> float :
    """ Prend en argument un tableau T correspondant à un 
    état du jeu et renvoie l'heuristique pour le joueur 1 
    correspondant à cet état c'est à dire le nombre de coups 
    que peut jouer le joueur 1 - le nombre de coups que peut j
    ouer le joueur 2 sauf si les des deux joueurs ne peut 
    pas jouer au quel cas, il renvoi +/- l'infini en fonction 
    de la situtation
    """
    n1, n2 = len(coupspos(T,1)), len(coupspos(T,2))
    if n1 == 0 :
        return -float('inf')
    if n2 == 0 :
        return float('inf')
    return n1-n2

## algorithme minmax
def minmax(T : np.ndarray, J : int, p : int) -> (float, tuple) :
    """ Prend en argument un tableau T corrsepondant à un
    état du jeu, un entier J correspondant au joueur et un
    entier p correspondant au nombre de coup anticipé et 
    renvoie l'heuristique calculer calculé avec la méthode du 
    minmax ainsi que un des coups ayant l'heuristique la plus
    favorable.
    """
    L = coupspos(T, J)
    if L == [] or p == 0 :
        return heuristique(T), None
    H = []
    for l in L :
        jouer(T, J, l)
        h = minmax(T, 3-J, p-1)[0]
        annuler(T, J, l)
        H.append(h)
    if J == 2 :
        m = min(H)
    else : 
        m = max(H)
    Lp = []
    for i in range(len(L)) :
        if H[i] == m :
            Lp.append(L[i])
    return m, rd.choice(Lp)

# un exemple simple
T = np.zeros((4,4), dtype = int)
jouer(T, 2, (1,2))
jouer(T, 1, (0,1))
jouer(T, 2, (2,0))
print(heuristique(T))
l = minmax(T, 1, 2)
print(l)
"""
"""

## Pour simuler plusieurs parties et calculer le nombre de partie perdue
def affrontement(n : int, m : int, p : int) -> int :
    """ Prend en argument deux entiers n et m disignant respectivement
    le nombre de lignes, le nombre de colonnes, un entier p correspondant
    au nombre de coups d'avances que le joueur 1 examine, et qui simule
    une partie et renvoie le numéro du gagnant.
    """
    T = np.zeros((n, m), dtype = int)
    L = coupspos(T, 1)
    J = 1
    while L != [] :
        if J == 1 :
            l = minmax(T,J,p)[1]
        else  :
            #l = minmax(T,J,1)[1] 
            l = rd.choice(coupspos(T, 2))
        jouer(T, J, l)
        J = 3-J
        L = coupspos(T, J)
    return J
"""
N = 10**3
p = 2
n, m = 5, 5
pA = 0
for i in range(N) :
    pA += (affrontement(n,m,p) == 1)
print(pA/N)
"""