# -*- coding: utf-8 -*-

##  Copyright 2005 Sébastien Fagot
##
##    This program is free software; you can redistribute it and/or modify
##    it under the terms of the GNU General Public License as published by
##    the Free Software Foundation; either version 2 of the License, or
##    (at your option) any later version.
##
##    This program is distributed in the hope that it will be useful,
##    but WITHOUT ANY WARRANTY; without even the implied warranty of
##    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##    GNU General Public License for more details.
##
##    You should have received a copy of the GNU General Public License
##    along with this program; if not, write to the Free Software
##    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

from Tkinter import *
from random import sample
from tkSimpleDialog import *
from time import *
from os import name

class Jeu_Demineur:
    "jeu de démineur version Sébastien"
    def __init__(self,largeur,hauteur,nombre_de_mine,x,y):
        self.largeur = largeur
        self.hauteur = hauteur
        self.nombre_de_mine = nombre_de_mine
        self.x = x
        self.y = y
        # création de la variable carte[i][j]
        self.carte = [[0]*30]
        for i in range(29) : self.carte.append(self.carte[0][:])
        # création de la variable exploration[i][j]
        self.exploration = [[0]*30]
        for i in range(29) : self.exploration.append(self.exploration[0][:])
        # placement des mines
        self.place_mine()
        # remplissage de la carte d'exploration
        self.remplir_exploration()   
    def place_mine(self):
        "place les mines au hasard"
        if self.largeur * self.hauteur == self.nombre_de_mine:
            for j in range(self.hauteur):
                for i in range(self.largeur):
                    self.carte[i][j] = 1
        else:
            for j in range(self.hauteur):
                for i in range(self.largeur):
                    self.carte[i][j] = 0
            n=0
            liste_case_libre = []
            for j in range(self.hauteur):
                for i in range(self.largeur):
                    liste_case_libre.append([i,j])
            liste_case_libre.remove([self.x,self.y])
            while n < self.nombre_de_mine :
                case = sample(liste_case_libre,1)
                i = case[0][0]
                j = case[0][1]
                self.carte[i][j] = 1
                liste_case_libre.remove([i,j])
                n = n + 1 
    def remplir_exploration(self):
        "remplit la carte d'exploration"
        for j in range(self.hauteur):
            for i in range(self.largeur):
                self.exploration[i][j] = 0
                for k in (-1,0,1):
                    if i+k<self.largeur and i+k>-1:
                        for l in (-1,0,1):
                            if j+l<self.hauteur and j+l>-1:
                                if self.carte[i+k][j+l]:
                                    self.exploration[i][j] = \
                                    self.exploration[i][j] + 1

class Case:
    "définit les attributs d'une case du jeu"
    def __init__(self):
        self.id = 0
        self.deja_testee = 0
        self.marque = 0
        self.x = 0
        self.y = 0

