DricomDragon 4 anni fa
parent
commit
649efe351e
64 ha cambiato i file con 5468 aggiunte e 0 eliminazioni
  1. 57 0
      project/EntrainementGenetic.py
  2. 94 0
      project/Fonctions_d_evaluation.py
  3. 40 0
      project/GeneticGraph.py
  4. 68 0
      project/GeneticMatch.py
  5. 85 0
      project/Outils/Arbre.py
  6. 41 0
      project/Outils/Elagage.py
  7. 48 0
      project/Outils/IA_MinMax.py
  8. 114 0
      project/Outils/IA_Stats.py
  9. 48 0
      project/Outils/IA_TEST.py
  10. 42 0
      project/Outils/IA_alphabeta.py
  11. 56 0
      project/Outils/IA_biland.py
  12. 19 0
      project/Outils/IA_chemin.py
  13. 31 0
      project/Outils/IA_semi_random.py
  14. 14 0
      project/Outils/Moteur_de_jeu/Barriere.py
  15. 255 0
      project/Outils/Moteur_de_jeu/Conversion.py
  16. 60 0
      project/Outils/Moteur_de_jeu/Coup.py
  17. 99 0
      project/Outils/Moteur_de_jeu/Distance.py
  18. 420 0
      project/Outils/Moteur_de_jeu/Fenetre.py
  19. 51 0
      project/Outils/Moteur_de_jeu/Joueur.py
  20. 278 0
      project/Outils/Moteur_de_jeu/Partie.py
  21. 284 0
      project/Outils/Moteur_de_jeu/Pathfinding.py
  22. 508 0
      project/Outils/Moteur_de_jeu/Plateau.py
  23. 83 0
      project/Outils/Symetries.py
  24. 24 0
      project/Outils/alphabeta.py
  25. 106 0
      project/Outils/arbre_alpha_beta.py
  26. 7 0
      project/Q _ I.A. vs I.A. (aléatoire).py
  27. 9 0
      project/Q _ JEU HUMAINS.py
  28. 190 0
      project/Q_Anti-Gael.py
  29. 53 0
      project/Q_Biland.py
  30. 18 0
      project/Q_HumainVsAlphaBeta2.py
  31. 35 0
      project/Q_HumainVsAlphaBeta3+.py
  32. 19 0
      project/Q_HumainVsAlphaBeta3.py
  33. 28 0
      project/Q_HumainVsGenetic1.py
  34. 33 0
      project/Q_HumainVsGeneticSelect.py
  35. 19 0
      project/Q_HumainVsMinMax1.py
  36. 44 0
      project/Q_HumainVsMinMax2+.py
  37. 16 0
      project/Q_HumainVsMinMax2.py
  38. 13 0
      project/Q_HumainVsMinMax3+.py
  39. 16 0
      project/Q_HumainVsMinMax3.py
  40. 7 0
      project/Q_HumainVsRandomIA.py
  41. 12 0
      project/Q_HumainVsSemiAleatoire.py
  42. 19 0
      project/Q_HumainVsStats.py
  43. 17 0
      project/Q_MinMax_vs_MinMax.py
  44. 44 0
      project/Q_anti_raphael.py
  45. 81 0
      project/Rejouer les parties.py
  46. BIN
      project/ReseauNeurones/ArchitecturePartieGénétique.odg
  47. BIN
      project/ReseauNeurones/ArchitecturePartieGénétique2.odg
  48. 6 0
      project/ReseauNeurones/Base_Poids/Primitif/poidsTest.pds
  49. 6 0
      project/ReseauNeurones/Base_Poids/Primitif/poidsTest2.pds
  50. 9 0
      project/ReseauNeurones/Base_Structures/Primitif.stc
  51. 9 0
      project/ReseauNeurones/Base_Structures/PrimitifEfficace.stc
  52. BIN
      project/ReseauNeurones/Base_Structures/PrimitifSchema.odg
  53. 20 0
      project/ReseauNeurones/activations.py
  54. 299 0
      project/ReseauNeurones/cerveau.py
  55. 51 0
      project/ReseauNeurones/entrepot.py
  56. 62 0
      project/ReseauNeurones/generation.py
  57. 303 0
      project/ReseauNeurones/incubateur.py
  58. 159 0
      project/ReseauNeurones/network.py
  59. 94 0
      project/Resultats_stats.py
  60. 174 0
      project/TraitementParties.py
  61. 82 0
      project/etude_resultats_elric.py
  62. 218 0
      project/fct_eval_elric.py
  63. 318 0
      project/match_fonctionnels.py
  64. 53 0
      project/mesure_compare_pathsfinders.py

+ 57 - 0
project/EntrainementGenetic.py

@@ -0,0 +1,57 @@
+## Fichier : EntrainementGenetic.py
+# Auteur : Jovian Hersemeule
+
+# Permet de lancer une session d'entraînement
+# pour un réseau de neurones.
+
+# Importations de fonctionnement
+from ReseauNeurones import *
+from ReseauNeurones.incubateur import Incubateur
+
+from Outils.Moteur_de_jeu import *
+from Outils.Moteur_de_jeu.Pathfinding import path_finding
+
+from Outils import IA_alphabeta
+from Outils.IA_alphabeta import IA_minmax_alpha_beta
+
+# Différents tournois
+from match_fonctionnels import tournoi # Toutes rondes ( IA )
+nom_tournoi = "Toutes rondes"
+
+##from match_fonctionnels import tournoi2 # Toutes rondes ( fonctions )
+##nom_tournoi = "Toutes rondes"
+
+##from match_fonctionnels import tournoi3 # Elimination directe
+##nom_tournoi = "Elimination directe"
+
+##from match_fonctionnels import rondes_suisses # Rondes suisses
+##nom_tournoi = "Rondes suisses"
+
+# Création
+myTrainer = Incubateur()
+
+myTrainer.save_rate = 2
+myTrainer.tournoi = tournoi
+myTrainer.nom_tournoi = nom_tournoi
+
+# Paramètres d'entraînement
+nom_structure = "PrimitifEfficace"
+nom_session = "PurAleatoirePrecis1"
+nb_gen_max = 300
+nb_pop = 20
+debug = True
+
+if debug :
+    from Outils.Moteur_de_jeu.Fenetre import Global
+    from Outils.Moteur_de_jeu.Fenetre import affichage_sauvage
+
+# Paramètres de génération
+myTrainer.nb_elite_kept = 5
+myTrainer.nb_elite_cross = 0
+myTrainer.nb_mixte_cross = 0
+myTrainer.nb_elite_mutate = 0
+myTrainer.nb_weak_mutate = 0
+myTrainer.nb_rd_generated = 15
+
+# Lancement de l'entraînement
+myTrainer.start_session(nom_structure, nom_session = nom_session, nb_gen_max = nb_gen_max, nb_pop = nb_pop, debug = debug )

+ 94 - 0
project/Fonctions_d_evaluation.py

@@ -0,0 +1,94 @@
+from Outils import Arbre
+from Outils.Arbre import *
+from Outils.Moteur_de_jeu import Joueur
+from Outils.Moteur_de_jeu.Joueur import *
+import math
+
+
+def distance_bord (position) :
+    return (min (position[0], 9-position[0]))
+
+def lin_tout (plateau, num, cdistance, cbarrieres, cbord) :
+    """on considère les coefficients associés à l'importance des barrières/...
+    et on les affecte 'linéairement'"""
+    b_num = plateau.barrieres_restantes[num]
+    b_opp = plateau.barrieres_restantes[1-num]
+    distance_num = plateau.longueur_chemin(num)
+    distance_opp = plateau.longueur_chemin(1-num)
+    dist_bord_num = distance_bord (plateau.pions[num])
+    dist_bord_opp = distance_bord (plateau.pions[1-num])
+    valeur_dist = cdistance*(distance_opp-distance_num)
+    valeur_b = cbarrieres*(b_opp - b_num)
+    valeur_bord = cbord*(dist_bord_opp-dist_bord_num)
+    v = valeur_dist + valeur_b + valeur_bord
+    return (v)
+
+
+def lineaire_basique (cdistance, cbarrieres, cbord) :
+    return (lambda bn, bo, dn, do, dbn, dbo : cdistance*(do-dn) + cbarrieres*(bo - bn) + cbord*(dbo-dbn))
+
+def lineaires () :
+    fonctions_lineaires = numpy.zeros (1000)
+    for i in range (0, 10) :                #associé à cdistance = i ici
+        for j in range (0, 10) :            #associé à cbarrieres = j*j ici
+            for k in range (0, 10) :        #associé à cbord = sqrt k ici
+                l = 100*i + 10*j + k
+                fonctions_lineaires[l] = lineaire_basique (i+1, (j+1)*(j+1), math.sqrt(k+1))
+    return (fonctions_lineaires)
+
+
+"""on a 1000 fonctions"""
+
+                
+def expo_basique_coefs_internes (cdistance, cbarrieres, cbord) :
+    return (lambda bn, bo, dn, do, dbn, dbo : math.exp(cdistance*do) - math.exp(cdistance*dn) + math.exp(cbarrieres*bo) - math.exp(cbarrieres*bn) + math.exp(cbord*dbo) - math.exp(cbord*dbn))
+
+def exponentielles_coefs_dans_lexpo () :
+    fonctions_expo1 = numpy.zeros (125)
+    for i in range (0, 5) :                
+        for j in range (0, 5) :            
+            for k in range (0, 5) :        
+                l = 25*i + 5*j + k
+                fonctions_expo1[l] = expo_basique_coefs_internes ((i+1)/2, (j+1), 0.2*(k+1))
+    return (fonctions_expo1)
+
+"""on a 125 fonctions"""
+
+def expo_basique_coefs_doubles (cdistance, cbarrieres, cbord) :
+    return (lambda bn, bo, dn, do, dbn, dbo : cdistance*math.exp(cdistance*do) - cdistance*math.exp(cdistance*dn) + cbarrieres*math.exp(cbarrieres*bo) - cbarrieres*math.exp(cbarrieres*bn) + cbord*math.exp(cbord*dbo) - cbord*math.exp(cbord*dbn))
+
+
+def exponentielles_coefs_doubles () :
+    fonctions_expo2 = numpy.zeros (125)
+    for i in range (0, 5) :                
+        for j in range (0, 5) :            
+            for k in range (0, 5) :        
+                l = 25*i + 5*j + k
+                fonctions_expo2[l] = expo_basique_coefs_doubles ((i+1)/3, (j+1), 0.15*(k+1))
+    return (fonctions_expo2)
+
+"""on a 125 fonctions"""
+
+#test = lambda x : x*5
+
+#test2 = lambda x,y : x*5 + y
+
+#print (test2 (1, 2))
+
+def lois_en_puissance (cdistance, cbarrieres, cbord) :
+    return (lambda bn, bo, dn, do, dbn, dbo : do**cdistance - dn**cdistance + bo**cbarrieres - bn**cbarrieres + dbo**cbord - dbn**cbord)
+
+
+def puissances () :
+    fonction_puissance1 = numpy.zeros (1000)
+    for i in range (0, 10) :                
+        for j in range (0, 10) :            
+            for k in range (0, 10) :        
+                l = 100*i + 10*j + k
+                fonctions_puissance1[l] = lois_en_puissance (i, j, k)
+    return (fonctions_puissance1)
+
+
+"""1000 fonctions"""
+
+

+ 40 - 0
project/GeneticGraph.py

@@ -0,0 +1,40 @@
+## Fichier : GeneteicGraph.py
+# Auteur : Jovian Hersemeule
+
+# Génénère des graphiques
+# pour visualiser les progrès de
+# l'algorithme génétique.
+
+from Outils.Moteur_de_jeu import *
+from Outils.Moteur_de_jeu.Partie import *
+
+from Outils import IA_MinMax
+from Outils.IA_MinMax import *
+
+from ReseauNeurones import *
+from ReseauNeurones.cerveau import *
+
+# Deboguage
+debug = True
+
+# Joueur de référence
+joueurRef
+
+# Chargement d'un réseau
+myBrain = Cerveau()
+myBrain.charger_structure( "PrimitifEfficace.stc", debug = debug )
+myBrain.charger_poids( "PurAleatoireFast2/Generation50/individu0.pds", debug = debug )
+myBrain.build_network( debug = debug )
+
+# Match
+joueurA = IA_minmax("Genetic:Primitif", myBrain.fct_eval, 1 )
+joueurB = Humain("Humain")
+
+Global.partie = Partie(joueurA, joueurB, True, "")
+
+vainqueur = Global.partie.demarrer()
+
+# Annonce
+print("J1 : ", joueurA.nom)
+print("J2 : ", joueurB.nom)
+print("Gagnant : ", vainqueur.nom)

+ 68 - 0
project/GeneticMatch.py

