from random import choice


# 1
def init():
    return [[0 for y in range(6)] for x in range(7)]


def affiche(G):
    print()
    for y in range(5, -1, -1):
        ligne = ''
        for x in range(7):
            if G[x][y] == 0:
                ligne += '. '
            elif G[x][y] == 1:
                ligne += 'o '
            else:  # G[x][y] == 2
                ligne += 'x '
        print(ligne)


G = init()
G[0][0] = 1
G[3][0] = 2
G[3][1] = 1
affiche(G)


## 2
def coups(G):
    def premier_libre(x):
        """
        renvoie le plus petit y tel que (x, y) est libre, -1 si colonne pleine
        """
        for y in range(6):
            if G[x][y] == 0:
                return y
        return -1
    res = []
    for x in range(7):
        y = premier_libre(x)
        if y != -1:
            res.append((x, y))
    return res


affiche(G)
print(coups(G))


##

def tous_alignements():
    res = []
    # horizontaux
    for x in range(4):
        for y in range(6):
            ligne = []
            for k in range(4):
                ligne.append((x + k, y))
            res.append(ligne)
    # verticaux
    for x in range(7):
        for y in range(3):
            ligne = []
            for k in range(4):
                ligne.append((x, y + k))
            res.append(ligne)
    # diag nord-est
    for x in range(4):
        for y in range(3):
            ligne = []
            for k in range(4):
                ligne.append((x + k, y + k))
            res.append(ligne)
    # diag nord-ouest
    for x in range(3, 7):
        for y in range(3):
            ligne = []
            for k in range(4):
                ligne.append((x - k, y + k))
            res.append(ligne)
    return res


tous = tous_alignements()


## 3
def fini(G):
    for ligne in tous:
        for joueur in [1, 2]:
            res = True
            k = 0
            while res and k < 4:
                x, y = ligne[k]
                res = (G[x][y] == joueur)
                k += 1
            if res:
                return joueur
    return 0


print(fini(G))
for x in range(4, 7):
    G[x][0] = 2
for x in range(4, 6):
    G[x][1] = 1
affiche(G)
print(fini(G))


## 4
def joue_hasard():
    G = init()
    joueur = 1
    for i in range(42):  # au plus 42 coups
        L = coups(G)
        x, y = choice(L)
        G[x][y] = joueur
        joueur = 3 - joueur
        if fini(G) != 0:
            return G
    return G


G = joue_hasard()
affiche(G)
print(fini(G))

##
N = 10000
nb = 0
for k in range(N):
    G = joue_hasard()
    if fini(G) == 1:
        nb += 1
print(nb/N)


## 5
def heuristique(G, w):
    def n(ligne, joueur):
        res = 0
        for x, y in ligne:
            if G[x][y] == joueur:
                res += 1
        return res
    score = 0
    for ligne in tous:
        n1 = n(ligne, 1)
        n2 = n(ligne, 2)
        if n2 == 0:
            score += w[n1]
        elif n1 == 0:
            score -= w[n2]
    return score


## 6
def choix_maxi(scores_coups):
    """
    scores_coups est une liste de couples non vide dont le premier élément
    est un nombre
    la fonction renvoie un couple parmi ceux qui ont le plus grand premier
    élément.
    """
    score_maxi = scores_coups[0][0]
    L_coups = [scores_coups[0][1]]
    for k in range(1, len(scores_coups)):
        if scores_coups[k][0] > score_maxi:
            score_maxi = scores_coups[k][0]
            L_coups = [scores_coups[k][1]]
        elif scores_coups[k][0] == score_maxi:
            L_coups.append(scores_coups[k][1])
    return score_maxi, choice(L_coups)


def choix_mini(scores_coups):
    score_mini = scores_coups[0][0]
    L_coups = [scores_coups[0][1]]
    for k in range(1, len(scores_coups)):
        if scores_coups[k][0] < score_mini:
            score_mini = scores_coups[k][0]
            L_coups = [scores_coups[k][1]]
        elif scores_coups[k][0] == score_mini:
            L_coups.append(scores_coups[k][1])
    return score_mini, choice(L_coups)


def minmax(G, joueur, w, p):
    """
    renvoie le score pour une profondeur p pour la grille G ainsi que
    le coup à jouer.
    """
    L = coups(G)
    if p == 0 or len(L) == 0:
        return heuristique(G, w), None
    scores_coups = []  # liste des couples (score, coup) des successeurs
    for coup in L:
        x, y = coup
        # joueur le coup
        G[x][y] = joueur
        scores_coups.append((minmax(G, 3 - joueur, w, p - 1)[0], coup))
        G[x][y] = 0
    if joueur == 1:
        return choix_maxi(scores_coups)
    else:
        return choix_mini(scores_coups)


## 7
def partie_humain_machine(w, p):
    G = init()
    joueur = 1
    for i in range(42):  # au plus 42 coups
        affiche(G)
        if joueur == 1:
            x = int(input("numéro de colonne : "))
            y = 0
            while G[x][y] != 0:
                y += 1
            G[x][y] = 1
        else:  # joueur 2 : machine
            score, coup = minmax(G, joueur, w, p)
            x, y = coup
            G[x][y] = joueur
        joueur = 3 - joueur
        gagnant = fini(G)
        if gagnant != 0:
            affiche(G)
            if gagnant == 1:
                print("gagné")
            else:
                print("perdu")
            return G
    return G


pinf = float('inf')
w = [0, 1, 10, 30, pinf]
partie_humain_machine(w, 2)


## 8
def partie2IA(w1, w2, p1, p2, dis):
    W = [w1, w2]
    P = [p1, p2]
    G = init()
    joueur = 1
    for i in range(42):  # au plus 42 coups
        score, coup = minmax(G, joueur, W[joueur - 1], P[joueur - 1])
        x, y = coup
        G[x][y] = joueur
        if dis:
            affiche(G)
        joueur = 3 - joueur
        if fini(G) != 0:
            return G
    return G

print(fini(partie2IA(w, w, 1, 4, False)))

## 9
W = [[0, 1, 10, 100, pinf],
     [0, 1, 10, 1000, pinf],
     [0, 1, 10, 30, pinf],
     [0, 1, 2, 4, pinf]]

# W = [W[0], W[3]]
# G= partie2IA(W, True)
# print(gagnant)

N = 10
parties_gagnees = [[0 for k in range(4)] for j in range(4)]

for i in range(4):
    for j in range(4):
        for k in range(N):
            G = partie2IA(W[i], W[j], 2, 2, False)
            if fini(G) == 1:
                parties_gagnees[i][j] += 1
print(parties_gagnees)
