#
#
#   Un MasterMind en Python
# 
#Décembre 2011
#Mickael Renault



#Pour faire fonctionner le programme, on va avoir besoin des modules "random" et "turtle".
from random import *
from turtle import *

#Avant toute chose, on établit la taille de la fenêtre de jeu, car quand on va importer le module "grille", certaines fonctions se basent sur celle-ci.
#Si l'on ne précise pas maintenant, on aura des bugs au niveau de l'affichage (dimensions de la grille, des cases, etc...
setup (width=500, height=650)

#Comme prévu, on importe le module "grille" (qui doit être dans le même répertoire que ce programme).
import grille


#---------------------------------------#
#      FONCTIONS D'INITIALISATION       #
#---------------------------------------#

# fonction qui crée et renvoie une liste de pions-solutions à la volée, en choisissant leur couleur aléatoirement dans la liste donnée en paramètre.
def liste_solution(liste):
    sol=[]
    for i in range(4):
        pion=randint(0,len(liste)-1)
        sol.append(liste[pion])
    return sol


#Fonction qui renvoie la ligne et la colonne où l'utilisateur est en train de placer un pion.
#Pour cela on utilise la variable-compteur "coup" qui recence le nombre de fois où l'utilisateur a joué un pion.
def OuJouer(coup):
    colonne=1+coup%4
    ligne=coup//4
    return [colonne, ligne]


#-----------------------------------#
#      FONCTIONS DE GRAPHISME       #
#-----------------------------------#

# Fonction qui affiche la solution en haut de la grille
def affiche_resultat(listsolution):
    bouton(grille.xgau+grille.largeurColonne,grille.ybas+grille.hauteur,4*grille.largeurColonne,grille.hauteurLigne,"")
    x=-3*grille.largeurColonne/2
    y=grille.hauteur/2+3
    r=grille.hauteurLigne/2 -3
    for i in range(4):
        Cercle(x,y,r,listsolution[i])
        x+=grille.largeurColonne
 
#Fonction qui crée un cercle de diamètre "diam", à la position (x,y). Le contour est noir et l'intérieur est de couleur "c".
def Cercle(x,y,diam,c) :
    grille.va(x,y)
    setheading(0)
    pencolor("black")
    fillcolor(c)
    begin_fill()
    circle(diam)
    end_fill()

#Simple fonction graphique qui dessinne un bouton à la position (x,y) (point en à bas-gauche), de dimensions "largeur"x"hauteur", contenant le "texte".
def bouton(x,y,largeur,hauteur,texte):  
    grille.va(x,y)                      
    setheading(0)
    pencolor("black")
    fillcolor("darkgrey")  # <<--- Si l'on veut changer la couleur de fond des boutons, c'est ici!
    begin_fill()
    for r in range(0,2) :
        forward(largeur)
        left(90)
        forward(hauteur)
        left(90)
    end_fill()
    grille.va(x+largeur/2,y+hauteur/4)
    write(texte,False,"center",("Arial", 10, "normal"))
    

#Fonction qui joue (dessine) un pion dans la grille. Pour cela, on regarde où jouer,
# et on le dessine de la couleur précisée en paramètre en utilisant la fonction Cercle.
def placepion(color):
    [colonne, ligne]= OuJouer(coup)
    [x,y]=grille.positionDansFenetre(colonne,ligne)
    x+=grille.largeurColonne/2
    y+=3
    Cercle(x,y,grille.hauteurLigne/2 -3,color)


#Fonction qui dessine le résultat intermédiaire à la fin de chaque ligne (mini-pions blancs ou noirs), en fonction de la liste de 0 et 1 donnée en paramètre.
def affiche_resultat_intermediaire(listintermed):
    [colonne, ligne]= OuJouer(coup-1)                   #On retrouve les coordonées du dernier coup joué.
    [x,y]=grille.positionDansFenetre(colonne+1,ligne)   #On va alors se placer dans la case suivante, c'est à dire sur la même ligne, dans la dernière colonne.
    x-=grille.largeurColonne/5-3
    y+=grille.hauteurLigne/3
    
    for element in listintermed:                        #Pour chaque élément de la liste,
        if element==0:                                  #si c'est un 0, on affiche un mini-pion blanc,
            Cercle(x+grille.largeurColonne/3,y,grille.hauteurLigne/4-3,"white")
        elif element==1:                                #si c'est un 0, on affiche un mini-pion noir.
            Cercle(x+grille.largeurColonne/3,y,grille.hauteurLigne/4-3,"black")
        x+=grille.largeurColonne/5


#--------------------------------------#
#      INTELLIGENCE ARTIFICIELLE       #
#--------------------------------------#