@@ -0,0 +1,68 @@
+## Fichier : GeneticMatch.py
+# Auteur : Jovian Hersemeule
+
+# Exécute des matchs
+# à des fins statistiques.
+
+from Outils.Moteur_de_jeu import *
+from Outils.Moteur_de_jeu.Partie import *
+
+#from Outils import IA_MinMax
+#from Outils.IA_MinMax import *
+from Outils import IA_alphabeta
+from Outils.IA_alphabeta import *
+
+from ReseauNeurones import *
+from ReseauNeurones.cerveau import *
+
+#from Eval_v1 import fct_eval as fct1
+#from Eval_v2 import fct_eval as fct2
+from Eval_v3 import fct_eval as fct3
+
+fct_eval = [(lambda x, y : 0), (lambda x, y : 0), fct3]
+
+# Paramètres
+nom_session = "PurAleatoireFast2"
+incr = 1
+
+id_eval = 3
+prof = 1
+
+debug = False
+
+
+# Annonce
+print("Ajuster les paramètres puis lancer write_stats()")
+print("string: nom_session > nom de la session génétique à éprouver")
+print("int: incr > l'écart entre chaque génération")
+print("int: id_eval > heuristique utilisée pour l'aplhabeta de référence")
+print("int: prof > profondeur du MinMax de référence")
+print("bool: debug > permet d'avoir des retours dans le log"
+
+
+# Fonction de stats
+def write_stats():
+    """
+    Ecrit les statistiques d'une session génétique.
+    """
+    # Joueur de référence
+    joueurRef = IA_minmax_alpha_beta("AlphaBeta-p" + str(prof)"-ev"+str(id_eval), fct_eval[id_eval], prof)
+
+    # Ouverture du flux
+    result_stream = open("ReseauNeurones/" + nom_session + "-VS-" joueurRef.nom + ".txt", "w")
+
+    # Chargement d'un réseau
+    myBrain = Cerveau()
+    myBrain.charger_structure( "PrimitifEfficace.stc", debug = debug )
+    myBrain.charger_poids( "PurAleatoireFast2/Generation50/individu0.pds", debug = debug )
+    myBrain.build_network( debug = debug )
+
+    # Match
+    joueurA = IA_minmax("Genetic:Primitif", myBrain.fct_eval, 1 )
+    joueurB = Humain("Humain")
+
+    Global.partie = Partie(joueurA, joueurB, True, "")
+
+    vainqueur = Global.partie.demarrer()
+
+    # TODO : boucle etc ...

+ 85 - 0
project/Outils/Arbre.py

@@ -0,0 +1,85 @@
+"""
+    MinMax consiste à calculer H coup d'avance, en considérant que l'adversaire joue au mieux.
+"""
+from random import randint
+from Outils.Elagage import *
+
+
+
+class Arbre() :     # Quentin 18/10/2016
+    """
+        Sert à construire l'arbre du minMax.
+
+        Attributs :
+        .plateau
+        .hauteur : hauteur de l'arbre (Les feuilles ont une hauteur de 0)
+        .num : joueur dont c'est le tour à cette position
+    """
+    def __init__(self, evaluer_position, plateau, num_racine, h, H) :
+        """
+            Sert à fabriquer l'arbre récursivement.
+
+            evaluer_position : fonction d'évaluation
+            plateau : clair
+            num_racine : numéro du joueur dont c'est le tour dans la partie associée à la racine
+            num_pere : numéro du père de l'arbre ici construit. Vaut None si l'arbre est la racine.
+            h : accumulateur comptant le nombre d'appels récursifs
+            H : hauteur désirée
+        """
+
+        self.hauteur = H - h
+        self.num = plateau.tour
+
+
+
+        # self.fils est la liste des fils correspondant aux nouveau plateaux obtenus après application des coups disponibles
+        # C'est une liste de couples (Coup, Nombre, Arbre)
+        self.fils = []
+
+        # Liste des valeurs des fils :
+        liste_valeurs = []
+
+        if h < H and plateau.gagnant() == -1 :  #Si l'arbre ne correspond pas à une situation gagnante, il a des fils
+            liste_coups = plateau.liste_coups_possibles()
+
+            for coup in liste_coups :
+                if elagage(plateau, coup) :
+                    plateau_fils = plateau.copie()
+                    plateau_fils.executer_coup(coup)
+                
+                # Chaque fils est associé au coup qui lui donne naissance et à la valeur de la position obtenue
+                    arbre = Arbre(evaluer_position, plateau_fils, num_racine, h+1, H)
+                    self.fils.append((coup, arbre))
+                    liste_valeurs.append(arbre.valeur)
+
+
+
+
+        # Calcul de la valeur :
+        if liste_valeurs == [] :
+            self.valeur = evaluer_position(plateau, num_racine)
+        elif self.num == num_racine :
+            self.valeur = max(liste_valeurs)
+        else :
+            self.valeur = min(liste_valeurs)
+
+
+
+
+            
+
+    def est_feuille(self) :
+        """ Retourne True si le noeud est une feuille """
+        return self.fils == []
+
+
+    def liste_feuilles(self) :
+        """ Retourne la liste des feuilles de l'arbre """
+        if self.est_feuille() :
+            return [self]
+        else :  # Si l'arbre n'est pas une feuille, on appelle récursivement la fonction  sur ses fils et on concatène les listes de feuilles obtenues
+            l = []
+            for f in self.fils :
+                l += f.liste_feuilles
+            return l
+

+ 41 - 0
project/Outils/Elagage.py

@@ -0,0 +1,41 @@
+
+from Outils.Moteur_de_jeu.Pathfinding import *
+from Outils.Moteur_de_jeu.Plateau import *
+
+def elagage(plateau, coup) :    # Elric
+    """
+        On élague un coup si il s'agit d'un mouvement qui n'est pas dans le pathfinding, ou
+        s'il s'aghit d'une barrière ne touchant ni bord, ni joueur, ni barrière
+        on retourne True si on garde le coup, false sinon
+    """
+    return True
+    if coup.type == "M" :
+        chemin = path_finding (plateau, plateau.pions[plateau.tour][0],
+                          plateau.pions[plateau.tour][1],plateau.rangee_desiree(plateau.tour))
+        return (coup.case in chemin or coup.case[1] == plateau.rangee_desiree(plateau.tour))
+    else :
+        bar = coup.barriere
+        a = plateau.barrieres_adjacentes (bar)
+        b = plateau.pions_voisins (bar)
+        c = plateau.bord_adjacent (bar)
+
+        return (a != [] or b)#ajouter or c
+
+ #Pour prendre moins de temps
+
+
+
+
+def elagage_prudent(plateau, coup) : # Quentin 02/12/2012
+    """
+        Même fonction sans condition sur les mouvements
+    """
+
+    if coup.type == "B" :
+        bar = coup.barriere
+        a = plateau.barrieres_adjacentes(bar)
+        b = plateau.pions_voisins(bar)
+
+        return a != [] or b
+    else :
+        return True

+ 48 - 0
project/Outils/IA_MinMax.py

@@ -0,0 +1,48 @@
+from Outils.Arbre import *
+from Outils.Moteur_de_jeu import Joueur
+from Outils.Moteur_de_jeu.Joueur import *
+
+from random import shuffle
+
+class IA_minmax(Joueur) :
+    def __init__(self, nom, fct_eval, prof) :
+        """
+            la fonction d'évaluation sera donnée en entrée, et évaluera les positions.
+            prof est la profondeur
+        """
+        Joueur.__init__(self,"O",nom)
+        self.fct_eval = fct_eval
+        self.prof = prof              
+
+       
+    def calculer_coup( self, plateau, liste_coup) :
+        #1. : avoir l'arbre
+        #2. : calculer le coup
+        #3 : le ressortir (trouver son indexation dans la liste des coups)
+
+        #etape 1
+
+        arb = Arbre (self.fct_eval, plateau, self.num, 0, self.prof)
+
+        #etape 2
+
+        t = arb.fils
+        l=[]
+        maxi = t[0][1].valeur
+        for i in range(len(t)) :
+            l.append (t[i][1].valeur)
+            if t[i][1].valeur >= maxi :
+                maxi = t[i][1].valeur
+
+        liste_coups = []
+        for i in range(len(t)) :
+            if t[i][1].valeur == maxi :
+                liste_coups.append(t[i][0])
+
+        
+        shuffle(liste_coups)
+        coup = liste_coups[0]
+
+        for i in range(len(liste_coup)) :
+            if liste_coup[i] == coup :
+                return i

+ 114 - 0
project/Outils/IA_Stats.py

@@ -0,0 +1,114 @@
+from Outils.Moteur_de_jeu import Joueur
+from Outils.Moteur_de_jeu.Joueur import *
+
+from Outils.Moteur_de_jeu import Conversion
+from Outils.Moteur_de_jeu.Conversion import *
+
+from Outils.Symetries import *
+from random import choice
+
+
+class IA_stats(Joueur) :
+    def __init__(self, nom, nom_fichier, adjoint) :
+        """
+            nom_fichier est le nom du fichier de statistiques que l'I.A. utilise
+            adjoint est l'I.A. utilisée en cas d'absence d'occurence de la situation
+        """
+        Joueur.__init__(self,"O",nom)
+
+        self.adjoint = adjoint
+        self.tab_reussites = []     # Tableau dont la case n vaut True si l'I.A. a trouvé une occurence au n^e coup et 0 sinon. Utile pour l'analyse des résultats
+
+        fichier = open(nom_fichier, "r")
+        self.stats = fichier.readlines()
+        fichier.close()
+        
+        self.tab_p = [l[:l.find(" ")] for l in self.stats]
+            
+
+
+
+
+
+    def coups_pour_situation(self, plateau) :
+        """ Retourne la liste des coups proposés pour cette situation """
+        code_p = code_from_plateau(plateau)
+        (trouve, id_p) = recherche(self.tab_p, code_p)
+
+        if trouve :
+            ligne = self.stats[id_p]
+            tab_c = ligne[ligne.find(" ") + 1:].split(",")
+
+            lcp = []    # Liste de coups pondérés (couples (coup, nb))
+            for l in tab_c :
+                i = l.find(":")
+                lcp.append( (coup_from_code(l[:i]) , int(l[i+1:])) )
+
+            return lcp
+                            
+        else :
+            return []
+        
+
+    def calculer_coup(self, plateau, liste_coups) :
+        lcp = self.coups_pour_situation(plateau)
+
+        # Symétries verticales :
+        lcp_v = self.coups_pour_situation(plateau_symetrie_verticale(plateau))
+        for c in lcp_v :
+            (coup,nb) = c
+            lcp.append((coup_symetrie_verticale(plateau, coup),nb))
+            
+
+        # Symétries horizontales :
+        lcp_h = self.coups_pour_situation(plateau_symetrie_horizontale(plateau))
+        for c in lcp_h :
+            (coup,nb) = c
+            lcp.append((coup_symetrie_horizontale(plateau, coup),nb))
+
+
+        # Composées des deux :
+        lcp_hv = self.coups_pour_situation(plateau_symetrie_verticale(plateau_symetrie_horizontale(plateau)))
+        for c in lcp_hv :
+            (coup,nb) = c
+            lcp.append((coup_symetrie_horizontale(plateau, coup_symetrie_verticale(plateau, coup)),nb))
+
+        
+        # Analyse :
+        if lcp == [] :
+            #print("Pas d'occurence")
+            self.adjoint.num = self.num
+
+            self.tab_reussites.append(0)
+            return self.adjoint.calculer_coup(plateau, liste_coups)
+        
+        else :
+            # Recherche du nombre d'occurences maximal :
+            n_max = 0
+            for c in lcp :
+                if c[1] > n_max :
+                    n_max = c[1]
+
+
+            liste_meilleurs_coups = [c[0] for c in lcp if c[1] == n_max]
+            coup = choice(liste_meilleurs_coups)
+
+            self.tab_reussites.append(1)
+
+            n = len(liste_coups)
+            for i in range(n) :
+                if  liste_coups[i] == coup :
+                    return i
+            return 0
+                           
+            
+        
+
+
+
+# Pour les tests :
+"""sym = plateau_symetrie_horizontale(plateau)
+affichage = Affichage(sym,False)
+partie = Partie(Humain(""),Humain(""),True,"")
+partie.plateau = sym
+affichage.afficher_canevas(partie)"""

+ 48 - 0
project/Outils/IA_TEST.py

@@ -0,0 +1,48 @@
+from Outils.Moteur_de_jeu import *
+from Outils.Moteur_de_jeu.Partie import *
+from Outils.IA_alphabeta import *
+from Outils.Moteur_de_jeu.Distance import *
+from Outils.Moteur_de_jeu.Plateau import *
+
+
+def fct_eval(plateau, num) :
+    nIA = plateau.longueur_chemin(num)
+    n_opponent = plateau.longueur_chemin(1-num)
+    b_IA = plateau.barrieres_restantes[num]
+    b_opponent = plateau.barrieres_restantes[1-num]
+
+    d = 0
+    """
+    if deux_chemins(plateau,num) : c = -4
+    if deux_chemins (plateau,1-num) : d = 3
+    
+    if c>3 :
+        return f(nIA) - f(n_opponent) + g2(b_IA) - g2(b_opponent)
+    else :
+    """
+    return f(nIA) - f(n_opponent) + g2(b_IA) - g2(b_opponent)
+
+
+
+
+
+
+def f(n) :
+    t = [1000000000,1000000,40,38]
+    a = len(t)
+    if n< a : return t[n]
+    else :
+        return 40-n
+
+def g2(b) :
+    t = [0.5,0,-0.5,-1,-1.5,-2,-2.5,-3,-3.5,-4,-7]
+    return t[10-b]
+
+def g(b) :
+    t = [2,0.9,-0.2,-1.3,-2.4,-3.5,-6,-8,-10,-13,-16]
+    return t[10-b]+5
+
+joueurA = Humain("Humain")
+joueurB = IA_minmax_alpha_beta("MinMax 3+", fct_eval, 3)
+Global.partie = Partie(joueurA, joueurB, True, Partie.DOSSIER_STANDARD)
+Global.partie.demarrer()

+ 42 - 0
project/Outils/IA_alphabeta.py

@@ -0,0 +1,42 @@
+from Outils.arbre_alpha_beta import *
+from Outils.Moteur_de_jeu import Joueur
+from Outils.Moteur_de_jeu.Joueur import *
+from Outils.Arbre import *
+from Outils.Moteur_de_jeu import Distance
+from Outils.Moteur_de_jeu.Distance import *
+
+class IA_minmax_alpha_beta(Joueur) :
+
+    dist = None
+
+    
+    def __init__(self, nom, fct_eval, prof) :
+        """
+            la fonction d'évaluation sera donnée en entrée, et évaluera les positions.
+            prof est la profondeur
+        """
+        Joueur.__init__(self,"O",nom)
+        self.fct_eval = fct_eval
+        self.prof = prof
+        
+
+       
+    def calculer_coup( self, plateau, liste_coup) :
+        #1. : avoir l'arbre
+        #2. : calculer le coup
+        #3 : le ressortir (trouver son indexation dans la liste des coups)
+
+        #etape 1
+        #etape 2
+        """
+        t = arb.fils
+        l=[]
+        maxi = t[0][1].valeur
+        for i in range(len(t)) :
+            l.append (t[i][1].valeur)
+            if t[i][1].valeur >= maxi :
+                maxi = t[i][1].valeur
+        print(l)"""
+
+        IA_minmax_alpha_beta.dist = distance_a_star( plateau )
+        return obtenir_coup (self.num, plateau, self.fct_eval, self.prof)

+ 56 - 0
project/Outils/IA_biland.py

@@ -0,0 +1,56 @@
+from Outils.Arbre import *
+from Outils.Moteur_de_jeu import Joueur
+from Outils.Moteur_de_jeu.Joueur import *
+from Outils.Moteur_de_jeu.Plateau import *
+from Outils.Moteur_de_jeu.Coup import *
+
+from random import shuffle
+
+def evaluation (plateau,n,premier,num) :
+    chemin = plateau.chemin(num)
+    t = plateau.pourri_chemin(chemin)
+    for k in t :
+        if not plateau.barriere_possible(k):
+            suppr(t,k)
+            
+    if chemin == [] :
+        print("il y a eu des erreurs")
+        return -10**100
+ 
+    if t == [] or n==0 :
+
+        return len(chemin)
+    #elif n == premier :
+    #    l = []
+    #    for bar in t :
+    #        coup = Coup("B",barriere = bar)
+    #        plateau.executer_coup(coup)
+    #        l.append(evaluation(plateau,n-1,premier,num))
+    #        
+    #        suppr(plateau.liste_barrieres , bar)
+    #
+    #
+    #        return(max(l))
+    else :
+        l = []
+        n2 = plateau.longueur_chemin(num)
+        n3 = plateau.longueur_chemin(1-num)
+        
+        for bar in t :
+            
+            coup = Coup("B",barriere = bar)
+            plateau.executer_coup(coup)
+            n1 = plateau.longueur_chemin(num)
+            n4 = plateau.longueur_chemin(1-num)
+            l.append(n1-n2-n3+n4)
+            suppr(plateau.liste_barrieres , bar)
+        k = max(l)
+        for i in range (len(l)) :
+            if l[i] == k:
+                coup = Coup("B",barriere = t[i])
+                plateau.executer_coup(coup)
+                return(evaluation(plateau,n-1,premier,num))
+                
+        
+
+        

+ 19 - 0
project/Outils/IA_chemin.py

@@ -0,0 +1,19 @@
+from Outils.Moteur_de_jeu import Pathfinding
+from Outils.Moteur_de_jeu.Pathfinding import *
+from Outils.Moteur_de_jeu import Joueur
+from Outils.Moteur_de_jeu.Joueur import *
+
+class IA_chemin_plus_court(Joueur) :      #Elric et Baptiste, 18/10/2016
+    """ I.A. brutale, pour tester. Hérite de la classe Joueur. """
+    def __init__(self, nom) :     #Elric et Baptiste, 18/10/2016
+        Joueur.__init__(self, "O", nom)
+
+    def calculer_coup(self, plateau, liste_coups) :  #Elric et Baptiste, 18/10/2016
+        t = path_finding (plateau, plateau.pions[self.num][0], plateau.pions[self.num][1],plateau.rangee_desiree(self.num))
+        for i in range (len(liste_coups)) :
+            a = liste_coups[i].case
+            a = (int(a[0]),int(a[1]))
+            if a in t :
+                return i
+        return (0)
+

+ 31 - 0
project/Outils/IA_semi_random.py

@@ -0,0 +1,31 @@
+from random import randint
+from Outils.Moteur_de_jeu import Pathfinding
+from Outils.Moteur_de_jeu.Pathfinding import *
+from Outils.Moteur_de_jeu import Joueur
+from Outils.Moteur_de_jeu.Joueur import *
+
+
+class IA_semi_random(Joueur) :
+    def __init__(self, nom) :
+        Joueur.__init__(self, "O", nom)
+
+    def calculer_coup(self, plateau, liste_coups) :
+        indices_B = []
+        for i in range(len(liste_coups)) :
+            if liste_coups[i].type == "B" :
+                indices_B.append(i)
+    
+    
+        #On choisit un des deux types de mouvement, on mélange la liste qui lui correspond et on en retourne le premier élément sous forme de coup
+        if randint(0,1) == 1 or indices_B == [] :   #Si aucune barrière ne peut être placée, on est obligé de se déplacer
+            chemin = path_finding (plateau, plateau.pions[self.num][0], plateau.pions[self.num][1],plateau.rangee_desiree(self.num))
+            for i in range (len(liste_coups)) :
+                if liste_coups[i].case == chemin[1] :
+                    return i
+
+            return 0
+            
+        else :
+            return randint(0, len(indices_B) - 1)
+            return indices_B[i]
+

+ 14 - 0
project/Outils/Moteur_de_jeu/Barriere.py

@@ -0,0 +1,14 @@
+class Barriere :
+    def __init__(barriere,orientation, x, y) :      # 09/2016
+        barriere.orientation = orientation #"h" ou "v"
+        barriere.x = x
+        barriere.y = y
+        #cf les conventions pour x et y
+
+    def __eq__(b1, b2) :    #Quentin 04/10/2016
+        """ Permet de savoir si deux barrières sont identiques """
+        return b1.orientation == b2.orientation and b1.x == b2.x and b1.y == b2.y
+
+
+    def copie(self) :
+        return Barriere(self.orientation, self.x, self.y)

+ 255 - 0
project/Outils/Moteur_de_jeu/Conversion.py

@@ -0,0 +1,255 @@
+from math import log
+from Outils.Moteur_de_jeu import Plateau
+from Outils.Moteur_de_jeu.Plateau import *
+
+
+
+BASE_REFERENCE = 62     # base utilisées par toutes les fonctions ici
+
+
+def tri_a_bulle(tab) :
+    n = len(tab)
+
+    for i in range(n - 1) :
+        for j in range(n - 1) :
+            if tab[j] > tab[j + 1] :
+                (tab[j],tab[j + 1]) = (tab[j + 1],tab[j])
+
+
+
+def insertion_place(tab, x, p) :
+    # Insère x à l'indice p, sans tenir compte de l'ordre
+    tab.append(x)
+
+    for i in range(len(tab) - 1, p, -1) :
+        (tab[i],tab[i - 1]) = (tab[i - 1], tab[i])
+
+
+
+def insertion_ordonnee(tab, x) :
+    tab.append(x)
+    i = len(tab) - 1
+
+    while tab[i] < tab[i - 1] and i > 0:
+        (tab[i],tab[i - 1]) = (tab[i - 1], tab[i])
+        i -= 1
+
+
+
+def recherche(tab, e) :
+    """ Recherche d'élément dans une tableau par dichotomie. Retourne un couple (booléen, indice) """
+    a = 0
+    b = len(tab) - 1
+
+    if b == -1 :
+        return (False, 0)
+
+    if tab[0] >= e :
+        return (tab[0] == e, 0)
+    elif tab[b] == e :
+        return (True, b)
+    elif tab[b] < e :
+        return (False, b + 1)
+    
+    c = 1
+    while b - a > 1 :
+        c = (a + b)//2
+
+        if tab[c] == e :
+            return (True, c)
+        elif tab[c] < e :
+            a = c
+        else :
+            b = c
+
+
+    if tab[c] == e :
+        return (True,c)
+    else :
+        if tab[c] < e :
+            return (False, c + 1)
+        else :
+            return (False, c)
+
+
+
+
+def conversion_base(x, bf) :
+    """
+        Convertit l'entier naturel x exprimée en base 10 dans la base bf
+    """
+    if x  == 0 :
+        return "0"
+    
+    chiffres = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+    s = ""
+
+    for n in range(int(log(x,bf)), -1, -1) :
+        s += chiffres[x//(bf**n)]
+        x = x % bf**n
+
+    return s
+
+
+
+def retour_base_10(s, bi) :
+    """
+        Convertit le nombre s exprimée en base bi dans la base 10
+    """
+    chiffres = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+    x = 0
+    n = len(s)
+    
+    for k in range(n) :
+        x += chiffres.find(s[n - 1 - k]) * bi**k
+
+    return x
+        
+    
+    
+        
+
+
+def code_from_barriere(b) :
+    """
+        Concatène les données relatives à la barrière sous la forme x,y,o
+        2 = v et 1 = h
+        Convertit ensuite ce nombre en base 62
+    """
+
+    if b.orientation == "h" :
+        n = 100*b.x + 10*b.y + 1
+    else :
+        n = 100*b.x + 10*b.y + 2
+
+    return conversion_base(n, BASE_REFERENCE)
+
+    
+    
+
+def code_from_plateau(plateau) :
+    """
+        Retourne l'entier associée au plateau
+        Structure : b1o,b1x,b1y ; ... ; bno,bnx,bny ; x0,y0,b0 ; x1,y1,b1 ; t
+        Les bio valent 1 si la barrière est horizontale et 2 sinon
+
+        Ce nombre est ensuite converti en base 62, tout en classant les barrières dans l'ordre croissant.
+    """    
+
+
+    s = conversion_base(10**8 * plateau.pions[1][0] + 10**7 * plateau.pions[1][1] + 10**5 * plateau.barrieres_restantes[1] +\
+                        10**4 * plateau.pions[0][0] + 10**3 * plateau.pions[0][1] + 10 * plateau.barrieres_restantes[0] +\
+                        plateau.tour, 62)
+
+        
+    codes_barrieres = [code_from_barriere(b) for b in plateau.liste_barrieres]
+    tri_a_bulle(codes_barrieres)
+
+    for b in codes_barrieres :
+        s = b + s
+    return s
+
+
+
+
+
+def coup_from_str(s) :
+    x = int(s[2])
+    y = int(s[4])
+
+    if s[0] == "M" :
+        return Coup("M", case = (x,y))
+    else :
+        return Coup("B", barriere = Barriere(s[6], x, y))
+
+
+
+
+
+
+def code_from_str(s) :
+    x = int(s[2])
+    y = int(s[4])
+
+    if s[0] == "M" :
+        n = 100*x + 10*y + 2
+    else :
+        n = 100*x + 10*y
+
+        if s[6] == "h" :
+            n +=  1
+
+    return conversion_base(n, BASE_REFERENCE)
+
+
+
+
+
+def code_from_coup(coup) :
+    if coup.type == "M" :
+        n = 100 * coup.case[0] + 10 * coup.case[1] + 2
+    else :
+        n = 100 * coup.barriere.x + 10 * coup.barriere.y
+
+        if coup.barriere.orientation == "h" :
+            n += 1
+
+    return conversion_base(n, BASE_REFERENCE)
+
+
+
+def coup_from_code(code) :
+    n = retour_base_10(code, BASE_REFERENCE)
+    info = n%10
+    x = n//100
+    y = (n - 100*x)//10
+
+
+    if info == 2 :
+        return Coup("M", case = (x,y))
+    else :
+        return Coup("B", barriere = Barriere(["v","h"][info], x, y))
+    
+ 
+
+
+
+"""coup = coup_from_str("B_4_5_h")
+print(coup.type, coup.barriere.x, coup.barriere.y, coup.barriere.orientation)
+
+coup = coup_from_str("M_4_5")
+print(coup.type, coup.case[0], coup.case[1])
+"""
+
+
+
+"""from random import shuffle
+p = Plateau(9,9,10)
+l = [Barriere("h",4,6), Barriere("h",2,6), Barriere("h",5,1), Barriere("h",3,2)]
+l = []
+shuffle(l)
+for b in l :
+    p.executer_coup(Coup("B", barriere = b))
+
+print(code_from_plateau(p))
+print(code_from_str("M_5_0"))
+print(coup_from_code("86").get_code())"""
+
+"""p = Plateau(9,9,10)
+p.executer_coup(Coup("B", barriere = Barriere("h",4,6)))
+p.executer_coup(Coup("B", barriere = Barriere("h",3,2)))
+print(code_from_plateau(p))
+
+
+p = Plateau(9,9,10)
+p.executer_coup(Coup("B", barriere = Barriere("h",3,2)))
+p.executer_coup(Coup("B", barriere = Barriere("h",4,6)))
+print(code_from_plateau(p))"""
+
+
+
+"""l = [4,9,4,57,547,5647,567,7,87,7,8]
+insertion_place(l, 0, 7)
+insertion_place(l, -1, 45)
+print(l)"""

+ 60 - 0
project/Outils/Moteur_de_jeu/Coup.py

@@ -0,0 +1,60 @@
+class Coup : # Baptiste 09/2016
+    def __init__( self, ctype, case = None, barriere = None ) : # Jovian : 11 octobre 2016
+        """
+            Permet de définir un coup réalisable ou réalisé par un joueur.
+            Attribut type : "B" ou "M" pour barrière ou mouvement.
+            Attribut case : case d'arrivée ( si type = "M" ) vaut None sinon. Format (x, y).
+            Attribut barriere : objet de type Barriere, contenant la barrière placée ou None.
+        """
+        self.type = ctype
+        self.case = case
+        self.barriere = barriere
+
+
+    def __eq__(c1, c2) :    # Quentin 17/10/2016
+        """ Permet de savoir si deux coups sont identiques """
+        return c1.type == c2.type and c1.barriere == c2.barriere and c1.case == c2.case
+
+
+
+    def copie(self) :
+        if self.type == "M" :
+            return Coup("M", case = self.case)
+        else :
+            return Coup("B", barriere = self.barriere)
+        
+
+    def get_code( self ) : # Jovian : 11 octobre 2016
+        """
+            Permet de récupérer un coup sous forme de chaîne de caractères.
+            Retour : Chaîne de caractères, syntaxe "B_x_y_orientation" ou "M_x_y".
+            Exemples :  Barrière au noeud x = 3 et y = 5 horizontale : "B_3_5_h" 
+                        Mouvement vers la case (7, 8) : "M_7_8"
+        """
+        # Variable de retour
+        rep = ""
+
+        # Codage d'un mouvement
+        if self.type == "M" :
+            rep += "M_"
+            (x , y) = self.case
+            rep += str(int(x))
+            rep += "_"
+            rep += str(int(y))
+
+            return rep
+
+        # Codage d'un placement de barrière
+        if self.type == "B" :
+            rep += "B_"
+            rep += str(int(self.barriere.x))
+            rep += "_"
+            rep += str(int(self.barriere.y))
+            rep += "_"
+            rep += self.barriere.orientation
+
+            return rep
+
+        # Gestion d'un type inexistants
+        print( "Plateau -> Coup -> get_code" )
+        print( "Erreur : type " + self.type + " inexistant." )

+ 99 - 0
project/Outils/Moteur_de_jeu/Distance.py

@@ -0,0 +1,99 @@
+import numpy as np
+from heapq import *
+
+from Outils.Moteur_de_jeu.Pathfinding import cases_accessibles
+
+def length_path(start, target, parent) : # Jovian : 17 janvier 2017
+    """ Construit et retourne la longueur du chemin solution.
+    """
+    i = 0
+    x, y = target
+    while (x, y) != start:
+        x, y = parent[x, y]
+        i += 1
+    return i
+
+
+def distance_a_star( plateau ) : # Jovian : 17 janvier 2017
+    """
+        Calcule la distance entre deux cases tenant compte des barrières.
+        Argument plateau : argument self de python.
+        Argument hcap : valeur malus ajoutée si le pion est en contact avec une barrière.
+        Retour : 
+            None si chemin inexistant.
+            Int comptant le nombre de cases.
+            Positions de départ exclue et d'arrivée inclue.
+    """
+    # Points
+    xi, yi = plateau.pions[0]
+    xf, yf = plateau.pions[1]
+    
+    # Variables
+    open_set = []
+    closed = np.zeros( (plateau.lg, plateau.ht), dtype = bool )
+    parent = np.zeros( (plateau.lg, plateau.ht, 2), dtype = int )
+    cost_so_far = np.ones((plateau.lg, plateau.ht)) * np.inf
+    
+    # Estimation distance restante
+    def heuristic( z ):
+        # Distance directe
+        x, y = z
+        dist =  abs(y - yf) + abs(x - xf)
+        return dist
+
+    # Ajoute un élément qui sera traité plus tard
+    def push(open_set, z):
+        priority = cost_so_far[z] + heuristic(z)
+        heappush(open_set, (priority, z) )
+
+    # Retire la case potentiellement la plus proche
+    def pop(open_set):
+        _, current = heappop(open_set)
+        return current
+
+    # Initialisation
+    start = xi, yi
+    cost_so_far[start] = 0
+    push(open_set, start)
+    current = (0, 0)
+
+    # Parcours
+    while open_set != []:
+        
+        # Case (x, y) en cours de traitement
+        current = pop(open_set)
+        x, y = current
+
+        # Si on a atteint l'arrivée
+        if current == ( xf, yf ) :
+            break
+
+        # Si la case a déjà été vue
+        if closed[current] : 
+            continue
+        
+        # On dit qu'on a traité cette case
+        closed[current] = True
+
+        # Parcours des voisins
+        for neighbor in cases_accessibles(plateau, x, y ):
+        
+            # Voisin déjà traité
+            if closed[neighbor]:
+                continue
+                
+            new_cost_neighbor = cost_so_far[current] + 1
+            
+            # Si notre chemin est plus court pour accéder à cette case
+            if new_cost_neighbor < cost_so_far[neighbor]:
+                cost_so_far[neighbor] = new_cost_neighbor
+                push(open_set, neighbor)
+                parent[neighbor] = current
+    
+    # Fin de parcours
+    if current == ( xf, yf ) :
+        # Chemin abouti
+        return length_path(start, current, parent)
+    else :
+        # Chemin inexistant
+        return None

File diff suppressed because it is too large
+ 420 - 0
project/Outils/Moteur_de_jeu/Fenetre.py


+ 51 - 0
project/Outils/Moteur_de_jeu/Joueur.py

@@ -0,0 +1,51 @@
+"""
+    Convention :
+    Toute I.A. possède une méthode calculer_coup. Celle-ci prend en argument le plateau de la situation courante et la liste des coups autorisés. Elle retourne un indice.
+
+"""
+
+from random import randint
+from Outils.Moteur_de_jeu.Coup import *    #Utilisé dans IA_random
+
+
+class Joueur :  #Quentin 13/10/2016
+    def __init__(self, jtype, nom) : #Quentin 14/10/2016
+        self.type = jtype     #"H" ou "O"
+        self.nom = nom
+        self.num = 0      #Par défaut, est modifié au début de la partie
+
+    def est_humain(joueur) :
+        return joueur.type == "H"
+
+
+
+class Humain(Joueur) :  #Quentin 14/10/2016
+    def __init__(self,nom) : #Quentin 14/10/2016
+        Joueur.__init__(self,"H",nom)
+        
+
+
+
+class IA_random(Joueur) :
+    """ I.A. aléatoire, pour tester. Hérite de la classe Joueur. """
+    def __init__(self, nom) :   #Quentin 13/10/2016
+        Joueur.__init__(self, "O", nom)
+
+    def calculer_coup(self, plateau, liste_coups) :  #Quentin 13/10/2016
+        indices_M = []
+        indices_B = []
+        for i in range(len(liste_coups)) :
+            if liste_coups[i].type == "M" :
+                indices_M.append(i)
+            else :
+                indices_B.append(i)
+    
+    
+        #On choisit un des deux types de mouvement, on mélange la liste qui lui correspond et on en retourne le premier élément sous forme de coup
+        if randint(0,1) == 1 or indices_B == [] :   #Si aucune barrière ne peut être placée, on est obligé de se déplacer
+            i = randint(0, len(indices_M) - 1)
+            return indices_M[i]
+        else :
+            return randint(0, len(indices_B) - 1)
+            return indices_B[i]
+

+ 278 - 0
project/Outils/Moteur_de_jeu/Partie.py

@@ -0,0 +1,278 @@
+from Outils.Moteur_de_jeu.Fenetre import *
+from Outils.Moteur_de_jeu.Joueur import *
+from random import randint
+
+from time import *
+
+import requests
+import json
+
+
+
+
+class Partie :   # Quentin 17/10/2016
+    """
+        Attributs :
+            .joueurs : Joueur * Joueur
+            .plateau : Plateau
+            .tour : int
+            .afficher : bool
+            .cases_selectionnables : int * int liste
+            .barrieres_selectionnables : Barriere liste
+            .liste_coups_licites : Coup liste
+            .etape : string
+            .deroulement : Coup liste
+            
+        Méthodes :
+            .joueur_actuel(self)
+            .est_selectionnable(self, barriere)
+            .executer_tour(self, coup)
+            .tourner_jeu(self)
+            .demarrer(self)
+            .sauvegarder_deroulement(self, nom, gagnant)
+    """
+
+    DOSSIER_STANDARD = "PARTIES_A_TRAITER"    # Chemin privililégié pour enregistrer les fichiers
+    
+    def __init__(self, joueurA, joueurB, afficher, dossier_save = "", demander_nom = False) : # Quentin 13/10/2016
+        """
+            joueurA et joueurB : les deux participants
+            afficher : booléen qui vaut true si une fenêtre doit s'afficher
+            dossier_save : '' -> pas de sauvegarde
+                           '*' -> sauvegarde sur jhub
+                           chemin -> sauvegarde dans le dossier indiqué
+        """
+        # Pour se souvenir :
+        self.joueurA = joueurA
+        self.joueurB = joueurB
+        
+        # Attribut .joueurs
+        if randint(0,1) == 1 : # Tirage au sort du joueur qui commence
+            self.joueurs = (joueurA, joueurB)
+            joueurA.num = 0
+            joueurB.num = 1
+        else :
+            self.joueurs = (joueurB, joueurA)
+            joueurB.num = 0
+            joueurA.num = 1
+        
+        # Attribut .plateau ; correspond respectivement à x lignes, y colonnes et n barrières par joueur
+        self.plateau = Plateau(9, 9, 10)
+        
+
+        # Attributs caractérisant l'affichage
+        config = self.types_joueurs()
+        self.afficher = afficher or config != "OO"      # L'affichage est nécessaire si un humain présent
+        self.demander_nom = demander_nom and config != "OO"         # S'il n'y a pas d'humain, il ne faut pas demander les noms
+
+
+        # Attribut caractérisant les items sélectionnables sur le plateau :
+        if self.joueur_actuel().type == "H" :
+                self.cases_selectionnables = self.plateau.cases_accessibles_joueur(0)
+                self.barrieres_selectionnables = self.plateau.liste_barrieres_possibles()
+        else :
+            self.cases_selectionnables = []
+            self.barrieres_selectionnables = []
+
+        #Attribut permettant de controler à posteriori le choix de l'I.A.
+        self.liste_coups_licites = self.plateau.liste_coups_possibles()
+
+        # Attribut .etape
+        self.etape = "J"
+        """
+            Quatre étapes possibles :
+            - J : Pour l'humain : se déplacer sans gagner -> J
+                                  se déplacer et atteindre la rangée opposée -> F
+                                  choisir de poser une barrière -> B1
+                  Pour l'ordinateur : attendre que l'utilisateur clique sur le bouton -> J
+
+            - B1 : choisir le barycentre de la barrière -> B2 si on peut choisir l'orientation, J sinon
+                   annuler -> J
+            - B2 : choisir l'orientation de la barrière -> J
+                   annuler -> B1
+            - F : fin de la self
+        """
+
+        # Attributs d'enregistrement
+        self.deroulement = []   # C'est une liste de Coups qu'il faut incrémenter à chaque action de joueur.
+        self.dossier_save = dossier_save
+        
+
+
+    def joueur_actuel(self) :      # Quentin 09/2016
+        """ Retourne le joueur dont c'est le tour. """
+        return self.joueurs[self.plateau.tour]
+
+
+    def types_joueurs(self) :
+        """ Renvoie 'OH', 'OO', 'HO' ou 'HH' selon les types des joueurs """
+        return self.joueurs[0].type + self.joueurs[1].type
+
+            
+
+    def est_selectionnable(self, barriere) :     # Quentin 04/10/2016
+        """ Permet de savoir si une barrière est dans la liste des barrières sélectionnables """
+        for b in self.barrieres_selectionnables :
+            if b == barriere :
+                return True
+        return False
+
+
+
+    def coup_IA(self) :      # Quentin 15/11/2016
+        """ Retourne le coup calculé par l'I.A. """
+        l = self.plateau.liste_coups_possibles()
+        id_coup = ( Global.partie.joueur_actuel().calculer_coup(Global.partie.plateau.copie_complete(), l) ) % len(l)
+        return l[id_coup]
+
+
+
+    def executer_tour(self, coup) :     # Quentin 15/11/2016
+        """
+            Fonction utilisée lorsqu'il y a affichage.
+            Applique un coup et fait la transition vers le tour suivant.
+        """      
+        
+        self.plateau.executer_coup(coup)
+        self.deroulement.append(coup)
+        
+        if self.plateau.gagnant() != -1 :    # Condition de victoire
+            self.etape = "F"
+            self.enregistrer_partie()
+                
+        else :
+            self.etape = "J"
+            if self.joueur_actuel().type == "H" :
+                # Calcul des nouvelles cases et barrières :
+                self.cases_selectionnables = self.plateau.cases_accessibles_joueur(self.plateau.tour)
+                self.barrieres_selectionnables = self.plateau.liste_barrieres_possibles()
+            else :
+                if self.types_joueurs() == "OO" or self.affichage.var_bouton.get() == 1 :
+                    self.cases_selectionnables = []
+                    self.barrieres_selectionnables = []
+                else :
+                    # Exécution immédiate du tour de l'I.A.
+                    self.affichage.rafraichissement(self)
+                    self.executer_tour(self.coup_IA())
+
+
+
+
+    def tourner_jeu(self) :     # Quentin 17/10/2016
+        """
+            Fonction utilisée lorsqu'il n'y a pas affichage.
+            Applique les coups en boucle
+            Retourne le gagnant
+        """
+
+
+        while self.plateau.gagnant() == -1 :  # Condition de victoire
+            coup = self.coup_IA()
+            self.plateau.executer_coup(coup)
+            self.deroulement.append(coup)
+
+
+        self.enregistrer_partie()
+        return self.joueurs[1 - self.plateau.tour]
+
+            
+
+
+
+    def lancement(self) :  # Quentin 15/11/2016
+        """ Lance la partie et retourne le gagnant dans le cadre d'un match d'I.A. """
+        if self.afficher :
+            self.affichage = Affichage(self.plateau, self.types_joueurs() in ("OH","HO"))
+            
+            if self.joueur_actuel().type == "O" and self.types_joueurs() in ("OH","HO") :
+                self.executer_tour(self.coup_IA())
+
+            self.affichage.rafraichissement(Global.partie)
+            self.affichage.fenetre.mainloop()
+        else :
+            return self.tourner_jeu()
+
+
+
+    def demarrer(self) :
+        """ Demande de saisir les noms puis lance la partie """
+        if self.demander_nom :
+            saisie_noms(self)
+        else :
+            return self.lancement()
+
+
+
+    def relancer(self) :        # Quentin 21/11/2016
+        """
+            Recrée une partie avec les mêmes joueurs et le même dossier de sauvegarde
+        """
+        return Partie(self.joueurs[0], self.joueurs[1], self.afficher, self.dossier_save, False)
+
+
+
+
+
+
+    #############################
+    # Sauvegarde de la partie : #
+    #############################
+
+    def obtenir_donnees(self) :
+        """ Retourne un couple (nom, contenu). Nom n'est que le nom du fichier, le chemin est ajouté après """
+        
+        (year, month, day, hour, minute, second, weekday, yearday, daylight) = localtime(time())
+        instant = "%02d-%02d-%04d %02dh%02dmin%02ds" % (day, month, year, hour, minute, second)
+        nom = self.joueurs[0].nom + " VS " + self.joueurs[1].nom + " " + instant + ".txt"
+
+        # En-tête :
+        contenu = ["Joueur 1 : " + self.joueurs[0].nom + " /" + self.joueurs[0].type + "\nJoueur 2 : " + self.joueurs[1].nom + " /" + self.joueurs[1].type + "\n\n"]
+        
+
+        # Coups :
+        for t in enumerate( self.deroulement ) :
+            k, cp = t
+            contenu.append( "Coup " + str(k + 1) + " : " + cp.get_code() + "\n" )
+
+        # Gagnant :
+        contenu.append("\nGagnant : joueur " + str(2 - self.plateau.tour))
+
+
+        return (nom, contenu)
+
+
+
+    def sauvegarde(self, nom, contenu, local) :
+        """ Enregistre les données localement si local = True, sur juhb dans le cas contraire """
+        
+        if local :
+            try :
+                flux = open(self.dossier_save + "/" + nom, "w" )
+            except :
+                print("Echec de l'enregistrement. Le fichier de partie a été envoyé sur jhub.")
+                self.sauvegarde(nom, contenu, False)
+                return
+
+            for l in contenu :
+                flux.write(l)
+
+            flux.close()
+
+
+        else :
+            info = {"nom" : nom, "contenu" : contenu}
+            data = json.dumps(info)
+            
+            requests.post('https://jhub.lycee-chateaubriand.fr/upload',
+                              headers={'Content-Type': 'application/json'},
+                              data=data, verify=False)
+
+
+    def enregistrer_partie(self) :
+        if self.dossier_save != "" :
+            (nom, contenu) = self.obtenir_donnees()
+            self.sauvegarde(nom, contenu, self.dossier_save != "*")
+            
+
+
+    

+ 284 - 0
project/Outils/Moteur_de_jeu/Pathfinding.py

@@ -0,0 +1,284 @@
+import numpy as np
+
+def suppr (t, a) : # Elric et Baptiste 09/2016
+    """ On supprime tout les elements valant a dans le tableau t """
+    for i in t :
+        if i == a :
+            t.remove(i)
+
+def cases_voisines(plateau, x, y) : # Elric et Baptiste 09/2016
+        """ Retourne les cases adjacentes à la case (x,y) """
+        
+        # On commence par tester les valeurs de x, puis on fait de même sur y.
+        if x == 0 :
+            if y == 0 :
+                return [(0, 1), (1, 0)]
+            elif y == plateau.ht - 1 :
+                return  [(1, y), (0, y-1)]
+            else :
+                return [(0, y-1), (0, y+1), (1, y)]
+        elif x == plateau.lg - 1 :
+            if y == 0 :
+                return [(x, 1), (x-1, 0)]
+            elif y == plateau.ht - 1 :
+                return  [(x-1, y), (x, y-1)]
+            else :
+                return [(x, y-1), (x, y+1), (x-1, y)]
+        else :
+            if y == 0 :
+                return [(x-1, y), (x+1, y), (x, 1)]
+            elif y == plateau.ht - 1 :
+                return [(x-1, y), (x+1, y), (x, y-1)]
+            else :
+                return [(x-1, y), (x+1, y), (x, y-1), (x, y+1)]
+
+
+
+def cases_accessibles(plateau, x, y) : # Elric et Baptiste 09/2016
+        """ Prend en compte les barrières """
+        t = cases_voisines ( plateau, x , y )
+        n = len(plateau.liste_barrieres)
+        b = plateau.liste_barrieres
+        for bar in b :
+            if bar.x == x :
+                if bar.y == y :
+                    if bar.orientation == "v" :
+                        suppr (t, (x+1, y))
+                    else:
+                        suppr (t, (x, y+1))
+                elif bar.y == y-1 :
+                    if bar.orientation == "v" :
+                        suppr (t, (x+1, y))
+                    else:
+                        suppr (t, (x, y-1))
+
+            elif bar.x == x-1 :
+                if bar.y == y :
+                    if bar.orientation == "v" :
+                        suppr (t, (x-1, y))
+                    else:
+                        suppr (t, (x, y+1))
+                elif bar.y == y-1 :
+                    if bar.orientation == "v" :
+                        suppr (t, (x-1, y))
+                    else:
+                        suppr (t, (x, y-1))
+        return (t)
+
+
+
+def path_finding_mapping( plateau, xi, yi, yf ) : # Jovian : 4 octobre 2016
+    """
+                Calcule le chemin le plus court pour gagner.
+                Argument plateau : argument self de python.
+                Argument xi : abscisse de départ.
+                Argument yi : ordonnée de départ.
+                Argument yf : ordonnée qui désigne la ligne d'arrivée.
+                Retour : 
+                        Une liste des couples (x, y) des positions successives.
+                        Position de départ inclue.
+                        Liste vide si aucun chemin possible.
+        """
+    # Cartographie les cases accessibles
+    carte = map_distance( plateau, xi, yi )
+    w = plateau.lg
+    h = plateau.ht
+
+    # Position variable
+    x = -1
+    y = yf
+
+    # Détermination du point d'arrivée
+    dist = -1
+    for k in range( w ) :
+        if carte[ k ][ yf ] > 0 :
+            if x == -1 or carte[ k ][ yf ] < dist :
+                x = k
+                dist = carte[ x ][ yf ]
+
+    # Arrivée non joignable
+    if x == -1 :
+        return []
+
+    # Reconstitution du chemin
+    rep = [(x, y)]
+    while x != xi or y != yi :
+        # Case actuelle
+        x, y = rep[ len(rep) - 1 ]
+        dist = carte[ x ][ y ]
+
+        # Récupère les cases accessibles
+        access = cases_accessibles( plateau, x, y )
+
+        # Recherche de la case la plus proche
+        while len( access ) > 0 :
+            u, v = access.pop(0)
+            if carte[ u ][ v ] < dist :
+                x = u
+                y = v
+                rep.append( (x, y) )
+                break
+
+    # Renvoie le chemin
+    return rep
+        
+
+def map_distance( plateau, xi, yi ) : # Jovian : 4 octobre 2016
+    """
+    Calcule un tableau contenant la distance de chaque case par rapport au joueur.
+    Tient compte de l'éloignement dû aux barrières.
+    La case contient 0 si inaccessible.
+    Argument plateau : argument self de python.
+    Argument xi : abscisse de départ.
+    Argument yi : ordonnée de départ.
+    Retour : un tableau d'entiers indexé (x, y).
+    """
+    # Création du tableau
+    w = plateau.lg
+    h = plateau.ht
+    rep = np.zeros( (w, h) )
+    rep[ xi ][ yi ] = 1
+
+    # Création de la file
+    fifo = [(xi, yi)]
+
+    # Balayage du plateau depuis la position de départ
+    while len( fifo ) > 0 :
+        # Point sur un bord
+        x, y = fifo.pop(0)
+        dist = rep[ x ][ y ]
+
+        # Récupère les cases accessibles
+        access = cases_accessibles( plateau, x, y )
+
+        # Traitement des cases adjacentes
+        while len( access ) > 0 :
+            u, v = access.pop(0)
+            if rep[ u ][ v ] == 0 :
+                rep[ u ][ v ] = dist + 1
+                fifo.append( (u, v ) )
+
+    # Retourne le tableau
+    return rep
+    
+
+## Version A*
+from heapq import *
+
+def get_path(start, target, parent) : # Jovian : 25 novembre 2016
+    """ Construit et retourne le chemin solution.
+    """
+    path = [target]
+    x, y = target
+    while (x, y) != start:
+        x, y = parent[x, y]
+        path.append((x, y))
+    return list( reversed(path) )
+
+
+def path_finding_a_star( plateau, xi, yi, yf, hcap = 0 ) : # Jovian : 7 décembre 2016 : éviter le contact barrières
+    """
+        Calcule le chemin le plus court pour gagner.
+        Argument plateau : argument self de python.
+        Argument xi : abscisse de départ.
+        Argument yi : ordonnée de départ.
+        Argument yf : ordonnée qui désigne la ligne d'arrivée.
+        Argument hcap : valeur malus ajoutée si le pion est en contact avec une barrière.
+        Retour : 
+            Une liste des couples (x, y) des positions successives.
+            Position de départ inclue.
+            Liste vide si aucun chemin possible.
+    """
+    # Variables
+    open_set = []
+    closed = np.zeros( (plateau.lg, plateau.ht), dtype = bool )
+    parent = np.zeros( (plateau.lg, plateau.ht, 2), dtype = int )
+    cost_so_far = np.ones((plateau.lg, plateau.ht)) * np.inf
+    
+    # Estimation distance restante
+    def heuristic( z ):
+        # Distance directe
+        x, y = z
+        dist =  abs(y - yf)
+        return dist
+
+    # Malus si contact avec une barrière
+    def malus( z ):
+        x , y = z
+        if plateau.contact( x, y ) :
+            return hcap
+        else :
+            return 0
+
+    # Ajoute un élément qui sera traité plus tard
+    def push(open_set, z):
+        priority = cost_so_far[z] + heuristic(z) + malus(z)
+        heappush(open_set, (priority, z) )
+
+    # Retire la case potentiellement la plus proche
+    def pop(open_set):
+        _, current = heappop(open_set)
+        return current
+
+    # Initialisation
+    start = xi, yi
+    cost_so_far[start] = 0
+    push(open_set, start)
+    current = (0, 0)
+
+    # Parcours
+    while open_set != []:
+        
+        # Case (x, y) en cours de traitement
+        current = pop(open_set)
+        x, y = current
+
+        # Si on a atteint l'arrivée
+        if current[1] == yf :
+            break
+
+        # Si la case a déjà été vue
+        if closed[current] : 
+            continue
+        
+        # On dit qu'on a traité cette case
+        closed[current] = True
+
+        # Parcours des voisins
+        for neighbor in cases_accessibles( plateau, x, y ):
+        
+            # Voisin déjà traité
+            if closed[neighbor]:
+                continue
+                
+            new_cost_neighbor = cost_so_far[current] + malus(neighbor) + 1
+            
+            # Si notre chemin est plus court pour accéder à cette case
+            if new_cost_neighbor < cost_so_far[neighbor]:
+                cost_so_far[neighbor] = new_cost_neighbor
+                push(open_set, neighbor)
+                parent[neighbor] = current
+    
+    # Fin de parcours
+    if current[1] == yf :
+        # Chemin abouti
+        return get_path(start, current, parent)
+    else :
+        # Chemin inexistant
+        return []
+
+## Alias du pathfinding
+def path_finding( plateau, xi, yi, yf ) : # Jovian : 7 décembre 2016 : éviter le contact barrières
+    """
+    Pour éviter le contact avec les barrières : mettez hcap = 0.05
+    Sinon hcap = 0.0
+    """
+    return path_finding_a_star(  plateau, xi, yi, yf, hcap = 0.05 )
+
+
+
+
+
+
+
+

+ 508 - 0
project/Outils/Moteur_de_jeu/Plateau.py

@@ -0,0 +1,508 @@
+from Outils.Moteur_de_jeu.Pathfinding import *
+from Outils.Moteur_de_jeu.Barriere import *
+from Outils.Moteur_de_jeu.Coup import *
+
+
+def suppr (t, a) :  #Elric et Baptiste 09/2016
+    """ On supprime tout les elements valant a dans le tableau t """
+    for i in t :
+        if i == a :
+            t.remove(i)
+
+
+"""
+Conventions :
+- Les coordonnées vont de 0 à lg - 1 horizontalement et ht - 1 verticalement 
+- L'origine des axes est en haut à gauche
+- Le barycentre d'une barrière est reperé par les coordonnées de la case en haut à gauche
+
+    --- ---
+   |ici|   |
+    ---B---     B = milieu de la barrière
+   |   |   |
+    --- ---
+    
+- Le joueur 0 commence sur la ligne 0, le joueur 1 sur la ligne ht - 1
+- D'accord ici
+"""
+
+
+
+
+class Plateau : # Jovian : 18 octobre 2016
+    """
+        On dispose des méthodes suivantes :
+        .copie(self)
+        .joueur_sur_case(self, num, x, y)
+        .cases_voisines(self, x, y)
+        .cases_accessibles(self, x, y)
+        .cases_accessibles_depuis(self, x, y, num)
+        .cases_accessibles_joueur(self, num)
+        .point_libre(plateau, x, y)
+        .point_barrieres_possibles(self, x, y)
+        .rangee_desiree(self, num)
+        .barriere_possible(self, barriere)
+        .liste_barrieres_possibles(plateau, num)
+        .liste_coups_possibles(self, num)
+        .executer_coup(self, coup, num)
+        .gagnant(self)
+        .longueur_chemin(self, num)
+        .barrieres_adjacentes
+        .pions_voisins
+        .bord_adjacent
+        .contact (self,num) 
+    """
+    
+    def __init__(self, lg, ht, nombre_barrieres) :     # Quentin 06/10/2016
+        self.pions = [[(lg-1)//2, 0], [(lg-1)//2, ht - 1]]
+        self.barrieres_restantes = [nombre_barrieres, nombre_barrieres]
+        self.liste_barrieres = []
+        self.lg = lg
+        self.ht = ht
+        self.tour = 0 # Joueur dont c'est le tour
+
+    def copie(self) :    #Quentin 06/10/2016
+        """ Retourne un nouveau plateau, copie de celui passé en paramètre. """
+        nouveau = Plateau(self.lg, self.ht, 0)
+        nouveau.pions = [list(self.pions[0]),list(self.pions[1])]
+        nouveau.barrieres_restantes = list(self.barrieres_restantes)
+        nouveau.liste_barrieres = list(self.liste_barrieres)
+        nouveau.tour = self.tour
+        return nouveau
+
+
+    def copie_complete(self) :
+        """ Copie également les barrières """
+        nouveau = Plateau(self.lg, self.ht, 0)
+        nouveau.pions = [list(self.pions[0]),list(self.pions[1])]
+        nouveau.barrieres_restantes = list(self.barrieres_restantes)
+
+        for b in self.liste_barrieres :
+            nouveau.liste_barrieres.append(b.copie())
+            
+        nouveau.tour = self.tour
+        return nouveau
+
+    
+
+    def joueur_sur_case(self, num, x, y) :   #Elric et Baptiste 10/2016
+        """ Retourne True si le joueur num est sur la case (x,y) """
+        return self.pions[num] == [x,y]
+
+
+
+    def cases_accessibles_depuis(self, x0, y0, num) : #Elric et Baptiste, 11/10
+        """Cases accessibles depuis la case (x0, y0) par le joueur num s'il était placé sur cette case. Prend en compte la possibilité de saut et les barrières."""
+        liste = cases_accessibles(self, x0, y0)
+
+        for c in liste :
+            (x, y) = c
+            if self.joueur_sur_case(1-num, x, y) :#Si l'adversaire est présent sur une case voisine,  
+                suppr(liste,c)
+                liste2 = cases_accessibles(self, x, y)
+                for i in liste2 :
+                    liste.append(i)
+                    
+                suppr(liste,(x0,y0))
+
+        return liste
+
+
+
+
+    
+
+    def cases_accessibles_joueur(self, num) :      #Quentin 06/10/2016
+        """ Cases accessibles par le joueur num depuis la case où il est réellement placé """
+        return self.cases_accessibles_depuis(self.pions[num][0], self.pions[num][1], num)
+
+    
+
+    def point_libre(self, x, y) :    #Elric et Baptiste 09/2016
+        """
+            Retourne une chaîne de caractères correspondant aux barrières pouvant être placées en (x, y)
+            "" : aucune
+            "h" : horizontale
+            "v" : verticale
+            "vh" : les deux
+        """
+        b = self.liste_barrieres
+        t = ["v", "h"]
+        for bar in b :
+            if bar.x == x-1 and bar.y == y and bar.orientation == "h" :
+                        suppr (t, "h")
+            elif bar.x == x+1 and bar.y == y and bar.orientation == "h" :
+                        suppr (t, "h")
+            elif bar.x == x and bar.y == y-1 and bar.orientation == "v" :
+                        suppr (t, "v")
+            if bar.x == x and bar.y == y+1 and bar.orientation == "v" :
+                        suppr (t, "v")
+            elif bar.x == x and bar.y == y :
+                t = []
+        while len(t) < 2 :
+            t.append(" ")
+        return(t[0] + t[1]) 
+
+    def pourri_chemin (self , t ) :
+        """
+        prend un plateau et un chemin en entrée
+        retourne la liste des barrières permettant de pourrir le chemin entré
+
+        """
+        n = len(t)
+        tab = []
+        for k in range (n - 1) :
+            x1 = t[k][0]
+            x2 = t[k+1][0]
+            y1 = t[k][1]
+            y2 = t[k+1][1]
+        
+            if x1 == x2 :
+                if y1 == y2 + 1 :
+                    tab.append ( Barriere("h", x1, y2) )
+                    tab.append (Barriere("h", x1 - 1, y2) )
+                else: 
+                    tab.append ( Barriere("h", x1, y1) )
+                    tab.append ( Barriere("h", x1 - 1, y1) )
+            else :
+                if  x1 == x2 + 1 :
+                    tab.append ( Barriere("v", x1 - 1, y1 - 1) )
+                    tab.append ( Barriere("v", x1 - 1, y1) )
+                else :
+                    tab.append ( Barriere("v", x1, y1 ) )
+                    tab.append ( Barriere("v", x1, y1 - 1) )
+
+        return tab
+    
+    def point_barrieres_possibles(self, x, y) :  #Elric et Baptiste 09/2016
+        """Retourne la liste des barrières pouvant être placées en (x, y)"""
+        p = self.point_libre(x, y)
+
+        if "  " == p :
+            return []
+        elif p == "h " :
+            return [Barriere("h", x, y)]
+        elif p == "v " :
+            return [Barriere("v", x, y)]
+        else :
+            return [Barriere("h", x, y), Barriere("v", x, y)]
+
+        
+    def rangee_desiree(self, num) :     #Quentin 04/10/2016
+        """ Retourne la rangée que doit atteindre le joueur num """
+        return (self.ht - 1)*(1 - num)
+    
+
+    def barriere_possible (self, barriere) : # Jovian 18/10/16 : optimisation
+        """
+            Permet de savoir si une barrière peut être placée sans isoler un pion du bord qu'il cherche à atteindre.
+            Argument self : le plateau qui lance la méthode.
+            Argument barriere : objet barrière supposée posable sans collision.
+            Retour : un booléen qui retourne True si la barrière est valide.
+        """
+
+
+        # Si la barrière a moins de deux contacts, elle ne peut pas fermer de contour. Il n'est donc pas nécessaire de la vérifier.
+        if self.nombre_contacts(barriere) < 2 :
+            return True
+
+        # Mise en place de la barrière pour l'expérience
+        self.liste_barrieres.append( barriere )
+
+        # Pathfinding joueur A
+        joueurA = self.pions[0]
+        trajetA = path_finding( self, joueurA[0], joueurA[1], self.rangee_desiree(0) )
+
+        if trajetA == [] :
+            # Arrivée non joignable dans ce cas
+            self.liste_barrieres.pop()
+            return False
+
+        # Pathfinding joueur B
+        joueurB = self.pions[1]
+        trajetB = path_finding( self, joueurB[0], joueurB[1], self.rangee_desiree(1) )
+
+        if trajetB == [] :
+            # Arrivée non joignable dans ce cas
+            self.liste_barrieres.pop()
+            return False
+
+        # L'arrivée est joignable
+        self.liste_barrieres.pop()
+        return True
+
+
+##    def liste_barrieres_possibles (self) :   #Elric et Baptiste 04/10/2016 #accélération Elric 25/11
+##        """ 
+##            Retourne la liste de toutes les barrières pouvant être placées ce tour-ci en tenant compte de toutes les contraintes existantes.
+##        """
+##        
+##        if self.barrieres_restantes[self.tour] == 0 :
+##            return []
+##        tab = []
+##        
+##        for y in range (self.ht - 1) :
+##            for x in range (self.lg - 1) :
+##                bar = self.point_barrieres_possibles (x, y)
+##                for i in bar :
+##                    if self.barriere_possible (i) :
+##                        tab.append(i)
+##        
+##        
+##        """joueurA = self.pions[0]
+##        trajetA = path_finding( self, joueurA[0], joueurA[1], self.rangee_desiree(0) )
+##        t = self.pourri_chemin(trajetA)
+##        joueurB = self.pions[1]
+##        trajetB = path_finding( self, joueurB[0], joueurB[1], self.rangee_desiree(1) )
+##
+##        t += self.pourri_chemin(trajetB)
+##
+##        for i in t :
+##            if self.barriere_possible (i) :
+##                tab.append(i)"""
+##        
+##        return tab
+
+
+
+
+    def liste_barrieres_possibles (self) :   #Elric et Baptiste 04/10/2016 #accélération Elric 25/11
+        """ 
+            Retourne la liste de toutes les barrières pouvant être placées ce tour-ci en tenant compte de toutes les contraintes existantes.
+        """
+        
+        if self.barrieres_restantes[self.tour] == 0 :
+            return []
+        tab = []
+        joueurA = self.pions[0]
+        trajetA = path_finding( self, joueurA[0], joueurA[1], self.rangee_desiree(0) )
+        t = self.pourri_chemin(trajetA)
+        joueurB = self.pions[1]
+        trajetB = path_finding( self, joueurB[0], joueurB[1], self.rangee_desiree(1) )
+
+        t += self.pourri_chemin(trajetB)
+
+        for x in range ( self.lg -1):
+            for y in range (self.ht -1) :
+                bar = self.point_barrieres_possibles( x, y)
+                for i in bar :
+                    if i in t :
+                        if self.barriere_possible (i) :
+                            tab.append(i)
+                    else :
+                        tab.append(i)
+
+        return tab
+
+
+    def liste_coups_possibles(self) :
+        """
+            Liste des coups possibles à disposition du joueur actuel
+        """
+        t = []
+        mouvements = self.cases_accessibles_joueur(self.tour)
+        barrieres = self.liste_barrieres_possibles()
+
+        #On met les mouvements
+        for m in mouvements :
+            t.append(Coup("M",case = m))
+
+        # On met les barrières
+        for b in barrieres :
+            t.append(Coup("B",barriere = b))
+
+        return t
+
+
+
+                            
+    def executer_coup(self, coup) :    #Quentin 06/10/2016
+        """ Exécute un coup de la part du joueur dont c'est le tour"""
+        
+        if coup.type == "M" :
+            self.pions[self.tour][0] = coup.case[0]
+            self.pions[self.tour][1] = coup.case[1]
+        else :
+            self.liste_barrieres.append(coup.barriere)
+            self.barrieres_restantes[self.tour] -= 1
+
+        self.tour = 1 - self.tour
+
+
+
+
+    def gagnant(self) :
+        """
+            Détermine si un plateau correpsond à une partie terminée. Retourne :
+            > -1 si la partie est toujours en cours
+            > le numéro du gagannt sinon
+        """
+
+        if self.pions[0][1] == self.rangee_desiree(0) :
+            return 0
+        elif self.pions[1][1] == self.rangee_desiree(1) :
+            return 1
+        else :
+            return -1
+
+    def chemin(self,num) :
+        return path_finding(self, self.pions[num][0], self.pions[num][1], self.rangee_desiree(num))
+    
+    def longueur_chemin(self, num) :        # Quentin 08/11/2016
+        """ Retourne la longueur du chemin le plus court entre un joueur et le bord recherché """
+        return len( path_finding(self, self.pions[num][0], self.pions[num][1], self.rangee_desiree(num)) ) - 1
+        
+
+    
+    def bord_adjacent (self, bar) : # Elric, 09/11/2016
+        """
+            Prend un barrière en entrée, et retourne le tableau contenant les bords la touchant
+            RETOURNE un tableau de booléens
+        """
+        if bar.orientation == "h" :
+            return [bar.x == 0, False, bar.x == self.lg - 2]
+        else :
+            return [bar.y == 0, False, bar.y == self.ht - 2]
+
+    def pions_voisins(self, bar) : # Elric, 19/11/2016
+        """
+            Prend une barrière en entrée, donne s'il y a un pion qui colle la barrière ou pas.
+            RETOURNE un booléen
+        """
+        position1 = self.pions[0]
+        position2 = self.pions[1]
+        t = [[bar.x,bar.y] , [bar.x + 1 , bar.y + 1] ,
+             [bar.x , bar.y + 1 ] , [bar.x +1, bar.y ]]
+        return ( position1 in t or position2 in t )
+
+    def barrieres_adjacentes(self, bar) :
+        """
+            Prend une barrière en entrée, et retourne le tableau contenant les barrières la touchant
+        """
+        if bar.orientation == "h" :
+            b1 = Barriere ("v", bar.x, bar.y + 1)
+            b2 = Barriere ("v", bar.x - 1, bar.y + 1)
+            b3 = Barriere ("v", bar.x + 1, bar.y + 1)
+            b4 = Barriere ("v", bar.x, bar.y - 1)
+            b5 = Barriere ("v", bar.x + 1, bar.y - 1)
+            b6 = Barriere ("v", bar.x - 1, bar.y - 1)
+            b7 = Barriere ("h", bar.x - 2, bar.y)
+            b8 = Barriere ("h", bar.x + 2, bar.y)
+            b9 = Barriere ("v", bar.x - 1, bar.y)
+            b10 =  Barriere ("v", bar.x + 1, bar.y)
+            t = [b1, b2, b3, b4, b5, b6, b7, b8, b9, b10 ]
+            res = []
+            for a in self.liste_barrieres :
+                if a in t :
+                    res.append(a)
+        else :
+            b1 = Barriere ("v", bar.x, bar.y + 2)
+            b2 = Barriere ("v", bar.x, bar.y - 2)
+            b3 = Barriere ("h", bar.x + 1, bar.y + 1)
+            b4 = Barriere ("h", bar.x + 1, bar.y - 1)
+            b5 = Barriere ("h", bar.x + 1, bar.y)
+            b6 = Barriere ("h", bar.x - 1, bar.y - 1)
+            b7 = Barriere ("h", bar.x - 1, bar.y)
+            b8 = Barriere ("h", bar.x - 1, bar.y + 1)
+            b9 = Barriere ("h", bar.x , bar.y - 1)
+            b10 = Barriere ("h", bar.x , bar.y + 1)
+            res = []
+            t = [b1, b2, b3, b4, b5, b6, b7, b8, b9, b10 ]
+            for a in self.liste_barrieres :
+                if a in t :
+                    res.append(a)
+        return res
+
+
+
+
+    def barrieres_adjacentes_precis(self, b0) :     # Quentin 15/11/2016
+        """
+            b0 est une barrière, qui peut être vue comme un triplet de trois noeuds.
+            Cette fonction retourne la liste des barrières et des bords (représentés par des None) qui touchent chaque noeud, sous forme d'un tableau de tableaux.
+            Le sens de lecture est de gauche à droite si b0 est horizontale, et de haut en bas si b0 est verticale.
+        """
+
+        tab_final = [[],[],[]]
+        if b0.orientation == "h" :
+            tab_potentiel = [[Barriere ("v", b0.x - 1, b0.y - 1), Barriere ("v", b0.x - 1, b0.y), Barriere ("v", b0.x - 1, b0.y + 1), Barriere ("h", b0.x - 2, b0.y)],
+                             [Barriere ("v", b0.x, b0.y - 1), Barriere ("v", b0.x, b0.y + 1)],
+                             [Barriere ("v", b0.x + 1, b0.y - 1), Barriere ("v", b0.x + 1, b0.y),  Barriere ("v", b0.x + 1, b0.y + 1), Barriere ("h", b0.x + 2, b0.y)]]
+
+            if b0.x == 0 :
+                tab_final[0].append(None)
+            if b0.x == self.lg - 2 :
+                tab_final[2].append(None)
+
+            
+        else :
+            tab_potentiel = [[Barriere ("h", b0.x - 1, b0.y - 1), Barriere ("h", b0.x, b0.y - 1), Barriere ("h", b0.x + 1, b0.y - 1), Barriere ("v", b0.x, b0.y - 2)],
+                             [Barriere ("h", b0.x - 1, b0.y), Barriere ("h", b0.x + 1, b0.y)],
+                             [Barriere ("h", b0.x - 1, b0.y + 1), Barriere ("h", b0.x, b0.y + 1), Barriere ("h", b0.x + 1, b0.y + 1), Barriere ("v", b0.x, b0.y + 2)]]
+
+            if b0.y == 0 :
+                tab_final[0].append(None)
+            if b0.y == self.ht - 2 :
+                tab_final[2].append(None)
+
+
+        for b in self.liste_barrieres :
+            for i in range(3) :
+                if b in tab_potentiel[i] :
+                    tab_final[i].append(b)
+
+
+        return tab_final
+
+
+
+
+    def nombre_contacts(self, b) :
+        """ Retourne le nombre de noeuds de la barrière b ayant un contact avec une barrière du plateau """
+        tab = self.barrieres_adjacentes_precis(b)
+        nb = 0
+
+        for i in range(3) :
+            if tab[i] != [] :
+                nb += 1
+
+        return nb
+
+    def contact (self, x, y) :#Elric, 25/11
+        """
+        Si une barrière touche le pion num. C'est un booléen
+        """
+        for i in self.liste_barrieres :
+            if (i.x == x and i.y == y ) or (i.x == x-1 and i.y == y ) or (i.x == x and i.y == y-1 ) or (i.x == x-1 and i.y == y-1 ) :
+                return True
+        return False
+                
+     
+
+
+      
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 83 - 0
project/Outils/Symetries.py

@@ -0,0 +1,83 @@
+##############
+# Barriere : #
+##############
+
+def barriere_symetrie_verticale(plateau, barriere) :
+    sym = barriere.copie()
+    sym.x = plateau.lg - 2 - sym.x
+    return sym
+
+
+def barriere_symetrie_horizontale(plateau, barriere) :
+    sym = barriere.copie()
+    sym.y = plateau.ht - 2 - sym.y
+    return sym
+
+
+
+
+
+##########
+# Coup : #
+##########
+
+def coup_symetrie_verticale(plateau, coup) :
+    sym = coup.copie()
+    
+    if sym.type == "M" :
+        (x,y) = sym.case
+        sym.case = (x, plateau.lg - 1 - y)
+    else :
+        sym.barriere = barriere_symetrie_verticale(plateau, sym.barriere)
+
+    return sym
+
+
+    
+def coup_symetrie_horizontale(plateau, coup) :
+    sym = coup.copie()
+    
+    if sym.type == "M" :
+        (x,y) = sym.case
+        sym.case = (x, plateau.ht - 1 - y)
+    else :
+        sym.barriere = barriere_symetrie_horizontale(plateau, sym.barriere)
+
+    return sym
+    
+
+
+#############
+# Plateau : #
+#############
+
+def plateau_symetrie_verticale(plateau) :
+    sym = plateau.copie_complete()
+
+    for i in range(2) :
+        sym.pions[i][0] = sym.lg - 1 - sym.pions[i][0]
+
+    for b in sym.liste_barrieres :
+        b.x = sym.lg - 2 - b.x
+
+    return sym
+
+
+
+def plateau_symetrie_horizontale(plateau) :
+    sym = plateau.copie_complete()
+
+    (sym.barrieres_restantes[1], sym.barrieres_restantes[0]) = (sym.barrieres_restantes[0], sym.barrieres_restantes[1])
+    sym.tour = 1 - sym.tour
+
+    for num in (0,1) :
+        sym.barrieres_restantes[num] = plateau.barrieres_restantes[1 - num]
+        sym.pions[num][0] = plateau.pions[1 - num][0]
+        sym.pions[num][1] = sym.ht - 1 - plateau.pions[1 - num][1]
+
+    for b in sym.liste_barrieres :
+        b.y = sym.ht - 2 - b.y
+
+    return sym
+
+

+ 24 - 0
project/Outils/alphabeta.py

@@ -0,0 +1,24 @@
+from Outils.arbre_alpha_beta import *
+from Outils.Moteur_de_jeu import Joueur
+from Outils.Moteur_de_jeu.Joueur import *
+
+
+class IA_minmax_alpha_beta(Joueur) :
+    def __init__(self, nom, fct_eval, prof) :
+        """
+            la fonction d'évaluation sera donnée en entrée, et évaluera les positions.
+            prof est la profondeur
+        """
+        Joueur.__init__(self,"O",nom)
+        self.fct_eval = fct_eval
+        self.prof = prof              
+
+       
+    def calculer_coup( self, plateau, liste_coup) :
+        #1. : avoir l'arbre
+        #2. : calculer le coup
+        #3 : le ressortir (trouver son indexation dans la liste des coups)
+
+        #etape 1
+
+        return obtenir_coup (self.num, plateau, self.fct_eval, self.prof)

+ 106 - 0
project/Outils/arbre_alpha_beta.py

@@ -0,0 +1,106 @@
+from random import randint
+from random import shuffle
+
+from Outils.Elagage import *
+from Outils.Moteur_de_jeu import Distance
+from Outils.Moteur_de_jeu.Distance import *
+
+
+
+def obtenir_coup (num, plateau, fct_eval, H) :
+    liste_coups = plateau.liste_coups_possibles()
+    t = []
+    for coup in liste_coups :
+        if elagage(plateau, coup) :
+            t.append ([coup, None ])
+    alpha = -10**100
+    beta = 10**100
+    dist = distance_a_star(plateau)
+
+    for k in range (len(t)) :
+        plateau2 = plateau.copie()
+  
+        plateau2.executer_coup (t[k][0])
+        t[k][1] = alphabeta (plateau2, num, alpha, beta, fct_eval,  1, H)
+        alpha = max (alpha, t[k][1])
+
+    maxi = t[0][1] 
+    l = []
+    for i in range(len(t)) :
+        l.append(t[i][1])
+        if t[i][1] >= maxi :
+            maxi = t[i][1]
+
+    liste_coups = []
+    a = []
+    for i in range(len(t)) :
+        if t[i][1] == maxi :
+            liste_coups.append(t[i][0])
+
+
+
+
+        
+
+        
+    shuffle(liste_coups)
+    coup = liste_coups[0]
+    liste = plateau.liste_coups_possibles()
+
+    for i in range(len(liste)) :
+        if liste[i] == coup :
+            return i
+
+
+
+
+def alphabeta (plateau, num, alpha, beta, fct_eval, h, H) :
+
+    if plateau.gagnant() !=-1 :
+        return fct_eval (plateau, num )
+    
+    if h == H :
+        return fct_eval (plateau, num )
+    else :
+        if h%2 == 1 :
+            Val = 10**100
+            liste_coups = plateau.liste_coups_possibles()
+            t = []
+            for coup in liste_coups :
+                if elagage(plateau, coup) :
+                    t.append (coup)
+
+            for k in range (len(t)) :
+                plateau2 = plateau.copie()
+                plateau2.executer_coup (t[k])
+
+                Val = min (Val, alphabeta (plateau2, num, alpha, beta, fct_eval, h+1, H))
+                if alpha > Val :
+                    return Val
+                beta = min (beta, Val)
+        else :
+            Val = -10**100
+            liste_coups = plateau.liste_coups_possibles()
+            t = []
+            for coup in liste_coups :
+                if elagage(plateau, coup) :
+                    t.append (coup)
+
+            for k in range (len(t)) :
+                plateau2 = plateau.copie()
+                plateau2.executer_coup (t[k])
+
+                Val = max (Val, alphabeta (plateau2, num, alpha, beta, fct_eval, h+1, H))
+                if beta <= Val :
+                    return Val
+                alpha = max (alpha, Val)
+
+    return Val
+
+
+
+
+
+
+
+                

+ 7 - 0
project/Q _ I.A. vs I.A. (aléatoire).py

@@ -0,0 +1,7 @@
+from Outils.Moteur_de_jeu import *
+from Outils.Moteur_de_jeu.Partie import *
+
+joueurA = IA_random("I.A. aleatoire")
+joueurB = IA_random("I.A. aleatoire")
+Global.partie = Partie(joueurA, joueurB, True, "")
+Global.partie.demarrer()

+ 9 - 0
project/Q _ JEU HUMAINS.py

@@ -0,0 +1,9 @@
+from Outils.Moteur_de_jeu import Partie
+from Outils.Moteur_de_jeu.Partie import *
+
+
+
+joueurA = Humain("Humain")
+joueurB = Humain("Humain")
+Global.partie = Partie(joueurA, joueurB, True, Partie.DOSSIER_STANDARD, True)
+Global.partie.demarrer()

+ 190 - 0
project/Q_Anti-Gael.py

@@ -0,0 +1,190 @@
+# Anti-Gaël
+
+from Outils.Moteur_de_jeu import *
+from Outils.Moteur_de_jeu.Partie import *
+from Outils import alphabeta
+from Outils.alphabeta import *
+
+from Outils.Moteur_de_jeu import *
+from Outils.Moteur_de_jeu.Partie import *
+from Outils import IA_alphabeta
+from Outils.IA_alphabeta import *
+
+
+
+def suppr (t, a) :  #Elric et Baptiste 09/2016
+    """ On supprime tout les elements valant a dans le tableau t """
+    for i in t :
+        if i == a :
+            t.remove(i)
+
+
+def panzer (plateau,num) :
+    if  plateau.rangee_desiree(num) == 8 :
+
+        b1 = Barriere ("h", 3, 5)
+        b2 = Barriere ("h", 6, 5)   
+
+        tr = [b1,b2]
+        bar = plateau.liste_barrieres_possibles()
+        for k in tr :
+            if k in plateau.liste_barrieres :
+                suppr (tr,k)
+        for k in bar :
+            if k == b1 :
+                return Coup("B", barriere = b1)
+            elif k == b2 :
+                        return Coup("B", barriere = b2)
+       
+        if plateau.joueur_sur_case( 1 - num, 5, 6) :
+            b1 = Barriere ("v", 5, 4)
+            b2 = Barriere ("v", 3, 4)
+            b3 = Barriere ("v", 4, 4)
+            b4 = Barriere ("v", 6, 4)
+            t = [b1,b2,b3,b4]
+            for k in t :
+                if k in plateau.liste_barrieres :
+                    suppr (t,k)
+            if len(t)==4 :
+                for k in bar :
+                    if k == b1 :
+                        return Coup("B", barriere = b1)
+                for k in bar :
+                    if k == b2 :
+                        return Coup("B", barriere = b2)
+                for k in bar :
+                    if k == b3 :
+                        return Coup("B", barriere = b3)
+                for k in bar :
+                    if k == b4 :
+                        return Coup("B", barriere = b4)
+    else :
+        b1 = Barriere ("h", 3, 2)
+        b2 = Barriere ("h", 6, 2)   
+
+        tr = [b1,b2]
+        bar = plateau.liste_barrieres_possibles()
+        for k in tr :
+            if k in plateau.liste_barrieres :
+                suppr (tr,k)
+        for k in bar :
+            if k == b1 :
+                return Coup("B", barriere = b1)
+            elif k == b2 :
+                        return Coup("B", barriere = b2)
+       
+        if plateau.joueur_sur_case( 1 - num, 5, 2) :
+            b1 = Barriere ("v", 5, 3)
+            b2 = Barriere ("v", 3, 3)
+            b3 = Barriere ("v", 4, 3)
+            b4 = Barriere ("v", 6, 3)
+            t = [b1,b2,b3,b4]
+            for k in t :
+                if k in plateau.liste_barrieres :
+                    suppr (t,k)
+            if len(t)==4 :
+                for k in bar :
+                    if k == b1 :
+                        return Coup("B", barriere = b1)
+                for k in bar :
+                    if k == b2 :
+                        return Coup("B", barriere = b2)
+                for k in bar :
+                    if k == b3 :
+                        return Coup("B", barriere = b3)
+                for k in bar :
+                    if k == b4 :
+                        return Coup("B", barriere = b4)
+            
+            
+        
+
+    return (Coup("B", barriere = Barriere ("v", 42, 42)   ))
+            
+            
+            
+
+
+
+
+
+class IA_Anti_Gaël(Joueur) :
+    def __init__(self, nom, fct_eval, prof ) :
+        """
+            la fonction d'évaluation sera donnée en entrée, et évaluera les positions.
+            prof indique la profondeur souhaitée 
+        """
+        Joueur.__init__(self,"O",nom)
+        self.fct_eval = fct_eval
+        self.prof = prof              
+
+    
+    def calculer_coup( self, plateau, liste_coup) :
+        #1. : avoir l'arbre
+        #2. : calculer le coup
+        #3 : le ressortir (trouver son indexation dans la liste des coups)
+
+        #etape 1
+        coup = panzer(plateau, self.num)
+
+        if not coup == Coup("B", barriere = Barriere ("v", 42, 42)) :
+            
+            for i in range(len(liste_coup)) :
+                if liste_coup[i] == coup :
+
+                    return i
+
+        return obtenir_coup(self.num, plateau, self.fct_eval, self.prof)
+        
+
+
+
+
+
+
+
+def fct_eval(plateau, num) :
+    nIA = plateau.longueur_chemin(num)
+    n_opponent = plateau.longueur_chemin(1-num)
+    b_IA = plateau.barrieres_restantes[num]
+    b_opponent = plateau.barrieres_restantes[1-num]
+    if IA_minmax_alpha_beta.dist == None or IA_minmax_alpha_beta.dist >5 :
+        return f(nIA) - f(n_opponent) + g(b_IA) - g(b_opponent) 
+        
+    """
+    if deux_chemins(plateau,num) : c = -4
+    if deux_chemins (plateau,1-num) : d = 3
+    """
+    return f(nIA) - f(n_opponent) + g2(b_IA) - g2(b_opponent) 
+
+
+
+
+
+
+def f(n) :
+    t = [1000000000,1000000,40,38]
+    a = len(t)
+    if n< a : return t[n]
+    else :
+        return 40-n
+
+
+
+def g(b) :
+    t = [6,4,2,0,-2,-4,-6,-8,-10,-12.9,-15.8]
+    return t[10-b]
+def g2(b) :
+    t = [0.5,0,-0.5,-1,-1.5,-2,-3,-4,-5,-7.9,-10.8]
+    return t[10-b]
+
+
+joueurA = Humain("Humain")
+joueurB = IA_Anti_Gaël("AntiGael", fct_eval, 3)     # /!\ Ne pas mettre d'accent à Gael
+Global.partie = Partie(joueurA, joueurB, True, Partie.DOSSIER_STANDARD, True)
+Global.partie.demarrer()
+
+
+
+
+

+ 53 - 0
project/Q_Biland.py

@@ -0,0 +1,53 @@
+from Outils.Moteur_de_jeu import *
+from Outils.Moteur_de_jeu.Partie import *
+from Outils import IA_alphabeta
+from Outils.IA_alphabeta import *
+from Outils.IA_biland import *
+
+
+def fct_eval_Biland (plateau,num) :
+    b_IA = plateau.barrieres_restantes[num]
+    b_adv = plateau.barrieres_restantes[1-num]
+    return -evaluation(plateau,b_IA,num) + evaluation(plateau,b_adv,1-num)
+
+def fct_eval(plateau, num) :
+    nIA = plateau.longueur_chemin(num)
+    n_opponent = plateau.longueur_chemin(1-num)
+    b_IA = plateau.barrieres_restantes[num]
+    b_adv = plateau.barrieres_restantes[1-num]
+    e = 0
+    if b_IA < 7 :
+        e = - evaluation(plateau,b_adv,b_adv,num) + evaluation(plateau,b_IA,b_IA,1-num)
+        print(e)
+    if IA_minmax_alpha_beta.dist == None or IA_minmax_alpha_beta.dist > 5 :
+        return   (f(nIA) - f(n_opponent) + g(b_IA) - g(b_adv))/10 + e
+        
+    """
+    if deux_chemins(plateau,num) : c = -4
+    if deux_chemins (plateau,1-num) : d = 3
+    """
+    return 10*e + f(nIA) - f(n_opponent) + g2(b_IA) - g2(b_adv) 
+
+
+ 
+
+def f(n) :
+    t = [1000000000,1000000,40,38]
+    a = len(t)
+    if n< a : return t[n]
+    else :
+        return 40-n
+
+
+
+def g(b) :
+    t = [6,4,2,0,-2,-4,-6,-8,-10,-12.9,-15.8]
+    return t[10-b]
+def g2(b) :
+    t = [0.5,0,-0.5,-1,-1.5,-2,-3,-4,-5,-7.9,-10.8]
+    return t[10-b]
+
+joueurA = Humain("Humain")
+joueurB = IA_minmax_alpha_beta("Biland", fct_eval, 2)
+Global.partie = Partie(joueurA, joueurB, True, Partie.DOSSIER_STANDARD, True)
+Global.partie.demarrer()

+ 18 - 0
project/Q_HumainVsAlphaBeta2.py

@@ -0,0 +1,18 @@
+from Outils.Moteur_de_jeu import *
+from Outils.Moteur_de_jeu.Partie import *
+from Outils import IA_alphabeta
+from Outils.IA_alphabeta import *
+
+
+def fct_eval(plateau, num) :
+    nIA = plateau.longueur_chemin(num)
+    n_opponent = plateau.longueur_chemin(1-num)
+    return -nIA + n_opponent 
+
+
+
+
+joueurA = Humain("Humain")
+joueurB = IA_minmax_alpha_beta("AlphaBeta 2", fct_eval, 2)
+Global.partie = Partie(joueurA, joueurB, True, Partie.DOSSIER_STANDARD)
+Global.partie.demarrer()

+ 35 - 0
project/Q_HumainVsAlphaBeta3+.py

@@ -0,0 +1,35 @@
+from Outils.Moteur_de_jeu import *
+from Outils.Moteur_de_jeu.Partie import *
+from Outils import IA_alphabeta
+from Outils.IA_alphabeta import *
+
+def fct_eval(plateau, num) :
+    nIA = plateau.longueur_chemin(num)
+    n_opponent = plateau.longueur_chemin(1-num)
+    b_IA = plateau.barrieres_restantes[num]
+    b_opponent = plateau.barrieres_restantes[1-num]
+	
+    if IA_minmax_alpha_beta.dist == None or IA_minmax_alpha_beta.dist > 5 :
+        return f(nIA) - f(n_opponent) + g(b_IA) - g(b_opponent)
+		
+    return f(nIA) - f(n_opponent) + g2(b_IA) - g2(b_opponent) 
+
+def f(n) :
+    t = [1000000000,1000000,40,38]
+    a = len(t)
+    if n< a : return t[n]
+    else :
+        return 40-n
+
+def g(b) :
+    t = [6,4,2,0,-2,-4,-6,-8,-10,-12.9,-15.8]
+    return t[10-b]
+
+def g2(b) :
+    t = [0.5,0,-0.5,-1,-1.5,-2,-3,-4,-5,-7.9,-10.8]
+    return t[10-b]
+
+joueurA = Humain("Humain")
+joueurB = IA_minmax_alpha_beta("AlphaBeta 3+", fct_eval, 3)
+Global.partie = Partie(joueurA, joueurB, True, Partie.DOSSIER_STANDARD)
+Global.partie.demarrer()

+ 19 - 0
project/Q_HumainVsAlphaBeta3.py

@@ -0,0 +1,19 @@
+from Outils.Moteur_de_jeu import *
+from Outils.Moteur_de_jeu.Partie import *
+from Outils import IA_alphabeta
+from Outils.IA_alphabeta import *
+
+
+def fct_eval(plateau, num) :
+    nIA = plateau.longueur_chemin(num)
+    n_opponent = plateau.longueur_chemin(1-num)
+    b_IA = plateau.barrieres_restantes[num]
+    return -nIA + n_opponent + 0.001 * b_IA
+
+
+
+
+joueurA = Humain("Humain")
+joueurB = IA_minmax_alpha_beta("AlphaBeta 3", fct_eval, 3)
+Global.partie = Partie(joueurA, joueurB, True, Partie.DOSSIER_STANDARD, True)
+Global.partie.demarrer()

+ 28 - 0
project/Q_HumainVsGenetic1.py

@@ -0,0 +1,28 @@
+from Outils.Moteur_de_jeu import *
+from Outils.Moteur_de_jeu.Partie import *
+
+from Outils import IA_MinMax
+from Outils.IA_MinMax import *
+
+from ReseauNeurones import *
+from ReseauNeurones.cerveau import *
+
+# Deboguage
+debug = True
+
+# Préparation
+myBrain = Cerveau()
+myBrain.charger_structure( "PrimitifEfficace.stc", debug = debug )
+myBrain.charger_poids( "Primitif/poidsTest.pds", debug = debug )
+myBrain.build_network( debug = debug )
+#myBrain.generer_poids( inf = -1.0, debug = debug )
+
+# Match
+joueurA = Humain("Humain")
+joueurB = IA_minmax("Genetic:Primitif", myBrain.fct_eval, 1 )
+
+Global.partie = Partie(joueurA, joueurB, True, "")
+
+Global.partie.demarrer()
+
+    

+ 33 - 0
project/Q_HumainVsGeneticSelect.py

@@ -0,0 +1,33 @@
+## Fichier : Q_HumainVsGeneticSelect.py
+# Auteur : Jovian Hersemeule
+
+# Permet de lancer un match
+# sur un réseau de neurones donné.
+
+from Outils.Moteur_de_jeu import *
+from Outils.Moteur_de_jeu.Partie import *
+
+from Outils import IA_MinMax
+from Outils.IA_MinMax import *
+
+from ReseauNeurones import *
+from ReseauNeurones.cerveau import *
+
+# Deboguage
+debug = True
+
+# Préparation
+myBrain = Cerveau()
+myBrain.charger_structure( "PrimitifEfficace.stc", debug = debug )
+myBrain.charger_poids( "PurAleatoireFast2/Generation50/individu0.pds", debug = debug )
+#myBrain.charger_poids( "PurAleatoirePrecis1/Generation81/individu0.pds", debug = debug )
+myBrain.build_network( debug = debug )
+#myBrain.generer_poids( inf = -1.0, debug = debug )
+
+# Match
+joueurA = IA_minmax("Genetic:Primitif", myBrain.fct_eval, 1 )
+joueurB = Humain("Humain")
+
+Global.partie = Partie(joueurA, joueurB, True, "")
+
+Global.partie.demarrer()

+ 19 - 0
project/Q_HumainVsMinMax1.py

@@ -0,0 +1,19 @@
+
+from Outils.Moteur_de_jeu import *
+from Outils.Moteur_de_jeu.Partie import *
+from Outils import IA_MinMax
+from Outils.IA_MinMax import *
+
+
+def fct_eval(plateau, num) :
+    return plateau.longueur_chemin(1 - num) - plateau.longueur_chemin(num)
+
+
+
+joueurA = Humain("Humain")
+joueurB = IA_minmax("MinMax 1", fct_eval, 1)
+Global.partie = Partie(joueurA, joueurB, True, Partie.DOSSIER_STANDARD)
+Global.partie.demarrer()
+
+    
+

+ 44 - 0
project/Q_HumainVsMinMax2+.py

@@ -0,0 +1,44 @@
+from Outils.Moteur_de_jeu import *
+from Outils.Moteur_de_jeu.Partie import *
+from Outils import IA_MinMax
+from Outils.IA_MinMax import *
+from test_heuristique import *
+
+def fct_eval(plateau, num) :
+    nIA = plateau.longueur_chemin(num)
+    n_opponent = plateau.longueur_chemin(1-num)
+    b_IA = plateau.barrieres_restantes[num]
+    b_opponent = plateau.barrieres_restantes[1-num]
+    c = 0
+    d = 0
+    """
+    if deux_chemins(plateau,num) : c = -4
+    if deux_chemins (plateau,1-num) : d = 3
+    """
+    return -nIA - f(n_opponent) +  3 * b_IA - g(b_opponent) 
+
+
+
+
+
+
+def f(n) :
+    t = [1000000000,1000000,50,46,42]
+    a = len(t)
+    if n< a : return t[n]
+    else :
+        return 46 -n
+
+
+
+def g(b) :
+    t = [1,0,-1,-2,-3,-4,-6,-8,-10,-12,-16]
+    return t[10-b]
+
+
+
+
+joueurA = Humain("Humain")
+joueurB = IA_minmax("MinMax2+", fct_eval, 2)
+Global.partie = Partie(joueurA, joueurB, True, Partie.DOSSIER_STANDARD)
+Global.partie.demarrer()

+ 16 - 0
project/Q_HumainVsMinMax2.py

@@ -0,0 +1,16 @@
+from Outils.Moteur_de_jeu import *
+from Outils.Moteur_de_jeu.Partie import *
+from Outils import IA_MinMax
+from Outils.IA_MinMax import *
+
+def fct_eval(plateau, num) :
+    return plateau.longueur_chemin(1 - num) - plateau.longueur_chemin(num)
+
+
+
+joueurA = Humain("Humain")
+joueurB = IA_minmax("MinMax 2", fct_eval, 2)
+Global.partie = Partie(joueurA, joueurB, True, Partie.DOSSIER_STANDARD)
+Global.partie.demarrer()
+
+    

+ 13 - 0
project/Q_HumainVsMinMax3+.py

@@ -0,0 +1,13 @@
+from Outils.Moteur_de_jeu import *
+from Outils.Moteur_de_jeu.Partie import *
+from Outils import IA_MinMax
+from Outils.IA_MinMax import *
+from Eval_v3 import fct_eval
+
+
+joueurA = IA_minmax("AlphaBeta 3+", fct_eval, 3)
+joueurB = Humain("Humain")
+Global.partie = Partie(joueurA, joueurB, True, Partie.DOSSIER_STANDARD)
+Global.partie.demarrer()
+
+    

+ 16 - 0
project/Q_HumainVsMinMax3.py

@@ -0,0 +1,16 @@
+from Outils.Moteur_de_jeu import *
+from Outils.Moteur_de_jeu.Partie import *
+from Outils import IA_MinMax
+from Outils.IA_MinMax import *
+
+def fct_eval(plateau, num) :
+    return plateau.longueur_chemin(1 - num) - plateau.longueur_chemin(num)
+
+
+
+joueurA = Humain("Humain")
+joueurB = IA_minmax("MinMax 3", fct_eval, 3)
+Global.partie = Partie(joueurA, joueurB, True, Partie.DOSSIER_STANDARD)
+Global.partie.demarrer()
+
+    

+ 7 - 0
project/Q_HumainVsRandomIA.py

@@ -0,0 +1,7 @@
+from Outils.Moteur_de_jeu import Partie
+from Outils.Moteur_de_jeu.Partie import *
+
+joueurA = Humain("Humain")
+joueurB = IA_random("I.A. aleatoire")
+Global.partie = Partie(joueurA, joueurB, True, Partie.DOSSIER_STANDARD, True)
+Global.partie.demarrer()

+ 12 - 0
project/Q_HumainVsSemiAleatoire.py

@@ -0,0 +1,12 @@
+from Outils.Outils.Moteur_de_jeu import *
+from Outils.Outils.Moteur_de_jeu.Partie import *
+from Outils import IA_semi_random
+from Outils.IA_semi_random import *
+
+
+joueurA = Humain("Humain")
+joueurB = IA_semi_random("Semi-aleatoire")
+Global.partie = Partie(joueurA, joueurB, True, Partie.DOSSIER_STANDARD)
+Global.partie.demarrer()
+
+    

+ 19 - 0
project/Q_HumainVsStats.py

@@ -0,0 +1,19 @@
+from Outils.Moteur_de_jeu import *
+from Outils.Moteur_de_jeu.Partie import *
+
+from Outils import IA_MinMax
+from Outils.IA_MinMax import *
+from Outils import IA_Stats
+from Outils.IA_Stats import *
+
+
+def fct_eval(plateau, num) :
+    return plateau.longueur_chemin(1 - num) - plateau.longueur_chemin(num)
+
+
+
+joueurA = Humain("Humain")
+joueurB = IA_stats("Stats", "stats.txt", IA_minmax("Adjoint",fct_eval,1))
+
+Global.partie = Partie(joueurA, joueurB, True, Partie.DOSSIER_STANDARD)
+Global.partie.demarrer()

+ 17 - 0
project/Q_MinMax_vs_MinMax.py

@@ -0,0 +1,17 @@
+from Outils.Moteur_de_jeu import *
+from Outils.Moteur_de_jeu.Partie import *
+from Outils import IA_MinMax
+from Outils.IA_MinMax import *
+
+
+def fct_eval(plateau, num) :
+    return plateau.longueur_chemin(1 - num) - plateau.longueur_chemin(num)
+
+
+
+joueurA = IA_minmax("MinMax 1", fct_eval, 1)
+joueurB = IA_minmax("MinMax 1", fct_eval, 1)
+Global.partie = Partie(joueurA, joueurB, True, "")
+Global.partie.demarrer()
+
+    

+ 44 - 0
project/Q_anti_raphael.py

@@ -0,0 +1,44 @@
+from Outils.Moteur_de_jeu import *
+from Outils.Moteur_de_jeu.Partie import *
+from Outils import IA_alphabeta
+from Outils.IA_alphabeta import *
+
+
+def fct_eval(plateau, num) :
+    nIA = plateau.longueur_chemin(num)
+    n_opponent = plateau.longueur_chemin(1-num)
+    b_IA = plateau.barrieres_restantes[num]
+    b_opponent = plateau.barrieres_restantes[1-num]
+    """
+    if deux_chemins(plateau,num) : c = -4
+    if deux_chemins (plateau,1-num) : d = 3
+    """
+    return f(nIA) - f(n_opponent) + g2(b_IA) - g2(b_opponent) 
+
+
+
+
+
+
+def f(n) :
+    t = [1000000000,1000000,40,38]
+    a = len(t)
+    if n< a : return t[n]
+    else :
+        return 40-n
+
+
+
+def g(b) :
+    t = [1,0,-1,-2,-3,-4,-6,-8,-10,-13,-16]
+    return t[10-b]
+
+def g2(b) :
+    t = [0.1,0,-0.1,-0.2,-0.3,-0.4,-0.5,-0.6,-3.5,-6,-9]
+    return t[10-b]
+
+
+joueurA = Humain("Humain")
+joueurB = IA_minmax_alpha_beta("AntiRaphael", fct_eval, 3)  # /!\ Pas d'accent à Raphael
+Global.partie = Partie(joueurA, joueurB, True, Partie.DOSSIER_STANDARD)
+Global.partie.demarrer()

+ 81 - 0
project/Rejouer les parties.py

@@ -0,0 +1,81 @@
+from math import log
+from Outils.Moteur_de_jeu import Plateau
+from Outils.Moteur_de_jeu.Plateau import *
+from Conversion import *
+import IA_alphabeta
+
+
+from Outils import Arbre
+from Outils.Arbre import *
+from Outils.Moteur_de_jeu import Partie
+from Outils.Moteur_de_jeu.Partie import *
+import math
+from random import shuffle
+import numpy
+
+from Outils import IA_MinMax
+from Outils.IA_MinMax import *
+
+
+
+"""def recommencer (nom) :
+    fichier = open (nom, "r")
+    l = fichier.readlines()
+    n = len(l)
+    coups = [0]*n
+    for i in range (n) :
+        li = l[i]                           #le i-ième ligne
+        coups[i] = coup_from_str (li)           #transforme en coup"""
+    
+
+class IA_rejouer(Joueur) :
+
+    def __init__(self, nom, fichier, curseur) :
+        Joueur.__init__(self,"O",nom)
+        self.curseur = curseur
+        self.liste_c = self.exploitation(fichier)
+        
+    def exploitation (self, fichier) :
+        f = open (fichier, "r")
+        l = f.readlines()
+        n = len(l)
+        coups = [0]*(n-2)
+        for i in range (3, n-2) :                        #IL FAUDRA ROGNER LES LIGNES POUR NE PRENDRE QUE LES COUPS
+            li = l[i]#la i-ième ligne
+            #print (li)
+            indexli = li.index(':')
+            liprete = li[(indexli + 2):]            #pour commencer à partir de la lettre
+            #print (liprete)
+            coups[i-3] = coup_from_str (liprete)
+        f.close()
+        return (coups)
+
+
+    def calculer_coup( self, plateau, liste_coup) :
+        coup_a_jouer = self.liste_c[self.curseur]
+        print(coup_a_jouer)
+        self.curseur = self.curseur + 1
+        liste = plateau.liste_coups_possibles()
+        for i in range(len(liste)) :
+            if liste[i] == coup_a_jouer :
+                return i              #renvoie l'indice du coup à faire dans liste
+
+
+fich = "BDD_Elric/MinMax 3+ VS MinMax 3 19-03-2017 22h31min45s.txt"
+joueur1 = IA_rejouer ("j1", fich, 0)
+#joueur2 = IA_rejouer ("j2", fich, 1)
+Global.partie = Partie (joueur1, joueur1, True)
+Global.partie.demarrer()
+
+    #liste = plateau.liste_coups_possibles()
+    #for i in range(len(liste)) :
+    #    if liste[i] == coup :
+    #        return i              #renvoie l'indice du coup à faire dans liste
+
+
+
+
+
+
+
+

BIN
project/ReseauNeurones/ArchitecturePartieGénétique.odg


BIN
project/ReseauNeurones/ArchitecturePartieGénétique2.odg


+ 6 - 0
project/ReseauNeurones/Base_Poids/Primitif/poidsTest.pds

@@ -0,0 +1,6 @@
+2.0
+0.5
+0.5
+2.0
+-1.0
+1.5

+ 6 - 0
project/ReseauNeurones/Base_Poids/Primitif/poidsTest2.pds

@@ -0,0 +1,6 @@
+2.0
+0.5
+0.5
+2.0
+-1.0
+1.5

+ 9 - 0
project/ReseauNeurones/Base_Structures/Primitif.stc

@@ -0,0 +1,9 @@
+g 1.0
+g 1.0
+>
+e1 i0
+e1 i1
+e3 i0
+e3 i1
+i0 s0
+i1 s0

+ 9 - 0
project/ReseauNeurones/Base_Structures/PrimitifEfficace.stc

@@ -0,0 +1,9 @@
+g 1.0
+g 1.0
+>
+e6 i0
+e6 i1
+e7 i0
+e7 i1
+i0 s0
+i1 s0

BIN
project/ReseauNeurones/Base_Structures/PrimitifSchema.odg


+ 20 - 0
project/ReseauNeurones/activations.py

@@ -0,0 +1,20 @@
+## Fichier : activations.py
+# Auteur : Jovian Hersemeule
+
+# Contient des fonctions d'activation pour les 
+# neurones intermédiaires.
+
+import numpy as np
+
+def sigmoid_std( x, l = 1.0 ):
+        """ écrit s dans les .stc """
+        return 1 / ( 1 + np.exp(-l*x))
+
+def sigmoid( x, l = 1.0 ):
+        """ écrit g dans les .stc """
+        return 2 * sigmoid_std( x, l = l ) - 1
+    
+def seuil( x ):
+	""" écrit d dans les .stc """
+	return sigmoid( x, l = 10.0 )
+    

+ 299 - 0
project/ReseauNeurones/cerveau.py

@@ -0,0 +1,299 @@
+## Fichier : cerveau.py
+# Auteur : Jovian Hersemeule
+
+# Un réseau de neurones possède des neurones d'entrée,
+# éventuellement des neurones intermédiaires, et des neurones
+# de sortie. Chaque entrée de neurone est constituée d'une combinaison
+# linéaire de sorties d'autres neurones.
+
+from random import uniform
+from ReseauNeurones.network import *
+from ReseauNeurones.activations import *
+from Outils.Moteur_de_jeu.Pathfinding import path_finding
+
+class Cerveau :
+    # Variables statiques
+    inSize = 144 # nombre total d'entrées
+    
+    def __init__( self ): # Jovian 17 janvier 2017
+        """
+        Créé un cerveau vide.
+        """
+        self.nom_structure = None # string
+        self.nom_poids = None # string
+        self.liste_entrees = [ 0.0 for k in range( Cerveau.inSize ) ] # Float list
+        self.liste_intermed = [] # Float list ( None dans une case si valeur non encore calculée )
+        self.liste_activ = [] # Function list
+        self.liste_synapses = [] # Couple string list
+        self.liste_poids = [] # Float list
+        self.network = Network() # Réseau
+
+    def calcule_noeud( self, index ): # Jovian 14 mars 2017
+        """
+	Prend un identifiant comme 'e1', 'i43' ou 's0' et retourne sa valeur.
+	Fonction récursive.
+	Argument id : string
+	Retour : float
+        """
+	# Analyse du noeud
+        node_type = index[0]
+        node_nb = int( index[1] )
+
+        # Cas de base
+        if node_type == 'e':
+            return self.liste_entrees[ node_nb ]
+
+        if node_type == 'i' and self.liste_intermed[ node_nb ] != None :
+            return self.liste_intermed[ node_nb ]
+
+        # Calcul récursif
+        parents = self.network.getParents( index )
+        rep = 0.0
+
+        w = 0.0
+        v = 0.0
+
+        for p in parents :
+            v = self.calcule_noeud( p )
+            w = self.network.getPoids( p, index )
+            rep += v * w
+
+        # Neurone intermédiaire : fonction d'activation et mémorisation
+        if node_type == 'i' :
+            rep = self.liste_activ[ node_nb ]( rep )
+            self.liste_intermed[ node_nb ] = rep
+
+        # Fin de la fonction
+        return rep
+    
+        
+    def fct_eval( self, plateau, num ): # Jovian 14 mars 2017
+        """
+        Fonction d'évaluation à passer au MinMax.
+        """
+        # Option de déboguage
+        debug = False
+        
+	# Actualise liste_entrees : attention à la relativité pos moi ou lui
+        mid_x = plateau.lg / 2
+        y_but_moi = plateau.rangee_desiree( num )
+        y_but_lui = plateau.rangee_desiree( 1 - num )
+	
+        self.liste_entrees[0] = ( plateau.pions[ num ][ 0 ] - mid_x ) / mid_x
+        self.liste_entrees[1] = abs( plateau.pions[ num ][ 1 ] - y_but_moi ) / plateau.ht
+
+        self.liste_entrees[2] = ( plateau.pions[ 1 - num ][ 0 ] - mid_x ) / mid_x
+        self.liste_entrees[3] = abs( plateau.pions[ 1 - num ][ 1 ] - y_but_lui ) / plateau.ht
+
+        self.liste_entrees[4] = plateau.barrieres_restantes[ num ] / 10
+        self.liste_entrees[5] = plateau.barrieres_restantes[ 1 - num ] / 10
+
+        self.liste_entrees[6] = len( path_finding( plateau, plateau.pions[num][0], plateau.pions[num][1], y_but_moi ) ) / 40
+        self.liste_entrees[7] = len( path_finding( plateau, plateau.pions[1 - num][0], plateau.pions[1 - num][1], y_but_lui ) ) / 40
+
+    	# TODO : Faire les booléens de barrière
+
+    	# Affichage des valeurs d'éntrées
+        if debug :
+            print("\nLancement de la fontion d'évaluation génétique.\n")
+            print("Identifiant du joueur : ", num )
+            print("Liste des entrées :")
+            for k in range(8) :
+                print("Entrée numéro ", k, " vaut : ", self.liste_entrees[k] )
+                
+    	# Reset intermédiaires
+        for k in range( len( self.liste_intermed ) ):
+            self.liste_intermed[k] = None
+		
+	# Calculer l'entrée : fonction récursive depuis, la sortie
+        return self.calcule_noeud( 's0' )                            
+
+    def charger_structure( self, nom_structure, nom_poids = None, debug = False ): # Jovian 7 février 2017
+        """
+        Créé ou recréé un réseau de neurones à partir d'un fichier structure dont le nom est passé en argument.
+        Si un fichier de poids n'est pas spécifié, tous les poids valent 0.
+        """
+        # Administratif
+        print("\n> Début de chargement de la structure.")
+        
+        if self.nom_structure == nom_structure :
+            print( "Structure <<", nom_structure, ">> déjà chargée." )
+            return None
+        elif self.nom_structure != None :
+            print( "Structure <<", self.nom_structure, ">> déjà chargée." )
+            print( "Structure <<", nom_structure, ">> non-chargée." )
+            return None
+        
+        self.nom_structure = nom_structure
+
+        # Ouverture du flux
+        chemin = "ReseauNeurones/Base_Structures/" + nom_structure
+        if chemin[-4] != "." :
+            chemin += ".stc"
+
+        flux = open( chemin )
+
+        # Lectures des neurones intermédiaires
+        self.liste_intermed = []
+        self.liste_activ = []
+        
+        buff = flux.readline()
+        
+        while buff[0] != ">" :
+            if debug : print("\nTraitement du neurone :", buff[0:-1])
+            
+            # Ajout d'un intermédiaire
+            self.liste_intermed.append( None )
+            
+            # Reconnaisance du type de fonction d'activation
+            if buff[0] == "g" :
+                arg = float( buff[2:] )
+                self.liste_activ.append( lambda x : sigmoid( x, l = arg ) )
+                if debug : print("Ajout d'une fonction sigmoïde d'argument", arg)
+            elif buff[0] == "d" :
+                self.liste_activ.append( seuil )
+                if debug : print("Ajout d'une fonction seuil.")
+            else :
+                print("Fonction d'activation ", buff, " inconnue." )
+
+            # Etape suivante
+            buff = flux.readline()
+
+        # Lectures des synapses
+        if debug : print("\nLecture des synapses : ")
+        node_start = "e0"
+        node_end = "s0"
+
+        buff = flux.readline()
+
+        while buff[0] == "e" or buff[0] == "i" :
+            if debug : print("Ajout du synapse : ", buff[0:-1])
+
+            # Ajout du synapse
+            node_start = buff[0:2]
+            node_end = buff[3:5]
+            self.liste_synapses.append( ( node_start, node_end ) )
+            
+            # Etape suivante
+            buff = flux.readline()
+            if len( buff ) == 0 :
+                break ;
+
+        # Charge les poids si argument utilisable
+        if nom_poids != None :
+            self.charger_poids( nom_poids )
+
+        # Debug : affichage des données
+        if debug :
+            print( "\nNeurones intermédiaires :")
+            print( len( self.liste_intermed ), " neurones intermédiaires. \n" )
+            print( "Synapses :" )
+            for syn in self.liste_synapses :
+                print( syn )
+            print("\n> Fin de chargement de la structure.")
+
+    def charger_poids( self, nom_poids, debug = False ): # Jovian 7 février 2017
+        """
+        Affecte des poids au réseau. Il faut que la structure soit compatible.
+        Un message d'erreur est affiché si ce n'est pas le cas.
+        """
+        # Administratif
+        if self.nom_structure == None :
+            print("Erreur : aucune structure n'est chargée !")
+            return None
+
+        self.nom_poids = nom_poids
+        
+        # Détruire les anciens poids
+        if debug : print( "\n> Chargement des poids pour ", self.nom_structure, ".")
+
+        if self.liste_poids != [] :
+            if debug : print("Destruction des anciens poids.")
+            self.liste_poids = []
+            
+        elif debug :
+            print("Poids inexistants : chargement pour la 1ere fois.")
+
+        # Charger
+        chemin = "ReseauNeurones/Base_Poids/" + nom_poids
+        if chemin[-4] != "." :
+            chemin += ".pds"
+
+        with open( chemin ) as flux :
+            for pds in flux :
+                self.liste_poids.append( float( pds ) )
+
+        # Debug : affichage des données
+        if debug :
+            print( "\nPoids des synapses :")
+            print( len( self.liste_poids ), " poids enregistrés. \n" )
+            print( "Poids :" )
+            for syn in self.liste_poids :
+                print( syn )
+            print("\n> Fin de chargement des poids.")
+        
+
+    def generer_poids( self, inf = -1.0, sup = 1.0, debug = False ): # Jovian 7 février 2017
+        """
+        Si une structure est chargée, génère des poids aléatoires.
+        """
+        # Détruire les anciens poids
+        if debug : print( "\n> Génération des poids pour ", self.nom_structure, ".")
+
+        if self.liste_poids != [] :
+            if debug : print("Destruction des anciens poids.")
+            self.liste_poids = []
+            
+        elif debug :
+            print("Poids inexistants : génération pour la 1ere fois.")
+
+        # Aléatoire
+        for k in range( len( self.liste_synapses ) ) :
+            self.liste_poids.append( uniform( inf, sup ) )
+
+        # Debug : affichage des données
+        if debug :
+            print( "\nPoids des synapses :")
+            print( len( self.liste_poids ), " poids enregistrés.\n" )
+            print( "Poids :" )
+            for syn in self.liste_poids :
+                print( syn )
+            print("\n> Fin de génération des poids.")
+
+    def donner_poids( self, liste_poids, debug = False ): # Jovian 21 mars 2017
+        """
+        Si une structure est chargée, assigne les poids donnés en argument.
+        """
+        # Vérification du format
+        if len(self.liste_synapses) != len(liste_poids) :
+            if debug : print( "\n>Taille de la nouvelle liste de poids incorrecte." )
+            return None
+        
+        # Détruire les anciens poids
+        if debug : print( "\n> Génération des poids pour ", self.nom_structure, ".")
+
+        if self.liste_poids != [] :
+            if debug : print("Destruction des anciens poids.")
+            self.liste_poids = []
+            
+        elif debug :
+            print("Poids inexistants : attribution manuelle.")
+
+        # Copie
+        self.liste_poids = list( liste_poids )
+
+        # Debug : affichage des données
+        if debug :
+            print( "\nPoids des synapses :")
+            print( len( self.liste_poids ), " poids enregistrés.\n" )
+            print( "Poids :" )
+            for syn in self.liste_poids :
+                print( syn )
+            print("\n> Fin d'attribution des poids.")
+        
+
+    def build_network( self, debug = False ): # Jovian 7 février 2017
+        """
+        Construit le réseau à partir des poids précedemment chargés.
+        """
+        self.network.build( self.liste_synapses, self.liste_poids, Cerveau.inSize, len( self.liste_intermed ), debug = debug )

+ 51 - 0
project/ReseauNeurones/entrepot.py

@@ -0,0 +1,51 @@
+# À inclure quelque part :
+#from IA_alphabeta import *
+#from (fichier de Baptiste) import *
+
+
+def sort(self): # Quentin 21 mars 2017
+    """ Classe la population via un système de tournoi. """
+    classement = self.tournoi([IA_minmax_alpha_beta("",cerveau.fct_eval,1) for cerveau in self.population])
+    
+    n = len(self.population)
+    nouveau = n*[None]
+    for i in range(n) :
+        nouveau[classement[i]] = self.population[i]
+
+    self.population = nouveau
+
+    
+    
+
+
+
+from os import system
+
+def creer_chemin(chemin) :
+    """ Crée les dossiers permettant l'existence du chemin passé en paramètre. Celui-ci doit être écrits avec des doubles antislashs comme délimiteurs. """
+    system("mkdir " + chemin)
+
+
+
+
+
+def etablir_classement(tab_scores) :
+    """ Etablit le classement à partir du score """
+    n = len(tab_scores)
+    tab = [(tab_scores[i],i) for i in range(n)]
+    tab.sort()
+
+    tab_classement = [0] * n        # Tableaux de rangs
+    for i in range(n) :
+        tab_classement[tab[i][1]] = n - 1 - i
+
+
+    return tab_classement
+
+
+
+
+print(etablir_classement([4,80,100,20]))
+
+    
+    

+ 62 - 0
project/ReseauNeurones/generation.py

@@ -0,0 +1,62 @@
+## Fichier : generation.py
+# Auteur : Jovian Hersemeule
+
+# Contient des fonctions qui construisent la génération suivante.
+
+from random import randint
+from random import uniform
+
+class Generateur : # Jovian 15 mars 2017
+	
+	def __init__(self): # Jovian 15 mars 2017
+		"""
+		Permet de créer un générateur avec les paramètres désirés.
+		"""
+	
+	def cross_random(self, chromA, chromB): # Jovian 15 mars 2017
+		"""
+		Retourne un chromosome dont chaque gène
+		est choisi aléatoirement chez A ou B.
+		"""
+		
+	def cross_mix(self, chromA, chromB): # Jovian 15 mars 2017
+		"""
+		Retourne un chromosome dont chaque gène
+		est la moyenne arithmétique des valeurs
+		des chromosomes chez A et B.
+		"""
+	
+	def mutate_total(self, chromA, nb = 1, inf = -1, sup = +1): # Jovian 31 mai 2017
+		"""
+		Remplace certains gènes du chromosome
+		par un poids aléatoire.
+		Arg nb : le nombre de gènes à muter.
+		"""
+		n = len(chromA)
+		
+		for k in range(nb):
+                        gene = randint(0, n-1)
+                        new_value = uniform(inf, sup)
+                        chromA[gene] = new_value
+                        
+		
+	def mutate_diff(self, chromA, diff = 0.1, nb = 1, inf = -1, sup = +1): # Jovian 31 mai 2017
+                """
+                Modifie un certain nombre de gène par l'ajout
+                d'une valeur aléatoire.
+                Arg diff : la différence maximale ajoutée.
+                Arg nb : le nombre de gènes à muter.
+                """
+                n = len(chromA)
+
+                for k in range(nb):
+                        gene = randint(0, n-1)
+                        new_diff = uniform(-diff, diff)
+                        
+                        chromA[gene] += new_diff
+
+                        if chromA[gene] >= sup :
+                                chromA[gene] = sup
+                        elif chromA[gene] <= inf :
+                                chromA[gene] = inf
+		

+ 303 - 0
project/ReseauNeurones/incubateur.py

@@ -0,0 +1,303 @@
+## Fichier : incubateur.py
+# Auteur : Jovian Hersemeule
+
+# Classe python. Se charge d'entrainer un réseau de
+# neurones par algorithme génétique.
+
+from ReseauNeurones.cerveau import Cerveau
+from ReseauNeurones.generation import Generateur
+from Outils.IA_alphabeta import IA_minmax_alpha_beta
+
+from random import randint
+from os import system
+
+class Incubateur :
+
+        def __init__(self): # Jovian 21 mars 2017
+                """
+		Construit l'incubateur.
+		"""
+                self.population = None # Cerveau list
+                self.iter = 0 # int
+                self.nom_structure = None # int
+                self.nom_session = None # string
+                self.tournoi = None # fonction de tournoi
+                self.nom_tournoi = "Pas de tournoi" # nom du tournoi
+                self.save_rate = None # nombre de générations entre chaque sauvegarde
+                                        #(None pas de sauvegarde avant la fin)
+
+                self.nb_elite_kept = 1
+                self.nb_elite_cross = 1
+                self.nb_mixte_cross = 1
+                self.nb_elite_mutate = 1
+                self.nb_weak_mutate = 1
+                self.nb_rd_generated = 1
+
+                self.generator = Generateur()
+
+        def start_session(self, nom_structure, nom_session = "BasicTraining", nb_gen_max = None, nb_pop = 20, debug = False ): # Jovian 21 mars 2017
+                """
+                Permet de démarrer une session d'entraînement.
+                Attention cela est une fonction blocante.
+                Arg nom_structure : le nom de la structure du réseau.
+                Arg nom_session : permet de choisir le nom du dossier pour cette session.
+                Arg nb_gen_max : le nombre maximal de générations avant l'arrêt. Si None pas de limite.
+                Arg nb_pop : nombre d'individus par génération.
+                Arg debug : affiche des informations si vaut True.
+                """
+                # Initialisation des attributs
+                self.nom_structure = nom_structure
+                self.nom_session = nom_session
+
+                # Création des population
+                self.population = []
+                self.iter = 1
+                
+                for i in range(nb_pop):
+                        individual = Cerveau()
+                        individual.charger_structure( nom_structure, debug = debug )
+                        individual.generer_poids( inf = -1.0, sup = 1.0, debug = debug )
+                        individual.build_network( debug = debug )
+                        
+                        self.population.append( individual )
+
+                # Sauvegarde des individus originaux
+                self.save_population(debug = debug)
+
+                # Ecriture du protocole
+                self.write_protocole(debug = debug)
+
+                # Entrainement
+                self.train( nb_gen_max = nb_gen_max, debug = debug )
+
+                # Sauvegarde de la population
+                self.save_population(debug = debug)
+	
+        def save_population(self, debug = False): # Jovian 30 mai 2017
+                """
+                Sauvegarde la totalité de la population.
+                """
+		# Dossier
+                chemin = "ReseauNeurones\\Base_Poids\\" + self.nom_session + "\\"
+                chemin += "Generation" + str(self.iter)
+
+                system("mkdir " + chemin)
+
+                # Fichier
+                chemin += "\\individu"
+                chemin = chemin.replace("\\", "/")
+
+		# Parcours
+                for tp in enumerate(self.population) :
+                    k, individual = tp
+                    with open( chemin + str(k) + ".pds", "w" ) as flux :
+                        for v in individual.liste_poids :
+                            flux.write( str(v) + "\n" )
+
+                # Annonce
+                if debug :
+                        print("|")
+                        print("Population sauvegardée.")
+                        
+		
+        def sort(self, debug = False): # Jovian 6 juin 2017
+                """
+                Classe la population par un système de tournoi.
+                """
+                if debug :
+                        print("|\nClassement des individus.")
+                        
+                # Création des intelligences à patir des cerveaux
+                liste_IA = [ IA_minmax_alpha_beta(self.nom_session, brain.fct_eval, 1) for brain in self.population ]
+                #liste_IA = [ IA_minmax_alpha_beta(self.nom_session, lambda x,y : 0, 1) for brain in self.population ]
+
+                # Classement par tournoi
+                tab_scores = self.tournoi( liste_IA )
+
+                if debug :
+                        print("Tableau des scores : ", tab_scores )                        
+
+
+                # Etablissement d'un classement à partir des scores :
+                n = len(self.population)
+                tab = [(tab_scores[i],i) for i in range(n)]
+                tab.sort()
+
+                tab_classement = [0] * n        # Tableaux de rangs
+                for i in range(n) :
+                        tab_classement[tab[i][1]] = n - 1 - i
+
+                if debug :
+                        print("Rang des joueurs : ", tab_classement ) 
+
+
+                # Ordonnancement de la population en conséquence :
+                nouveau = n*[None]
+                for i in range(n) :
+                        nouveau[tab_classement[i]] = self.population[i]
+
+                self.population = nouveau
+                
+		
+        def generate(self, debug = False): # Jovian 2 juin 2017
+                """
+                Génère la génération suivante d'individus.
+                """
+                # Annonce
+                if debug :
+                        print("|\nGénération en cours de création : ", self.iter)
+
+                # Liste des nouvaux chromosomes
+                chrom_list = []
+                
+                # Séparation faibles / forts
+                mid = len(self.population) // 2 + 1
+
+                # Garder les meilleurs
+                if debug and self.nb_elite_kept > 0 :
+                        print("On garde les ", self.nb_elite_kept, " meilleurs.")
+                        
+                for k in range( self.nb_elite_kept ):
+                    chrom_list.append( self.population[k].liste_poids )
+
+                # Croiser les meilleurs
+                if debug and self.nb_elite_cross > 0 :
+                        print("On croise les ", self.nb_elite_cross, " meilleurs.")
+                        
+                for k in range( self.nb_elite_cross ):
+                    # Choisir deux individus à reproduire
+                    indA = randint(0, mid)
+                    indB = randint(0, mid)
+
+                    # Opérations génétiques
+                    chromA = self.population[indA].liste_poids
+                    chromB = self.population[indB].liste_poids
+
+                    chromCross = self.generator.cross_random( chromA, chromB )
+
+                    # Ecriture du résultat
+                    chrom_list.append( chromCross )
+
+                # Croiser des individus aléatoirement
+                if debug and self.nb_mixte_cross > 0 :
+                        print("On croise aléatoirement ", self.nb_mixte_cross, " individus.")
+                        
+                for k in range( self.nb_mixte_cross ):
+                    # Choisir deux individus à reproduire
+                    indA = randint(0, len( self.population ) )
+                    indB = randint(0, len( self.population ) )
+
+                    # Opérations génétiques
+                    chromA = self.population[indA].liste_poids
+                    chromB = self.population[indB].liste_poids
+
+                    chromCross = self.generator.cross_random( chromA, chromB )
+
+                    # Ecriture du résultat
+                    chrom_list.append( chromCross )
+
+                # Mutation des élites
+                if debug and self.nb_elite_mutate > 0 :
+                        print("On mute les ", self.nb_elite_mutate, " meilleurs.")
+                        
+                for k in range( self.nb_elite_mutate ):
+                    # Choisir
+                    indA = randint(0, mid)
+
+                    # Opérations génétique
+                    chromA = self.population[indA].liste_poids
+
+                    chromMuted = self.generator.mutate_total( chromA, nb = 1 )
+
+                    # Ecriture du résultat
+                    chrom_list.append( chromMuted )
+
+                # Mutation des faibles
+                if debug and self.nb_weak_mutate > 0 :
+                        print("On mute les ", self.nb_weak_mutate, " plus faibles.")
+                        
+                for k in range( self.nb_weak_mutate ):
+                    # Choisir
+                    indA = randint(mid, len( self.population ) )
+
+                    # Opérations génétique
+                    chromA = self.population[indA].liste_poids
+
+                    chromMuted = self.generator.mutate_total( chromA, nb = 1 )
+
+                    # Ecriture du résultat
+                    chrom_list.append( chromMuted )
+                
+
+                # Mise à jour des réseaux
+                for k in range( self.nb_elite_kept, len(chrom_list) ) :
+                    self.population[k].donner_poids( chrom_list[k] )
+                    self.population[k].build_network()
+
+                # On complète la population en générant des membres aléatoirement
+                if debug and len(self.population) - len(chrom_list) > 0 :
+                        print("On rajoute ", len(self.population) - len(chrom_list), " générés au hasard.")
+                
+                for k in range( len(chrom_list), len(self.population) ) :
+                    self.population[k].generer_poids()
+                    self.population[k].build_network()
+                        
+		
+        def train(self, nb_gen_max = None, debug = False): # Jovian 31 mai 2017
+                """
+                Entraîne le réseau.
+                int nb_gen_max : Le nombre de générations avant l'arrêt (None pour boucle infinie)
+                bool debug : Affichage d'informations de suivi
+                """
+                last_save = self.iter
+                if debug :
+                        print("Démarrage de l'entraînement à partir de la génération ", self.iter )
+                
+                # Itération
+                while self.iter < nb_gen_max :
+                        self.iter += 1
+                        print("\n> Génération ", self.iter, " pour la session ", self.nom_session,".")
+
+                        # Classement
+                        self.sort(debug = debug)
+
+                        # Génération suivante
+                        self.generate(debug = debug)
+
+                        # Sauvegarde
+                        if self.save_rate != None and self.iter - last_save >= self.save_rate  :
+                                last_save = self.iter
+                                self.save_population(debug = debug)
+
+                # Fin de l'entraînement
+                return None
+
+        def write_protocole(self, debug = False): # Jovian 6 juin 2017
+                """
+                Sauvegarde les paramètres utilisés dans le dossier
+                qui contient les résultats de l'entraînement.
+                bool debug : Affichage d'informations de suivi
+                """
+                # Chemin
+                chemin = "ReseauNeurones/Base_Poids/" + self.nom_session
+                chemin += "/Protocole.txt"
+
+		# Ecriture
+                with open( chemin, "w" ) as flux :
+                        flux.write( "Nom du tournoi : " + str(self.nom_tournoi) + "\n" )
+                        flux.write( "Nombre de générations maximal : " + "inconnu" + "\n" )
+                        flux.write( "Taille de population : " + str(len(self.population)) + "\n" )
+                        flux.write( "\n\n" )
+                        flux.write( "nb_elite_kept = " + str(self.nb_elite_kept) + "\n" )
+                        flux.write( "nb_elite_cross = " + str(self.nb_elite_cross) + "\n" )
+                        flux.write( "nb_elite_kept = " + str(self.nb_mixte_cross) + "\n" )
+                        flux.write( "nb_elite_mutate = " + str(self.nb_elite_mutate) + "\n" )
+                        flux.write( "nb_weak_mutate = " + str(self.nb_weak_mutate) + "\n" )
+                        flux.write( "nb_rd_generated = " + str(self.nb_rd_generated) + "\n" )
+                        
+
+                # Annonce
+                if debug :
+                        print("|")
+                        print("Protocole enregistré.")
+                

+ 159 - 0
project/ReseauNeurones/network.py

@@ -0,0 +1,159 @@
+## Fichier : network.py
+# Auteur : Jovian Hersemeule
+
+# Gère l'arbre des neurones pour le cerveau.
+
+class Network :
+    def __init__( self ): # Jovian 17 janvier 2017
+        """
+        Créé un réseau vide. Le graphe est implémenté par liste d'adjacence.
+        La sortie est indexée 0.
+        Les entrées sont indexées de 1 à inSize.
+        Les intermédiaires sont indexés de inSize + 1 à total de neurones.
+        """
+        self.tree = None # string list list
+        self.poids = None # matrice de poids[de][vers]
+        self.total = 1 # int
+
+        self.nb_in = 0
+        self.nb_aux = 0
+        self.nb_out = 1
+
+    def in_node_to_num( self, node ): # Jovian 7 mars 2017
+        """
+        Convertit un identidiant de noeud ( par exemple s0 ou i42 ) en indice de tableau.
+        Les entrées occupent [0 : nb_in ]
+        Les intermédiaires occupent [ nb_in : nb_in + nb_aux ]
+        """
+        # Décryptage du noeud
+        sort = node[0]
+        num = int( node[1:] )
+
+        # Détection d'une requête sans sens
+        if sort == 's' :
+            print( "Appel de in_node_to_num impertinent sur un neurone de sortie." )
+            print( "Exécuté sur ", node )
+            return -1
+
+        # Décalage dû aux types
+        if sort == "i" :
+            num += self.nb_in
+
+        # Fin
+        return num
+
+    def out_node_to_num( self, node ): # Jovian 7 mars 2017
+        """
+        Convertit un identidiant de noeud ( par exemple s0 ou i42 ) en indice de tableau.
+        Les intermédiaires occupent [0 : nb_aux ]
+        Les sorties occupent [ nb_aux : nb_aux + nb_out ]
+        """
+        # Décryptage du noeud
+        sort = node[0]
+        num = int( node[1:] )
+
+        # Détection d'une requête sans sens
+        if sort == 'e' :
+            print( "Appel de out_node_to_num impertinent sur un neurone d'entrée." )
+            print( "Exécuté sur ", node )
+            return -1
+
+        # Décalage dû aux types
+        elif sort == "s" :
+            num += self.nb_aux
+
+        # Fin
+        return num
+
+    def build( self, liste_synapses, liste_poids, nb_in, nb_aux, nb_out = 1, debug = False ): # Jovian 7 mars 2017
+        """
+        Construit le réseau à partir d'une liste de synapses.
+        Argument liste_synapses : Couple string list, liste de synapses.
+        Argument liste_poids : liste des poids respectifs des synapses.
+        Argument nb_in : entier nombre d'entrées.
+        Argument nb_int : entier nombre d'intermédiaires.
+        Argument nb_out : entier nombre de sortie(s).
+        """
+        # Annonce
+        if debug : print("\n> Début de la procédure de construction du network.")
+        
+        # Ecrasement possible
+        if self.total > 1 :
+            if debug : print( "\n> Network déjà existant : écrasement." )
+
+        self.tree = None
+        self.poids = None
+        self.total = 1
+
+        self.nb_in = nb_in
+        self.nb_aux = nb_aux
+        self.nb_out = nb_out
+
+        # Initialisation des attributs
+        self.tree = [[] for k in range( nb_out + nb_aux ) ]
+        self.poids = [ [ 0 for j in range( nb_aux + nb_out ) ] for i in range( nb_in + nb_aux ) ]
+
+        # Balayage
+        nb_synapses = len( liste_synapses )
+        
+        for k in range( nb_synapses ):
+            # Déconstruction du synapse
+            node_start = liste_synapses[k][0]
+            node_end = liste_synapses[k][1]
+
+            if debug :
+                print( "> ", liste_synapses[k] )
+                print("Traitement synapse de ", node_start, " à ", node_end )
+            
+            # Ajout dans l'arbre
+            id_end = self.out_node_to_num( node_end )
+            self.tree[id_end].append( node_start )
+
+            # Ajout dans la matrice
+            id_start = self.in_node_to_num( node_start )
+            self.poids[ id_start ][ id_end ] = liste_poids[k]
+
+        # Fin
+        if debug :
+            print( len( self.poids ) * len( self.poids[0] ), " cases dans la matrice poids." )
+            print( nb_synapses, " poids enregistrés dans la matrice." )
+            #print( "Matrice de poids :" )
+            #for syn in self.poids :
+                #print( syn )
+            print( "\nListe des parents :" )
+            for c in enumerate(self.tree) :
+                # Tuple
+                nb, parents = c
+
+                # Annonce de la classe
+                if nb == 0 :
+                    print("| Les parents des intermédiaires")
+                elif nb == nb_aux :
+                    print("\n| Les parents des sorties")
+
+                # Affichage parents
+                print( parents )
+            print("\n> Fin de la procédure de construction du network.\n")
+
+    def getParents( self, index ): # Jovian 7 mars 2017
+        """
+        Retourne liste identifiants des parents.
+        Argument index : noeud dont on veut connaître les parents.
+        """
+        identifiant = self.out_node_to_num( index )
+
+        if identifiant == -1 :
+            return []
+        else :        
+            return self.tree[ identifiant ]
+
+    def getPoids( self, nodeA, nodeB ): # Jovian 7 mars 2017
+        """
+        Retourne le poids du noeud A au noeud B.
+        Argument nodeA : noeud A.
+        Argument nodeB : noeud B.
+        """
+        identifiantA = self.in_node_to_num( nodeA )
+        identifiantB = self.out_node_to_num( nodeB )
+
+        return self.poids[ identifiantA ][ identifiantB ]

+ 94 - 0
project/Resultats_stats.py

@@ -0,0 +1,94 @@
+from Outils.Moteur_de_jeu import *
+from Outils.Moteur_de_jeu.Partie import *
+
+from Outils import IA_alphabeta
+from Outils.IA_alphabeta import *
+from Outils import IA_Stats
+from Outils.IA_Stats import *
+
+
+
+def fct_eval(plateau, num) :
+    return plateau.longueur_chemin(1 - num) - plateau.longueur_chemin(num)
+
+
+
+
+def lire_fichier(nom) :
+    fichier = open(nom,"r")
+    tab_reussites = []
+    tab_total = []
+
+    l = fichier.readline()
+    i = l.find("/")
+    parties_gagnees = int(l[:i])
+    parties_total = int(l[i+1:])
+
+    l = fichier.readline()
+    while "/" in l :
+        i = l.find("/")
+        tab_reussites.append(int(l[:i]))
+        tab_total.append(int(l[i+1:]))
+        
+        l = fichier.readline()
+
+
+
+    fichier.close()
+    return (tab_reussites, tab_total, parties_gagnees, parties_total)
+    
+
+
+def ecrire_fichier(nom, tab_reussites, tab_total, parties_gagnees, parties_total) :
+    fichier = open(nom, "w")
+    fichier.write(str(parties_gagnees) + "/" + str(parties_total) + "\n")
+
+    for i in range(len(tab_reussites)) :
+        fichier.write(str(tab_reussites[i]) + "/" + str(tab_total[i])  + "\n")
+
+
+    fichier.close()
+
+
+
+
+def confrontation(profondeur) :
+    joueurA = IA_minmax_alpha_beta("Adjoint",fct_eval,profondeur)
+    joueurB = IA_stats("Stats", "stats.txt", IA_minmax_alpha_beta("Adjoint",fct_eval,profondeur))
+    nom_fichier = "Divers/Donnees_stats/" + "resultats_profondeur_" + str(profondeur) + ".txt"
+
+    
+    try :
+        (tab_reussites, tab_total, nombre_victoires, nombre_parties) = lire_fichier(nom_fichier)
+    except :
+        (tab_reussites, tab_total, nombre_victoires, nombre_parties) = ([],[],0,0)
+        
+
+    i = 0
+    while True :
+        joueurB.tab_reussites = []
+        Global.partie = Partie(joueurA, joueurB, False, "")
+        gagnant = Global.partie.demarrer()
+
+        nombre_parties += 1
+        i += 1
+        if gagnant.nom == "Stats" :
+            nombre_victoires += 1
+
+            
+        tab = joueurB.tab_reussites
+        for n in range(len(tab)) :
+            try :
+                tab_total[n] += 1
+            except :
+                tab_total.append(1)
+                tab_reussites.append(0)
+                
+            if tab[n] :
+                tab_reussites[n] += 1
+
+
+        print(str(i) + " partie terminée")
+
+        ecrire_fichier(nom_fichier, tab_reussites, tab_total, nombre_victoires, nombre_parties)
+                

+ 174 - 0
project/TraitementParties.py

@@ -0,0 +1,174 @@
+from Outils.Moteur_de_jeu import Conversion
+from Outils.Moteur_de_jeu.Conversion import *
+from os import (popen,system)
+
+
+"""
+    Attention, ce code est conçu pour des plateaux standards : 9 cases sur 9 avec 10 barrières par joueurs
+"""
+
+
+
+def inserer_coup(stats, code_p, code_c) :
+    """
+        Mets à jour les stats de la partie code_p pour le coup code_c
+    """
+    # Création du tableau des codes de parties à partir du texte brut, puis recherche :
+    tab_p = [l[:l.find(" ")] for l in stats]
+    (trouve, id_p) = recherche(tab_p, code_p)
+
+    if not trouve :
+        insertion_place(stats," ", id_p)
+
+
+    # Création du tableau de codes de parties à partir de la ligne brute :
+    l  =  stats[id_p]
+    tab_c_complet = l[l.find(" ") + 1:].split(",")
+    if tab_c_complet == [''] :
+        tab_c_complet = []
+
+    # Création du tableau de nombres d'occurences :
+    tab_c = []
+    tab_n = []
+    for c in tab_c_complet :
+        tab_c.append(c[:c.find(":")])
+        tab_n.append(int(c[c.find(":") + 1:]))
+
+
+    # Recherche du coup et insertion/incrémentation :
+    (trouve, id_c) = recherche(tab_c, code_c)
+
+    if not trouve :
+        insertion_place(tab_c_complet, code_c + ":1", id_c)
+    else :
+        tab_c_complet[id_c] = code_c + ":" + str(tab_n[id_c] + 1)
+
+
+    # Recomposition :
+    l = code_p + " "
+    for coup in tab_c_complet :
+        l += coup + ","
+    l = l[:-1]
+
+    stats[id_p] = l
+    
+
+
+
+
+
+def analyser_fichier(origine, destination, nom, stats) :
+    # Déplacement du fichier avant traitement :
+    original = open(origine + "/" + nom, "r")
+    contenu = original.readlines()
+    original.close()
+
+    copie = open(destination + "/" + nom, "w")
+    for l in contenu :
+        copie.write(l)
+    copie.close()
+    ###
+
+    
+    
+    gagnant = int(contenu[-1][-1]) - 1
+    types_joueur = ["",""]
+
+
+
+    # Recherche du début de la partie :
+    l = 0
+    while contenu[l][:3] != "Cou" :
+        if contenu[l][0] == "J" :
+            types_joueur[int(contenu[l][7]) - 1] = contenu[l][-2]
+        l += 1
+
+
+    # On ne prend en compte que les coups joués par des humains :
+    if types_joueur[gagnant] == "O" :
+        return
+
+
+
+    plateau = Plateau(9,9,10)
+
+    # Analyse de chaque coup :
+    while contenu[l][:3] == "Cou" :
+        s = contenu[l]
+        
+        #Césure de la ligne :
+        str_c = s[s.find(":") + 2:]
+        coup = coup_from_str(str_c)
+
+
+        #Si le coup a été joué par le gagnant, on l'insère dans stats :
+        if plateau.tour == gagnant :
+            code_p = code_from_plateau(plateau)
+            code_c = code_from_coup(coup)
+            
+            inserer_coup(stats, code_p, code_c)
+            
+        plateau.executer_coup(coup)
+        l += 1
+
+        
+        
+
+        
+    
+
+def traiter_parties(origine, destination, nom_fichier_stats) :
+    liste_noms_brute = popen("dir " + origine + " /b").read().split("\n")[:-1]
+    liste_noms = [nom for nom in liste_noms_brute if nom[-3:] == "txt"]
+
+    fichier = open(nom_fichier_stats, "r")
+    stats = []
+    for l in fichier :
+        if l != [] :
+            stats.append(l[:-1])
+    fichier.close()
+
+    for nom in liste_noms :
+        analyser_fichier(origine, destination, nom, stats)
+
+
+    fichier = open(nom_fichier_stats, "w")
+    for l in stats :
+        fichier.write(l + "\n")
+    fichier.close()
+
+    system("del /Q " + origine)
+    
+    
+
+
+
+
+
+def test() :
+    fichier = open("stats.txt", "r")
+    stats = fichier.readlines()
+    fichier.close()
+
+    stats = inserer_coup([], "bill", "ah_ah")
+    stats = inserer_coup(stats, "bill", "oh_oh")
+    stats = inserer_coup(stats, "bill", "ah")
+    stats = inserer_coup(stats, "bill", "zh_zh")
+    stats = inserer_coup(stats, "bill", "oh_oh")
+
+    stats = inserer_coup(stats, "zzz", "oh_oh")
+    stats = inserer_coup(stats, "zzz", "oh_oh")
+    stats = inserer_coup(stats, "gauss", "bah")
+    stats = inserer_coup(stats, "d", "oh_oh")
+    stats = inserer_coup(stats, "zzzz", "oh_oh")
+
+
+    fichier = open("stats.txt", "w")
+    for l in stats :
+        fichier.write(l + "\n")
+    fichier.close()
+
+
+
+#traiter_parties("PARTIES_A_TRAITER", "PARTIES_TRAITEES", "stats.txt")
+traiter_parties("TEST_PARTIES_A_TRAITER", "TEST_PARTIES_TRAITEES", "test_stats.txt")

+ 82 - 0
project/etude_resultats_elric.py

@@ -0,0 +1,82 @@
+from os import (popen,system)
+
+
+
+def traiter_donnees(nom_joueur_1, nom_joueur_2, nom_gagnant, data) :
+    a = nom_joueur_1
+    b = nom_joueur_2
+    c = nom_gagnant
+    for l in data :
+        if l[0] == (a,b) and c == a :
+            l[1][0] += 1
+        if l[0] == (b,a) and c == a :
+
+
+            l[1][1] += 1
+        if l[0] == (a,b) and c == b :
+
+
+            l[1][1] += 1
+        if l[0] == (b,a) and c == b :
+            l[1][0] += 1
+
+        
+            
+    
+
+
+
+
+def analyser_fichier(dossier, nom, data) :
+    fichier = open(dossier + "/" + nom, "r")
+    contenu = fichier.readlines()
+    fichier.close()
+
+    noms_joueur = ["",""]
+
+    # Recherche du début de la partie :
+    l = 0
+    while contenu[l][:3] != "Cou" :
+        if contenu[l][0] == "J" :
+            noms_joueur[int(contenu[l][7]) - 1] = contenu[l][11:-4]
+        l += 1
+
+        
+    traiter_donnees(noms_joueur[0], noms_joueur[1], noms_joueur[int(contenu[-1][-1]) - 1], data)
+
+
+
+
+
+
+def analyser_parties(dossier, data) :
+    liste_noms_brute = popen("dir " + dossier + " /b").read().split("\n")[:-1]
+    liste_noms = [nom for nom in liste_noms_brute if nom[-3:] == "txt"]
+
+    texte = " milliers de parties traitées sur " + str(len(liste_noms))
+    compteur = 0
+
+    for nom in liste_noms :
+        analyser_fichier(dossier, nom, data)
+        compteur += 1
+
+        if compteur % 1000 == 0 :
+            print(str(compteur//1000) + texte)
+
+
+t = ["MinMax 1","MinMax 2","MinMax 3","MinMax 1+","MinMax 2+","MinMax 3+","MinMax 1++","MinMax 2++","MinMax 3++","AntiGael"]
+def tableau(t) :
+    res = []
+    for i in range (10) :
+        for j in range(i+1,10) :
+            res.append([(t[i],t[j]),[0,0]])
+    return res
+
+data = tableau(t)
+
+analyser_parties("BDD_Elric1", data)
+"""
+# Revoir les stat n°2
+"""
+    
+    

+ 218 - 0
project/fct_eval_elric.py

@@ -0,0 +1,218 @@
+from Outils.Moteur_de_jeu import *
+from Outils.Moteur_de_jeu.Partie import *
+from Outils import alphabeta
+from Outils.alphabeta import *
+
+from Outils.Moteur_de_jeu import *
+from Outils.Moteur_de_jeu.Partie import *
+from Outils import IA_alphabeta
+from Outils.IA_alphabeta import *
+
+
+def fct_eval_basique(plateau, num) :
+            return plateau.longueur_chemin(1 - num) - plateau.longueur_chemin(num)
+
+def fct_eval_heuristique_1 (plateau,num) :
+    nIA = plateau.longueur_chemin(num)
+    n_opponent = plateau.longueur_chemin(1-num)
+    b_IA = plateau.barrieres_restantes[num]
+    return -nIA + n_opponent + 0.001 * b_IA
+
+
+def fct_eval_heuristique_2 (plateau,num):
+    def f(n) :
+        t = [1000000000,1000000,40,38]
+        a = len(t)
+        if n< a : return t[n]
+        else :
+            return 40-n
+
+
+
+    def g(b) :
+        t = [6,4,2,0,-2,-4,-6,-8,-10,-12.9,-15.8]
+        return t[10-b]
+    def g2(b) :
+        t = [0.5,0,-0.5,-1,-1.5,-2,-3,-4,-5,-7.9,-10.8]
+        return t[10-b]
+
+    nIA = plateau.longueur_chemin(num)
+    n_opponent = plateau.longueur_chemin(1-num)
+    b_IA = plateau.barrieres_restantes[num]
+    b_opponent = plateau.barrieres_restantes[1-num]
+	
+    if IA_minmax_alpha_beta.dist == None or IA_minmax_alpha_beta.dist > 5 :
+        return f(nIA) - f(n_opponent) + g(b_IA) - g(b_opponent) 
+        
+    return f(nIA) - f(n_opponent) + g2(b_IA) - g2(b_opponent) 
+
+
+
+
+
+
+
+
+
+def panzer (plateau,num) :
+    if  plateau.rangee_desiree(num) == 8 :
+
+        b1 = Barriere ("h", 3, 5)
+        b2 = Barriere ("h", 6, 5)   
+
+        tr = [b1,b2]
+        bar = plateau.liste_barrieres_possibles()
+        for k in tr :
+            if k in plateau.liste_barrieres :
+                suppr (tr,k)
+        for k in bar :
+            if k == b1 :
+                return Coup("B", barriere = b1)
+            elif k == b2 :
+                        return Coup("B", barriere = b2)
+       
+        if plateau.joueur_sur_case( 1 - num, 5, 6) :
+            b1 = Barriere ("v", 5, 4)
+            b2 = Barriere ("v", 3, 4)
+            b3 = Barriere ("v", 4, 4)
+            b4 = Barriere ("v", 6, 4)
+            t = [b1,b2,b3,b4]
+            for k in t :
+                if k in plateau.liste_barrieres :
+                    suppr (t,k)
+            if len(t)==4 :
+                for k in bar :
+                    if k == b1 :
+                        return Coup("B", barriere = b1)
+                for k in bar :
+                    if k == b2 :
+                        return Coup("B", barriere = b2)
+                for k in bar :
+                    if k == b3 :
+                        return Coup("B", barriere = b3)
+                for k in bar :
+                    if k == b4 :
+                        return Coup("B", barriere = b4)
+    else :
+        b1 = Barriere ("h", 3, 2)
+        b2 = Barriere ("h", 6, 2)   
+
+        tr = [b1,b2]
+        bar = plateau.liste_barrieres_possibles()
+        for k in tr :
+            if k in plateau.liste_barrieres :
+                suppr (tr,k)
+        for k in bar :
+            if k == b1 :
+                return Coup("B", barriere = b1)
+            elif k == b2 :
+                        return Coup("B", barriere = b2)
+       
+        if plateau.joueur_sur_case( 1 - num, 5, 2) :
+            b1 = Barriere ("v", 5, 3)
+            b2 = Barriere ("v", 3, 3)
+            b3 = Barriere ("v", 4, 3)
+            b4 = Barriere ("v", 6, 3)
+            t = [b1,b2,b3,b4]
+            for k in t :
+                if k in plateau.liste_barrieres :
+                    suppr (t,k)
+            if len(t)==4 :
+                for k in bar :
+                    if k == b1 :
+                        return Coup("B", barriere = b1)
+                for k in bar :
+                    if k == b2 :
+                        return Coup("B", barriere = b2)
+                for k in bar :
+                    if k == b3 :
+                        return Coup("B", barriere = b3)
+                for k in bar :
+                    if k == b4 :
+                        return Coup("B", barriere = b4)
+            
+            
+        
+
+    return (Coup("B", barriere = Barriere ("v", 42, 42)   ))
+            
+            
+            
+
+
+
+
+
+class IA_Anti_Gaël(Joueur) :
+    def __init__(self, nom, fct_eval, prof ) :
+        """
+            la fonction d'évaluation sera donnée en entrée, et évaluera les positions.
+            prof indique la profondeur souhaitée 
+        """
+        Joueur.__init__(self,"O",nom)
+        self.fct_eval = fct_eval
+        self.prof = prof              
+
+    
+    def calculer_coup( self, plateau, liste_coup) :
+        #1. : avoir l'arbre
+        #2. : calculer le coup
+        #3 : le ressortir (trouver son indexation dans la liste des coups)
+
+        #etape 1
+        coup = panzer(plateau, self.num)
+
+        if not coup == Coup("B", barriere = Barriere ("v", 42, 42)) :
+            
+            for i in range(len(liste_coup)) :
+                if liste_coup[i] == coup :
+
+                    return i
+
+        return obtenir_coup(self.num, plateau, self.fct_eval, self.prof)
+        
+
+
+
+
+
+
+
+def fct_eval_anti_gael(plateau, num) :
+    nIA = plateau.longueur_chemin(num)
+    n_opponent = plateau.longueur_chemin(1-num)
+    b_IA = plateau.barrieres_restantes[num]
+    b_opponent = plateau.barrieres_restantes[1-num]
+        
+    """
+    if deux_chemins(plateau,num) : c = -4
+    if deux_chemins (plateau,1-num) : d = 3
+    """
+    def f(n) :
+        t = [1000000000,1000000,40,38]
+        a = len(t)
+        if n< a : return t[n]
+        else :
+            return 40-n
+
+
+
+    def g(b) :
+        t = [6,4,2,0,-2,-4,-6,-8,-10,-12.9,-15.8]
+        return t[10-b]
+    def g2(b) :
+        t = [0.5,0,-0.5,-1,-1.5,-2,-3,-4,-5,-7.9,-10.8]
+        return t[10-b]
+
+    if IA_minmax_alpha_beta.dist == None or IA_minmax_alpha_beta.dist >5 :
+        return f(nIA) - f(n_opponent) + g(b_IA) - g(b_opponent) 
+
+    return f(nIA) - f(n_opponent) + g2(b_IA) - g2(b_opponent) 
+
+
+
+
+
+
+
+

+ 318 - 0
project/match_fonctionnels.py

@@ -0,0 +1,318 @@
+from Outils import *
+from Outils.Arbre import *
+from Outils.Moteur_de_jeu import Partie
+from Outils.Moteur_de_jeu.Partie import *
+import math
+from random import shuffle
+import numpy
+
+from Outils.Arbre import *
+from Outils.Moteur_de_jeu import Joueur
+from Outils.Moteur_de_jeu.Joueur import *
+
+
+from Outils import IA_MinMax
+from Outils.IA_MinMax import *
+from time import time
+
+
+from fct_eval_elric import *
+from Outils.IA_alphabeta import *
+
+
+
+#Il y a 3 types de tournois différents :
+#par élimination directe, en toutes rondes ou en rondes suisses
+# toutes rondes : chacun joue contre tous les autres
+# rondes suisses : les joueurs jouent contre des adversaires ayant un score proche du leur, le nombre de rondes (donc de matchs de chaque joueur) est fixé à l'avance
+
+#pour les utiliser, "tournoi" fait une élimination directe
+#tournoi2 fait un toutes rondes avec des fonctions, tournoi3 directement avec les intelligences artificielles
+#rondes_suisses en rondes suisses
+
+
+
+
+
+
+def match (joueurA, joueurB, k = 2) : # Baptiste 31 05
+    """prend 2 intelligences artificielles en paramètres, ainsi que le nombre minimum de partie pour gagner le match"""
+    joueurA.nom = "A"
+    joueurB.nom = "B"
+    wins = numpy.zeros (2)
+    for i in range (0, 2*(k)) :
+        if wins[0] < k and wins[1] < k :
+            Global.partie = Partie(joueurA, joueurB, False, "")
+            vainqueur = Global.partie.demarrer()
+            if vainqueur.nom == "A"  :
+                wins[0] = wins[0]+1
+            else :
+                wins[1] = wins[1] + 1
+    if wins[0] > wins [1] :
+        return (joueurA)
+    else :
+        return (joueurB)
+
+def max_tab (t) :
+    """indice maximum d'un tableau"""
+    l = len(t)
+    k = t[0]
+    x = 0
+    for i in range (l) :
+        if t[i] > k :
+            x = i
+    return (x)
+
+
+
+def etablir_classement(tab_scores) :
+    """ Etablit le classement à partir du score """
+    n = len(tab_scores)
+    tab = [(tab_scores[i],i) for i in range(n)]
+    tab.sort()
+    tab_classement = [0] * n
+    for i in range(n) :
+        tab_classement[tab[i][1]] = n - 1 - i
+    return tab_classement
+
+def tournoi (intelligences) :
+    """prend en compte des intelligences artificielles, qui jouent toutes les unes contre les autres"""
+    n = len(intelligences)
+    joueurs = [0]*n
+    for i in range (n) :
+        joueurs[i] = intelligences[i]
+    scores = [0]*n
+    for i in range (n) :
+        for j in range (i+1, n) :
+            vainqueur = match (joueurs[i], joueurs[j], 2)
+            if vainqueur == joueurs[i] :
+                scores[i] = scores[i] + 1
+            else :
+                scores[j] = scores[j] + 1
+    return (etablir_classement(scores))
+
+
+def conversion(fonctions) :
+    """Convertit des fonctions d'évaluation en intelligences artificielles MinMax"""
+    n = len(fonctions)
+    joueurs = [0]*n
+    for i in range (n) :
+        joueurs[i] = IA_minmax_alpha_beta("MinMax 2", fonctions[i], 2)
+    return (joueurs)
+
+def distance_bord (position) :
+    """distance au bord"""
+    return (min (position[0], 9-position[0]))
+
+
+def tournoi2(fonctions) :
+    """tournoi de type élimination directe, à partir de fonctions"""
+    return (tournoi3(conversion(fonctions)))
+
+def tournoi3 (joueurs) :
+    """tournoi de type élimination directe, à partir d'intelligences artificielles"""
+    n = len(joueurs)
+    if n == 1 :
+        return (joueurs[0])
+    while len(joueurs) != 1 :
+        n = len(joueurs)
+        if n%2 == 0 :                           #si il y a un nombre de joueurs pair, on les fait se rencontrer
+            k = n//2
+            joueurs_vainqueurs = [0]*(k)
+            for i in range (0, k) :
+                vainqueur = match (joueurs[2*i], joueurs[2*i+1], 2)
+                joueurs_vainqueurs[i] = vainqueur
+            joueurs = joueurs_vainqueurs
+        else :                                  #si il y a un nombre de joueurs impair, on les fait se rencontrer, le dernier est bye, considéré gagnant
+            k = (n-1)//2
+            joueurs_vainqueurs = [0]*(k+1)
+            for i in range (0, k) :
+                vainqueur = match (joueurs[2*i], joueurs[2*i+1], 2)
+                joueurs_vainqueurs[i] = vainqueur
+            joueurs_vainqueurs[-1] = joueurs[-1]
+            joueurs = joueurs_vainqueurs
+            t = match (joueurs[0], joueurs[1], 2)
+    return joueurs[0]
+
+
+def false_sum (tableau) :
+    """compte le nombre de 1 d'un tableau"""
+    n = len(tableau)
+    s = 0
+    for i in range (n) :
+        if tableau[i] == 1 :
+            s = s+1
+    return (s)
+
+
+"""un dernier élément à régler serait le fait que les joueurs ne rejouent pas contre les mêmes adversaires, ce qui n'est pas vérifié ici, est supposé fait par appariement"""
+
+"""le problème semble venir de la partie d'identification avec le vainqueur dans cette fonction-ci"""
+
+def rondes_suisses (joueurs, nombre_rondes = None) :
+    """tournoi en rondes suisses, des IA en paramètres et le nombre de rondes"""
+    n = len (joueurs)
+    if nombre_rondes == None :
+        nombre_rondes = int(log(len(joueurs), 2) + 1 )
+    p = nombre_rondes
+    matrice_matchs = numpy.zeros((n,n))
+    matrice_scores = numpy.zeros((n,p))                     #(appariement prend en compte les joueurs, la matrice des scores, la ronde (utile : non))
+    for i in range (0, p) :
+        t_init = time()
+        if n%2 == 0 :                           #si il y a un nombre de joueurs pair, on les fait se rencontrer
+            k = n//2                           #appariement renvoie les indices des joueurs qui se rencontrent
+            appariements = appariement(joueurs, matrice_scores, i)
+            for j in range (0, k) :
+                J0 = appariements[j][0]
+                J1 = appariements[j][1]
+                vainqueur = match (joueurs[J0], joueurs[J1], 2)                     #J0 et J1 sont des indices
+                matrice_matchs[J0][J1] = 1
+                matrice_matchs[J1][J0] = 1
+                if vainqueur == joueurs[J0] :
+                    matrice_scores[J0][i] = +1
+                    matrice_scores[J1][i] = -1
+                else :
+                    matrice_scores[J0][i] = -1
+                    matrice_scores[J1][i] = +1
+        else :
+            k = (n-1)//2
+            appariements = appariement(joueurs, matrice_scores, i)
+            for j in range (0, k) :
+                J0 = appariements[j][0]
+                J1 = appariements[j][1]
+                vainqueur = match (joueurs[J0], joueurs[J1], 2)
+                matrice_matchs[J0][J1] = 1
+                matrice_matchs[J1][J0] = 1
+                if vainqueur == joueurs[J0] :
+                    matrice_scores[J0][i] = +1
+                    matrice_scores[J1][i] = -1
+                else :
+                    matrice_scores[J0][i] = -1
+                    matrice_scores[J1][i] = +1
+            matrice_scores[appariements[-1]][i] = +1
+    scores_finaux = [0]*n
+    for l in range (n) :
+        scores_finaux[l] = false_sum(matrice_scores[l])
+    return scores_finaux
+
+
+def egalite_tableau (t1, t2) :
+    """vérifie si deux tableaux sont égaux"""
+    c = 0
+    b = True
+    n = len(t1)
+    while c < n and b == True :
+        if t1[c] != t2[c] :
+            b = False
+        c = c + 1
+    return (b)
+
+def appariement (joueurs, matrice_des_scores, ronde) :
+    """la fonction qui à partir des scores renvoie quels matchs sont à faire"""
+    n = len(joueurs)                                           # si il y a un nombre de joueurs impairs, le dernier est 'bye' et gagne automatiquement
+    apparies = [-1]*n
+    if n%2 == 0 :                                               #nombre pair de joueurs
+        compteur = 0
+        k = n//2
+        liste_matchs = [[ 0 for i in range (2)] for j in range (k)]
+        for i in range (n) :                                    #on part du i et on on vérifie lesquels ont le même score (en partant du suivant)
+            if apparies[i] == -1 :                            #on ne continue que s'il n'est pas déjà apparié
+                for j in range (i+1, n) :
+                    if apparies[j] == -1 : 
+                        if egalite_tableau(matrice_des_scores[i], matrice_des_scores[j]) == True :
+                            liste_matchs[compteur][0] = i
+                            liste_matchs[compteur][1] = j
+                            apparies[i] = 1
+                            apparies[j] = 1                #tous ceux qui ont des scores similaires sont apparies faire un test while sur la somme du tableau apparies jusqu'à n
+                            compteur = compteur + 1
+                            break
+        compteur_tolerance = 1
+        while sum(apparies) < n :
+            for i in range (sommenegative(apparies)) :                              #on remet la même chose car on remplit des appariements
+                depart = find_meilleur(apparies, matrice_des_scores)                #en fait ça sert à apparier les joueurs qui n'ont pas exactement les mêmes scores, mais ayant des scores "similaires"
+                for j in range (n) :
+                    if apparies[j] == -1 and j!= depart :
+                        if quasi_similitude (matrice_des_scores[depart], matrice_des_scores[j], compteur_tolerance) == True :    #ça fait la même chose, au début on accepte s'il y a une différence de score
+                            liste_matchs[compteur][0] = depart                                                                   
+                            liste_matchs[compteur][1] = j
+                            apparies[depart] = 1
+                            apparies[j] = 1
+                            compteur = compteur +1
+                            break
+            compteur_tolerance = compteur_tolerance + 1                                                 #si tout n'est pas déjà fait, on passe au degré de tolérance suivant (2 différences, 3...)
+        return (liste_matchs)
+    else :
+        #Nombre de joueurs IMPAIR
+        #c'est excatement la même chose mais le dernier est 'bye' et gagne automatiquement
+        compteur = 0                                                                            
+        k = (n-1)//2
+        liste_matchs = [[ 0 for i in range (2)] for j in range (k+1)]
+        for i in range (n) :
+             if apparies[i] == -1 :
+                 for j in range (i+1, n) :
+                     if apparies[j] == -1 : 
+                         if egalite_tableau(matrice_des_scores[i], matrice_des_scores[j]) == True :
+                            liste_matchs[compteur][0] = i
+                            liste_matchs[compteur][1] = j
+                            apparies[i] = 1
+                            apparies[j] = 1                #tous ceux qui ont des scores similaires sont apparies faire un test while sur la somme du tableau apparies jusqu'à n
+                            compteur = compteur + 1
+                            break
+        compteur_tolerance = 1
+        while sum(apparies) < (n-2) :
+            #"""LA IL Y A UNE DIFFERENCE""" on ne peut nas atteindre n, car un ne sera jamais apparie
+            for i in range (sommenegative(apparies)-1) :
+            #on remet la même chose car on remplit des appariements
+                depart = find_meilleur(apparies, matrice_des_scores)                                       #en fait ça sert à apparier les joueurs qui n'ont pas exactement les mêmes scores, mais ayant des scores "similaires"
+                for j in range (n) :
+                    if apparies[j] == -1 and j!= depart :
+                        if quasi_similitude (matrice_des_scores[depart], matrice_des_scores[j], compteur_tolerance) == True :    #ça fait la même chose, au début on accepte s'il y a une différence de score
+                            liste_matchs[compteur][0] = depart                                                                   
+                            liste_matchs[compteur][1] = j
+                            apparies[depart] = 1
+                            apparies[j] = 1
+                            compteur = compteur +1
+                            break
+            compteur_tolerance = compteur_tolerance + 1    #si tout n'est pas déjà fait, on passe au degré de tolérance suivant (2 différences, 3...)
+        liste_matchs[-1] = find (apparies, -1)
+        return liste_matchs
+
+
+def quasi_similitude (tableau1, tableau2, tolerance) :
+    """renvoie true si le nombre de différences dans les tableaux est inférieur ou égal à la tolérance"""
+    l = len(tableau1)
+    s = 0
+    for i in range (l) :
+        if tableau1[i] != tableau2[i] :
+            s = s+1
+    return (s <= tolerance)
+
+def find(tableau, valeur) :
+    n = len(tableau)
+    for i in range (n) :
+        if tableau[i] == valeur :
+            return (i)
+        
+def sommenegative (tableau) :
+    """compte le nombre de -1"""
+    n = len(tableau)
+    s = 0
+    for i in range (n) :
+        if tableau[i] == -1 :
+            s = s+1
+    return (s)
+
+def find_meilleur (apparies, matrice_scores) :
+    """renvoie la personne avec le meilleur score et son indice, parmi ceux qui ne sont pas appariés"""
+    n = len(apparies)
+    scores = [0]*n
+    p = len(matrice_scores[0])
+    for i in range (n) :
+        if apparies[i] == 1 :
+            scores[i] = -p-1
+        else :
+            scores[i] = sum(matrice_scores[i])
+    sm = max(scores)
+    return(find(scores, sm))
+
+

+ 53 - 0
project/mesure_compare_pathsfinders.py

@@ -0,0 +1,53 @@
+from Outils.Moteur_de_jeu import Plateau
+from Outils.Moteur_de_jeu.Plateau import *
+
+from time import time
+from random import shuffle
+
+# Fonction aléa
+def faire_alea_plat( n = 10 ) :
+    plateau = Plateau(9,9,n)
+    for i in range(n) :
+        l = plateau.liste_barrieres_possibles()
+        shuffle(l)
+        plateau.executer_coup(Coup("B", barriere = l[0]))
+
+    return plateau
+
+# Procédure de mesure
+nb_tests = 50
+
+ti = 0
+
+t_map = 0
+t_star = 0
+
+for k in range(nb_tests):
+    p = faire_alea_plat()
+    for i in range(9):
+        for j in range(9):
+            ti = time()
+            path_finding_a_star( p, i, j, 0 )
+            t_star += time() - ti
+            
+            ti = time()
+            path_finding_mapping( p, i, j, 0 )
+            t_map += time() - ti
+
+
+print("Nombre de tests : ", 9*9*nb_tests)
+print()
+
+print("Performances Mapping :")
+print("Temps total : ", t_map )
+print("Temps par test : ", t_map / ( 9*9*nb_tests ) )
+print()
+
+print("Performances Star :")
+print("Temps total : ", t_star )
+print("Temps par test : ", t_star / ( 9*9*nb_tests ) )
+print()
+
+print("Comparaison :")
+print("Map est ", t_map / t_star, " fois plus lents.")
+print("Temps réduit de ", (t_map - t_star) / t_map * 100, " pourcents.")