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