## 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 )