class Gui_Demineur:
    "interface graphique du démineur"
    def __init__(self,hauteur=8,largeur=8,nombre_de_mine=10):
        # initialisation des variables
        self.largeur = largeur
        self.hauteur = hauteur
        self.nombre_de_mine = nombre_de_mine
        self.nombre_de_marque = 0
        self.jeu = Jeu_Demineur(largeur,hauteur,nombre_de_mine,0,0)
        # réglage des polices en fonction du système d'exploitation
        if name == 'nt' : # windows
            self.police_mine = "comic 10 bold"
            self.police_explose = "comic 10 bold"
            self.police_message = "arial 12 bold"
        else : # linux ou mac ou autre
            self.police_mine = "comic 13 bold"
            self.police_explose = "comic 15 bold"
            self.police_message = "arial 17 bold"
        # création de la variable case[i][j]
        self.case = []
        for i in range(30) : self.case.append([[]]*30)
        for i in range(30):
            for j in range(30):
                self.case[i][j]=Case()
        # création de la fenetre principale
        self.fenetre = Tk()
        self.fenetre.title('SebMineur')
        self.fenetre.resizable(0, 0)
        # création du menu général
        menu_general = Menu(self.fenetre)
        self.fenetre.configure(menu = menu_general)
        menu_general.add_command(label='Nouvelle partie',\
                                 command=self.commencer_partie)
        menu_niveau = Menu(menu_general)
        menu_general.add_cascade(label='Niveau',menu=menu_niveau)
        menu_niveau.add_command(label ='Débutant', command = self.debutant)
        menu_niveau.add_command(label ='Intermédiaire',\
                                command = self.intermediaire)
        menu_niveau.add_command(label ='Expert', command = self.expert)
        menu_niveau.add_separator()
        menu_niveau.add_command(label ='Libre',command = self.libre)
        menu_general.add_command(label='Quitter',command=self.fenetre.destroy)
        menu_general.add_command(label='Aide',command=self.aide)
        # message
        self.message = Label(self.fenetre,text='Bienvenue',\
                             font=self.police_message)
        self.message.grid(row=0)
        # information sur les mines
        self.info = Label(self.fenetre)
        self.info.grid(row=1,sticky=W)
        # horloge
        self.horloge = Label(self.fenetre)
        self.horloge.grid(row=1,sticky=E)
        # création du plateau de jeu
        self.plateau = Canvas(self.fenetre,borderwidth=1, \
                    bg='grey',highlightthickness=0)
        self.plateau.grid(row=2)
        # commencement du jeu
        self.fenetre.bind("<Destroy>",self.arret)
        self.commencer_partie()
        self.continuer = 1
        while self.continuer:
            self.fenetre.after(40,self.temps)
            self.fenetre.mainloop() # démarrage du réceptionnaire d'événements
    def aide(self):
        "affiche une fenêtre d'aide"
        fenetre_aide = Toplevel(self.fenetre)
        fenetre_aide.title("Aide - SebMineur")
        fenetre_aide.resizable(0, 0)
        texte = "Aide du jeu\n\n"\
                "Le but est de découvrir toutes les cases sans mines.\n\n"\
                "clic gauche : teste la case, un chiffre indique le "\
                "nombre de mines présentes sur l'ensemble des 8 cases "\
                "voisines\n\n"\
                "clic droit : protège la case, empèche un clic "\
                "gauche sur cette case (utile pour éviter une erreur de "\
                "clic), clic une 2ème fois pour déprotéger la case\n\n"\
                "clic milieu : découvre toutes les cases voisines "\
                "non-protégées (si le nombre de cases protégées est égal "\
                "au nombre de mines voisines)\n\n"\
                "Le temps de jeu s'affiche en haut à droite.\n"\
                "Le nombre de cases protégées et le nombre total de mines "\
                "s'affichent en haut à gauche.\n\n"\
                "N'aie pas peur ! Ton 1er coup sera toujours sur une case "\
                "sans mine ! (sauf si tu fais un niveau totalement rempli "\
                "de mines...)\n\n"\
                "!!! BONNE CHANCE !!!"
        Message(fenetre_aide, text = texte).grid()
    def affiche_tout(self):
        "affiche tout"
        for j in range(self.hauteur):
            ligne = ''
            for i in range(self.largeur):
                ligne = ligne + str(self.case[i][j].exploration) + ' '
            print ligne
    def arret(self,event):
        "arrêt du programme"
        self.continuer = 0
    def temps(self):
        "chronomètre le temps de jeu"
        if self.partie_en_cours:
            duree = int(time() - self.instant_zero)
            self.horloge.configure(text=str(duree))
        self.fenetre.quit()
    def libre(self):
        "réglages du niveau libre"
        Niveau_libre(self.fenetre,self.largeur,self.hauteur,\
                     self.nombre_de_mine,self)
    def debutant(self):
        "réglages du niveau débutant"
        self.efface_le_plateau()
        self.largeur = 8
        self.hauteur = 8
        self.nombre_de_mine = 10
        self.commencer_partie()
    def intermediaire(self):
        "réglages du niveau intermédiaire"
        self.efface_le_plateau()
        self.largeur = 16
        self.hauteur = 16
        self.nombre_de_mine = 40
        self.commencer_partie()
    def expert(self):
        "réglages du niveau expert"
        self.efface_le_plateau()
        self.largeur = 30
        self.hauteur = 16
        self.nombre_de_mine = 99
        self.commencer_partie()
    def efface_le_plateau(self):
        "efface le plateau"
        liste_de_tous = self.plateau.find_all()
        for i in liste_de_tous:
            self.plateau.delete(i)
    def commencer_partie(self):
        "commence une nouvelle partie"
        self.efface_le_plateau()
        self.message.configure(text='Bienvenue',fg='black')
        self.plateau_premier_coup()
        self.nombre_de_case_decouverte = 0
        self.nombre_de_marque = 0
        self.affiche_info()
    def plateau_premier_coup(self):
        "dessine le plateau de départ avant le premier coup"
        self.plateau.configure(width=18*self.largeur+1,height=18*self.hauteur+1)
        for i in range(self.largeur):
            for j in range(self.hauteur):
                self.case[i][j].id = self.plateau.create_rectangle(18*i+1,\
                    18*j+1,18*(i+1),18*(j+1),fill='white',outline='grey')
                self.case[i][j].deja_testee = 0
                self.case[i][j].marque = 0
                self.case[i][j].x = i
                self.case[i][j].y = j
                def premier_clic(event,self=self,case=self.case[i][j]):
                    return self.premier_clic_gauche(event,case)
                self.plateau.tag_bind(self.case[i][j].id,"<Button-1>",premier_clic)
        self.partie_en_cours = 0
        self.duree = 0
        self.horloge.configure(text=str(self.duree))
    def premier_clic_gauche(self,event,case):
        "initialisation du jeu après le premier clic"
        self.instant_zero = time()
        self.jeu = Jeu_Demineur(self.largeur,self.hauteur,self.nombre_de_mine,\
                                case.x,case.y)
        self.dessine_plateau()
        self.partie_en_cours = 1
        self.clic_gauche(event,case)
    def dessine_plateau(self):
        "dessine le plateau de départ"
        for i in range(self.largeur):
            for j in range(self.hauteur):
                self.case[i][j].mine = self.jeu.carte[i][j]
                self.case[i][j].exploration = self.jeu.exploration[i][j]
                def clic_gauche(event,self=self,case=self.case[i][j]):
                    return self.clic_gauche(event,case)
                def clic_milieu(event,self=self,case=self.case[i][j]):
                    return self.clic_milieu(event,case)
                def clic_droit(event,self=self,case=self.case[i][j]):
                    return self.clic_droit(event,case)
                self.plateau.tag_bind(self.case[i][j].id, "<Button-1>",\
                                      clic_gauche)
                self.plateau.tag_bind(self.case[i][j].id, "<Button-2>",\
                                      clic_milieu)
                self.plateau.tag_bind(self.case[i][j].id, "<Button-3>",\
                                      clic_droit)
    def clic_milieu(self,event,case):
        "action lors d'un clic sur une case avec le bouton du milieu"
        if not case.deja_testee : return
        if case.marque : return
        if case.mine : return
        # compte le nombre de marques autour de la case
        nombre_de_marque = 0
        for i in (-1+case.x,case.x,1+case.x):
            if i<self.largeur and i>-1:
                for j in (-1+case.y,case.y,1+case.y):
                    if j<self.hauteur and j>-1:
                        if self.case[i][j].marque:
                            nombre_de_marque = nombre_de_marque + 1
        # appuie sur toutes les cases voisines si c'est cohérent
        if nombre_de_marque == case.exploration:
            for i in (-1+case.x,case.x,1+case.x):
                if i<self.largeur and i>-1:
                    for j in (-1+case.y,case.y,1+case.y):
                        if j<self.hauteur and j>-1:
                            case_sans_risk = self.case[i][j]
                            self.clic_gauche(event,case_sans_risk)
    def clic_droit(self,event,case):
        "action lors d'un clic sur une case avec le bouton droit"
        if case.deja_testee : return
        if case.marque :
            self.plateau.itemconfigure(case.id,fill='white')
            case.marque = 0
            self.nombre_de_marque = self.nombre_de_marque - 1
        else :
            self.plateau.itemconfigure(case.id,fill='blue')
            case.marque = 1
            self.nombre_de_marque = self.nombre_de_marque + 1
        self.affiche_info()
    def clic_gauche(self,event,case):
        "action lors d'un clic sur une case avec le bouton gauche"
        couleurs = ['blue','darkgreen','red','purple','brown','yellow','black','black']
        if case.marque : return
        if case.deja_testee : return
        case.deja_testee = 1
        self.nombre_de_case_decouverte = self.nombre_de_case_decouverte + 1
        if case.mine :
            self.message.configure(text='!!! BOOOM !!!',fg="red")
            self.stop(case)
        else:
            self.plateau.itemconfigure(case.id,fill='lightgrey')
            if case.exploration:
                texte=self.plateau.create_text(case.x*18+10,\
                    case.y*18+10,fill=couleurs[case.exploration-1],\
                    font=self.police_mine,text=str(case.exploration))
                def clic_milieu(event,self=self,case=case):
                    return self.clic_milieu(event,case)
                self.plateau.tag_bind(texte,"<Button-2>",clic_milieu)
            else :
                for i in (-1+case.x,case.x,1+case.x):
                    if i<self.largeur and i>-1:
                        for j in (-1+case.y,case.y,1+case.y):
                            if j<self.hauteur and j>-1:
                                case_sans_risk = self.case[i][j]
                                self.clic_gauche(event,case_sans_risk)
            if self.nombre_de_case_decouverte == \
               self.hauteur*self.largeur - self.nombre_de_mine:
               self.message.configure(text='!!! BRAVO !!!',fg='green')
               self.stop(case)
    def stop(self,case):
        "arrête la partie"
        liste_de_tous = self.plateau.find_all()
        for i in liste_de_tous:
            self.plateau.tag_unbind(i,"<Button-1>")
            self.plateau.tag_unbind(i,"<Button-2>")
            self.plateau.tag_unbind(i,"<Button-3>")
        if not case.mine:
            for j in range(self.hauteur):
                for i in range(self.largeur):
                    if self.jeu.carte[i][j]:
                        self.plateau.itemconfigure(self.case[i][j].id,\
                                                       fill='green')
        else :
            for j in range(self.hauteur):
                for i in range(self.largeur):
                    if self.jeu.carte[i][j]:
                        self.plateau.itemconfigure(self.case[i][j].id,\
                                                       fill='red')
                        self.plateau.create_text(i*18+10,\
                                j*18+10,fill='black',text='M',\
                                font=self.police_explose)
        self.partie_en_cours = 0
    def affiche_info(self):
        "affiche des infos sur la partie en cours"
        self.info.configure(text=str(self.nombre_de_mine - \
            self.nombre_de_marque)+' / '+str(self.nombre_de_mine))        