# Fonction au coeur de la stratégie du jeu:
#
# On crée 2 listes temporaires: l'une avec la solution, l'autre avec les couleurs choisies par l'utilisateur.
# On crée une liste-bilan composée de 0 (pour "bien placé"), et de 1 (pour "existe mais mal placé").
#
# Exemple:
#   Solution:   ["orange",  "purple",   "darkred",  "gold"]
#   Joueur:     ["orange",  "darkred",  "purple",   "darkblue"]
#   Bilan:  []
#
# Etape 1: On supprime les éléments qui sont égaux (même couleur, même place), et on ajoute le nombre de 0 correspondnat dans la liste-bilan.
#
#   Solution:   ["purple",   "darkred",  "gold"]
#   Joueur:     ["darkred",  "purple",   "darkblue"]
#   Bilan:  [0]
#
# Etape 2: On supprime les éléments qui existent dans les 2 listes, et on ajoute le nombre de 1 correspondnat dans la liste-bilan.
#
#   Solution:   ["gold"]
#   Joueur:     ["darkblue"]
#   Bilan:  [0,1,1]
#
# Etape 3: On retourne la liste-bilan et on réinitialise la liste du joueur qui passe à une nouvelle ligne.

def resultat_intermediaire(listsolution):
    global joueurlist

    #Initialisation:
    liste1=joueurlist[0:]
    liste2=listsolution[0:]
    intermediaire=[]

    #Etape 1:
    for i in range(4):
        if joueurlist[i]==listsolution[i]:
            intermediaire.append(0)
            liste1[i]="suppr"
            liste2[i]="suppr"

    while liste1.count("suppr")>0:
        liste1.remove("suppr")
        liste2.remove("suppr")

    #Etape 2:
    for j in range(len(liste2)):
        for k in range(len(liste1)):
            if liste2[j]==liste1[k] and liste1[k]!="suppr":
                liste2[j]="suppr"
                liste1[k]="suppr"
                intermediaire.append(1)
                break
            
    while liste1.count("suppr")>0:
        liste1.remove("suppr")
        liste2.remove("suppr")

    #Etape 3:
    joueurlist=[]
    return intermediaire



#----------------------------------------------#
#      INTERACTION PROGRAMME-UTILISATEUR       #
#----------------------------------------------#

#En fonction de l'endroit où l'on clique, soit on joue un coup en placant un pion, soit on remet à zéro, soit on affiche la solution, soit on quitte le jeu...
#Pour donner une interaction quand on clique dessus, il faut ajouter une ligne dans la fonction clic()
def clic(x, y, listsolution):
    global coup, joueurlist, fini

    if fini==False: #Si le jeu n'est pas fini:
        #Si l'on clique dans (ou proche) du dessin pion violet, le programme place un pion violet dans la grille et mémorise qu'un coup a été joué.
        #On ajoute à la liste de pions proposés par l'utilisateur la couleur "purple".
        if -3*grille.largeurColonne<x<-2*grille.largeurColonne and -grille.hauteur*2/3<y<-grille.hauteur*2/3+grille.hauteurLigne :
            placepion("purple")
            coup+=1
            joueurlist.append("purple")

        #De même avec le pion bleu foncé
        elif -2*grille.largeurColonne<x<-grille.largeurColonne and -grille.hauteur*2/3<y<-grille.hauteur*2/3+grille.hauteurLigne :
            placepion("darkblue")
            coup+=1
            joueurlist.append("darkblue")

        #De même avec le pion vert foncé
        elif -grille.largeurColonne<x<0 and -grille.hauteur*2/3<y<-grille.hauteur*2/3+grille.hauteurLigne :
            placepion("darkgreen")
            coup+=1
            joueurlist.append("darkgreen")

        #De même avec le pion jaune
        elif 0<x<grille.largeurColonne and -grille.hauteur*2/3<y<-grille.hauteur*2/3+grille.hauteurLigne :
            placepion("gold")
            coup+=1
            joueurlist.append("gold")

        #De même avec le pion orange
        elif grille.largeurColonne<x<2*grille.largeurColonne and -grille.hauteur*2/3<y<-grille.hauteur*2/3+grille.hauteurLigne :
            placepion("orange")
            coup+=1
            joueurlist.append("orange")

        #De même avec le pion rouge
        elif 2*grille.largeurColonne<x<3*grille.largeurColonne and -grille.hauteur*2/3<y<-grille.hauteur*2/3+grille.hauteurLigne :
            placepion("darkred")
            coup+=1
            joueurlist.append("darkred")

        #Si l'on a épuisé tous les coups disponibles, ou si l'utilisateur clique sur le bouton "Solution",
        #on affiche le résulat, retourne un message à l'utilisateur, et met fin au jeu.
        if coup==48 or grille.xgau-2*grille.largeurColonne<x<grille.xgau-2*grille.largeurColonne+80 and 100<y<140 :
            affiche_resultat(listsolution)
            grille.va(0,0)
            color("red")
            write("Perdu... :-(",False,"center",("Arial", 30, "normal"))
            fini=True
            
        #Si l'on vient de proposer 4 pions, on affiche le résultat intermédiaire.
        #Si le résultat intermédiaire correspond à 4 mini-pions blanc, l'utilisateur a gagné et le jeu est fini!
        if coup%4==0 and joueurlist!=[]:
            listintermed=resultat_intermediaire(listsolution)
            affiche_resultat_intermediaire(listintermed)
            if listintermed==[0,0,0,0]:
                affiche_resultat(listsolution)
                grille.va(0,0)
                color("red")
                write("Gagné!!!",False,"center",("Arial", 30, "normal"))
                fini=True

    #Que l'utilisateur ait gagné, perdu ou n'ai pas fini de jouer,
    # s'il clique sur "Reset", la grille et la solution se renouvelle et le jeu recommence.
    if grille.xgau-2*grille.largeurColonne<x<grille.xgau-2*grille.largeurColonne+80 and 0<y<40:
        reset()
        jouer()
    # s'il clique sur "Quitter", le programme se ferme.
    elif grille.xgau-2*grille.largeurColonne<x<grille.xgau-2*grille.largeurColonne+80 and -100<y<-60:
        exit()



