# -*- coding:utf-8 -*-

__nom_fichier__ = "grille"
__author__ = "tinet & Fay-Varnier"
__date__ = "avril 2018"

"""
Exercices récapitulatifs

Le Sudoku

Classe Grille
"""

# Comptage de lignes
# Grille : 12 (__init__) + 5 (getEtat) + 8 (filtrer) + 15 (résoudre) + 8 (sauvegarder)
# Total : 51 lignes

from region import Ligne, Colonne, Carre
from case import Case


class Grille:
    """Permet de représenter et de résoudre une grille de Sudoku."""

    def __init__(self, filename):
        """
        Constructeur de la classe Grille. Créé les attributs matrice_ et regions_
        :param filename: Fichier de données
        """

        self.nom_ = filename

        # Création de la matrice des cases via lecture du fichier, nécessaire pour générer les régions
        fdat = open(filename, 'r')

        matrice = []
        for ligne in fdat:
            # pour chaque ligne du fichier, on crée une liste de cases
            liste = [Case(int(nb)) for nb in ligne.split()]
            matrice.append(liste)

        fdat.close()

        # Création des régions, les 9 premières régions doivent être les lignes (facilite l'affichage)
        self.regions_ = []
        for numl in range(9):
            self.regions_.append(Ligne(matrice, numl))

        for numc in range(9):
            self.regions_.append(Colonne(matrice, numc))

        for numc in range(9):
            self.regions_.append(Carre(matrice, numc))

        # on aurait pu également écrire sous la forme
        # for region_format in (Ligne, Colonne, Carre):
        #     # Il y a 9 lignes, 9 colonnes et 9 carrés 3x3
        #     for i in range(9):
        #         self.regions_.append(region_format(matrice, i))

    def getEtat(self):
        """
        Vérifie si une grille est déjà résolue
        :return: bool
        """
        for region in self.regions_[:9]:
            # on ne test que sur les lignes cela suffit

            if not region.get_etat():
                return False

        return True

    def __str__(self):
        """
        Affiche la grille
        :return: None
        """
        output = ""
        for ligne in self.regions_[:9]:
            output += ligne.__str__() + "\n"
        output += "\n"

        return output

    def resoudre(self):
        """
        Résout une grille de sudoku à l'aide des règles d'exclusion des doublons, de sélection de places uniques
        :return: bool
        """

        # Initialisation
        action = True

        if self.getEtat():
            return action

        # On cherche tant qu'une action est possible ou que la grille est résolue
        while action:
            action = False

            # On fait les trois règles séquentiellement sur l'ensemble de la grille
            for region in self.regions_:
                result = region.exclut_double_valeur()
                if result:
                    # il y a eu une action, on le marque
                    action = True
                    if self.getEtat():
                        # la grille est résolue on sauvegarde le résultat dans un fichier
                        self.sauvegarder()
                        return True

            # application de la seconde règle
            for region in self.regions_:
                result = region.place_unique()
                if result:
                    # il y a eu une action, on le marque
                    action = True
                    if self.getEtat():
                        # la grille est résolue on sauvegarde le résultat dans un fichier
                        self.sauvegarder()
                        return True

            # application de la seconde règle
            for region in self.regions_:
                result = region.couple_de_valeurs()
                if result:
                    # il y a eu une action, on le marque
                    action = True
                    if self.getEtat():
                        # la grille est résolue on sauvegarde le résultat dans un fichier
                        self.sauvegarder()
                        return True

        return action

    # version avec une seulle méthode de filtrage "générique"
    # à laquelle on passe en paramètre le nom de la fonction à appliquer
    # def filtrer(self, func):
    #     """
    #     Applique la regle func pour chaque région.
    #     :return: int, 0 si pas d'action, 1 si une action a été faite, 2 si la grille est résolue
    #     """
    #     action = 0
    #
    #     for region in self.regions_:
    #
    #         if func(region):
    #             action = 1
    #
    #             # On fait le test de résolution pour chaque action, de façon à arrêter les tests le plus tôt possible
    #             if self.getEtat():
    #                 return 2
    #
    #     return action

    # def resoudre(self):
    #     """
    #     Résout une grille de sudoku à l'aide des règles d'exclusion des doublons, de sélection de places uniques
    #     :return: bool
    #     """
    #
    #     # Initialisation
    #     action = True
    #
    #     if self.getEtat():
    #         return action
    #
    #     # On cherche tant qu'une action est possible ou que la grille est résolue
    #     while action:
    #
    #         action = False
    #
    #         # On fait les trois règles séquentiellement sur l'ensemble de la grille
    #         # for regle in [Region.exclut_double_valeur, Region.place_unique, Region.couple_de_valeurs]:
    #         for regle in [Region.exclut_double_valeur]:
    #
    #             result = self.filtrer(regle)
    #             for c in self.regions_[0].lcase_:
    #                 print(c.get_domaine())
    #
    #             print()
    #
    #             # Grille résolue, on sauvegarde le résultat
    #             if result == 2:
    #                 self.sauvegarder()
    #                 return True
    #
    #             if result == 1:
    #                 action = True
    #
    #     # Grille bloquée, on sauvegarde le résultat
    #     self.sauvegarder()
    #     return action

    def sauvegarder(self):
        """
        Affiche la grille résolue dans un fichier de sortie.
        :return: None
        """

        # On crée le nom du fichier de sortie en fonction de celui d'entrée
        dot = self.nom_.find('.')
        filename = self.nom_[:dot] + '_solution.txt'

        # Sauvegarde des résultats
        fres = open(filename, 'w')

        for ligne in self.regions_[:9]:
            fres.write(str(ligne))

            fres.write('\n')

        fres.close()

if __name__ == '__main__':
    sudoku = Grille("sudoku0.dta")
    print(sudoku)
    print(sudoku.resoudre())