class Niveau_libre(Dialog):
    "fenetre de réglage pour le niveau libre"
    def __init__(self,parent,largeur,hauteur,nombre_de_mine,jeu):
        self.largeur = largeur
        self.hauteur = hauteur
        self.nombre_de_mine = nombre_de_mine
        self.jeu = jeu
        Dialog.__init__(self, parent)
    def body(self, master):
        self.title("Niveau libre")
        self.reglage_largeur = Scale(master,from_=1,to=30,orient="horizontal",\
                        tickinterval=1,label='Largeur',command=self.max_mine)
        self.reglage_largeur.set(self.largeur)
        self.reglage_largeur.pack(fill=X)
        
        self.reglage_hauteur = Scale(master,from_=1,to=30,orient="horizontal",\
                        tickinterval=1,label='Hauteur',command=self.max_mine)
        self.reglage_hauteur.set(self.hauteur)
        self.reglage_hauteur.pack(fill=X)
        
        self.reglage_nombre_de_mine = Scale(master,from_=0,length=900+1+33,\
            to=self.reglage_largeur.get()*self.reglage_hauteur.get(),\
            orient="horizontal",label='Nombre de mine')
        self.max_mine(None)
        self.reglage_nombre_de_mine.set(self.nombre_de_mine)
        self.reglage_nombre_de_mine.pack(fill=X)
    def apply(self):
        self.jeu.largeur = self.reglage_largeur.get()
        self.jeu.hauteur = self.reglage_hauteur.get()
        self.jeu.nombre_de_mine = self.reglage_nombre_de_mine.get()
        self.jeu.commencer_partie()
    def max_mine(self,event):
        interval = self.reglage_largeur.get()*self.reglage_hauteur.get()/30
        if interval == 0 : interval = 1
        self.reglage_nombre_de_mine.config(tickinterval=interval,\
            to=self.reglage_largeur.get()*self.reglage_hauteur.get())

Gui_Demineur()