#--------------------------------------#
#      CONSTRUCTEUR DU PROGRAMME       #
#--------------------------------------#

def jouer():
    #Initialisation des variables. Les variable globales sont utilisables dans toutes les fonctions du programme:
    global coup         #retient le nombre de fois où l'on a cliqué sur un pion de couleur
    global joueurlist   #retient la combinaison proposée par l'utilisateur
    global fini         #retient l'état du jeu: "true" s'il est fini, "false" sinon.
    coup=0
    joueurlist=[]
    fini=False

    listepions=["purple","darkblue","darkgreen","gold","orange","darkred"] #Couleurs disponibles dans le jeu
    listsol=liste_solution(listepions)  #On détermine la liste solution du jeu
    print(listsol)                      # <<--- Utile pour vérifier le fonctionnement du jeu, mais à retirer si l'on souhaite le faire essayer à quelqu'un.

    #Début du programme d'affichage
    ht()    #On cache le curseur Turtle
    grille.initialiseGrille(clic,listsol,12,6,110)
    grille.dessineGrille("grey")        # <<--- Pour changer la couleur de la grille, c'est ici!
    # Affichage des nombres d'essais
    pencolor("black")
    x=-grille.largeur/2+grille.largeurColonne/2
    y=-grille.hauteur/2+grille.hauteurLigne/3
    for i in range(12):
        grille.va(x, y+i*grille.hauteurLigne)
        write(str(i+1)+".",False,"center",("Arial", 10, "normal"))

    #Affichage des pions sur lesquels pourra cliquer l'utilisateur
    Cercle(-5*grille.largeurColonne/2,-grille.hauteur*2/3,grille.hauteurLigne/2 -3, 'purple')
    Cercle(-3*grille.largeurColonne/2,-grille.hauteur*2/3,grille.hauteurLigne/2 -3, 'darkblue')
    Cercle(-grille.largeurColonne/2,-grille.hauteur*2/3,grille.hauteurLigne/2 -3, 'darkgreen')
    Cercle(grille.largeurColonne/2,-grille.hauteur*2/3,grille.hauteurLigne/2 -3, 'gold')
    Cercle(3*grille.largeurColonne/2,-grille.hauteur*2/3,grille.hauteurLigne/2 -3, 'orange')
    Cercle(5*grille.largeurColonne/2,-grille.hauteur*2/3,grille.hauteurLigne/2 -3, 'darkred')

    #Affichage des consignes du jeu
    grille.va(0,-grille.hauteur*4/7)
    write("Essayez de retrouver, dans l'ordre, les 4 pions mystères !",False,"center",("Arial", 10, "normal"))
    grille.va(0,-grille.hauteur*5/7-5)
    write("◯ = un pion est bien placé      ● = un pion est mal placé",False,"center",("Arial", 10, "normal"))

    #Affichage des boutons
    bouton(grille.xgau-2*grille.largeurColonne,100,80,40,"Solution")
    bouton(grille.xgau-2*grille.largeurColonne,0,80,40,"Reset")
    bouton(grille.xgau-2*grille.largeurColonne,-100,80,40,"Quitter")
    bouton(grille.xgau+grille.largeurColonne,grille.ybas+grille.hauteur,4*grille.largeurColonne,grille.hauteurLigne,"Les 4 pions à trouver sont ici")


    #IMPORTANT: sans cette boucle, la tortue s'arrête, et il devient impossible d'utiliser getscreen() (impossible d'interagir avec le programme, le clic ne produit rien)
    while True:
        up()
        goto(0,0)

#===========================================================================================================================================================
        
jouer()



