cerveau.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. ## Fichier : cerveau.py
  2. # Auteur : Jovian Hersemeule
  3. # Un réseau de neurones possède des neurones d'entrée,
  4. # éventuellement des neurones intermédiaires, et des neurones
  5. # de sortie. Chaque entrée de neurone est constituée d'une combinaison
  6. # linéaire de sorties d'autres neurones.
  7. from random import uniform
  8. from ReseauNeurones.network import *
  9. from ReseauNeurones.activations import *
  10. from Outils.Moteur_de_jeu.Pathfinding import path_finding
  11. class Cerveau :
  12. # Variables statiques
  13. inSize = 144 # nombre total d'entrées
  14. def __init__( self ): # Jovian 17 janvier 2017
  15. """
  16. Créé un cerveau vide.
  17. """
  18. self.nom_structure = None # string
  19. self.nom_poids = None # string
  20. self.liste_entrees = [ 0.0 for k in range( Cerveau.inSize ) ] # Float list
  21. self.liste_intermed = [] # Float list ( None dans une case si valeur non encore calculée )
  22. self.liste_activ = [] # Function list
  23. self.liste_synapses = [] # Couple string list
  24. self.liste_poids = [] # Float list
  25. self.network = Network() # Réseau
  26. def calcule_noeud( self, index ): # Jovian 14 mars 2017
  27. """
  28. Prend un identifiant comme 'e1', 'i43' ou 's0' et retourne sa valeur.
  29. Fonction récursive.
  30. Argument id : string
  31. Retour : float
  32. """
  33. # Analyse du noeud
  34. node_type = index[0]
  35. node_nb = int( index[1] )
  36. # Cas de base
  37. if node_type == 'e':
  38. return self.liste_entrees[ node_nb ]
  39. if node_type == 'i' and self.liste_intermed[ node_nb ] != None :
  40. return self.liste_intermed[ node_nb ]
  41. # Calcul récursif
  42. parents = self.network.getParents( index )
  43. rep = 0.0
  44. w = 0.0
  45. v = 0.0
  46. for p in parents :
  47. v = self.calcule_noeud( p )
  48. w = self.network.getPoids( p, index )
  49. rep += v * w
  50. # Neurone intermédiaire : fonction d'activation et mémorisation
  51. if node_type == 'i' :
  52. rep = self.liste_activ[ node_nb ]( rep )
  53. self.liste_intermed[ node_nb ] = rep
  54. # Fin de la fonction
  55. return rep
  56. def fct_eval( self, plateau, num ): # Jovian 14 mars 2017
  57. """
  58. Fonction d'évaluation à passer au MinMax.
  59. """
  60. # Option de déboguage
  61. debug = False
  62. # Actualise liste_entrees : attention à la relativité pos moi ou lui
  63. mid_x = plateau.lg / 2
  64. y_but_moi = plateau.rangee_desiree( num )
  65. y_but_lui = plateau.rangee_desiree( 1 - num )
  66. self.liste_entrees[0] = ( plateau.pions[ num ][ 0 ] - mid_x ) / mid_x
  67. self.liste_entrees[1] = abs( plateau.pions[ num ][ 1 ] - y_but_moi ) / plateau.ht
  68. self.liste_entrees[2] = ( plateau.pions[ 1 - num ][ 0 ] - mid_x ) / mid_x
  69. self.liste_entrees[3] = abs( plateau.pions[ 1 - num ][ 1 ] - y_but_lui ) / plateau.ht
  70. self.liste_entrees[4] = plateau.barrieres_restantes[ num ] / 10
  71. self.liste_entrees[5] = plateau.barrieres_restantes[ 1 - num ] / 10
  72. self.liste_entrees[6] = len( path_finding( plateau, plateau.pions[num][0], plateau.pions[num][1], y_but_moi ) ) / 40
  73. self.liste_entrees[7] = len( path_finding( plateau, plateau.pions[1 - num][0], plateau.pions[1 - num][1], y_but_lui ) ) / 40
  74. # TODO : Faire les booléens de barrière
  75. # Affichage des valeurs d'éntrées
  76. if debug :
  77. print("\nLancement de la fontion d'évaluation génétique.\n")
  78. print("Identifiant du joueur : ", num )
  79. print("Liste des entrées :")
  80. for k in range(8) :
  81. print("Entrée numéro ", k, " vaut : ", self.liste_entrees[k] )
  82. # Reset intermédiaires
  83. for k in range( len( self.liste_intermed ) ):
  84. self.liste_intermed[k] = None
  85. # Calculer l'entrée : fonction récursive depuis, la sortie
  86. return self.calcule_noeud( 's0' )
  87. def charger_structure( self, nom_structure, nom_poids = None, debug = False ): # Jovian 7 février 2017
  88. """
  89. Créé ou recréé un réseau de neurones à partir d'un fichier structure dont le nom est passé en argument.
  90. Si un fichier de poids n'est pas spécifié, tous les poids valent 0.
  91. """
  92. # Administratif
  93. print("\n> Début de chargement de la structure.")
  94. if self.nom_structure == nom_structure :
  95. print( "Structure <<", nom_structure, ">> déjà chargée." )
  96. return None
  97. elif self.nom_structure != None :
  98. print( "Structure <<", self.nom_structure, ">> déjà chargée." )
  99. print( "Structure <<", nom_structure, ">> non-chargée." )
  100. return None
  101. self.nom_structure = nom_structure
  102. # Ouverture du flux
  103. chemin = "ReseauNeurones/Base_Structures/" + nom_structure
  104. if chemin[-4] != "." :
  105. chemin += ".stc"
  106. flux = open( chemin )
  107. # Lectures des neurones intermédiaires
  108. self.liste_intermed = []
  109. self.liste_activ = []
  110. buff = flux.readline()
  111. while buff[0] != ">" :
  112. if debug : print("\nTraitement du neurone :", buff[0:-1])
  113. # Ajout d'un intermédiaire
  114. self.liste_intermed.append( None )
  115. # Reconnaisance du type de fonction d'activation
  116. if buff[0] == "g" :
  117. arg = float( buff[2:] )
  118. self.liste_activ.append( lambda x : sigmoid( x, l = arg ) )
  119. if debug : print("Ajout d'une fonction sigmoïde d'argument", arg)
  120. elif buff[0] == "d" :
  121. self.liste_activ.append( seuil )
  122. if debug : print("Ajout d'une fonction seuil.")
  123. else :
  124. print("Fonction d'activation ", buff, " inconnue." )
  125. # Etape suivante
  126. buff = flux.readline()
  127. # Lectures des synapses
  128. if debug : print("\nLecture des synapses : ")
  129. node_start = "e0"
  130. node_end = "s0"
  131. buff = flux.readline()
  132. while buff[0] == "e" or buff[0] == "i" :
  133. if debug : print("Ajout du synapse : ", buff[0:-1])
  134. # Ajout du synapse
  135. node_start = buff[0:2]
  136. node_end = buff[3:5]
  137. self.liste_synapses.append( ( node_start, node_end ) )
  138. # Etape suivante
  139. buff = flux.readline()
  140. if len( buff ) == 0 :
  141. break ;
  142. # Charge les poids si argument utilisable
  143. if nom_poids != None :
  144. self.charger_poids( nom_poids )
  145. # Debug : affichage des données
  146. if debug :
  147. print( "\nNeurones intermédiaires :")
  148. print( len( self.liste_intermed ), " neurones intermédiaires. \n" )
  149. print( "Synapses :" )
  150. for syn in self.liste_synapses :
  151. print( syn )
  152. print("\n> Fin de chargement de la structure.")
  153. def charger_poids( self, nom_poids, debug = False ): # Jovian 7 février 2017
  154. """
  155. Affecte des poids au réseau. Il faut que la structure soit compatible.
  156. Un message d'erreur est affiché si ce n'est pas le cas.
  157. """
  158. # Administratif
  159. if self.nom_structure == None :
  160. print("Erreur : aucune structure n'est chargée !")
  161. return None
  162. self.nom_poids = nom_poids
  163. # Détruire les anciens poids
  164. if debug : print( "\n> Chargement des poids pour ", self.nom_structure, ".")
  165. if self.liste_poids != [] :
  166. if debug : print("Destruction des anciens poids.")
  167. self.liste_poids = []
  168. elif debug :
  169. print("Poids inexistants : chargement pour la 1ere fois.")
  170. # Charger
  171. chemin = "ReseauNeurones/Base_Poids/" + nom_poids
  172. if chemin[-4] != "." :
  173. chemin += ".pds"
  174. with open( chemin ) as flux :
  175. for pds in flux :
  176. self.liste_poids.append( float( pds ) )
  177. # Debug : affichage des données
  178. if debug :
  179. print( "\nPoids des synapses :")
  180. print( len( self.liste_poids ), " poids enregistrés. \n" )
  181. print( "Poids :" )
  182. for syn in self.liste_poids :
  183. print( syn )
  184. print("\n> Fin de chargement des poids.")
  185. def generer_poids( self, inf = -1.0, sup = 1.0, debug = False ): # Jovian 7 février 2017
  186. """
  187. Si une structure est chargée, génère des poids aléatoires.
  188. """
  189. # Détruire les anciens poids
  190. if debug : print( "\n> Génération des poids pour ", self.nom_structure, ".")
  191. if self.liste_poids != [] :
  192. if debug : print("Destruction des anciens poids.")
  193. self.liste_poids = []
  194. elif debug :
  195. print("Poids inexistants : génération pour la 1ere fois.")
  196. # Aléatoire
  197. for k in range( len( self.liste_synapses ) ) :
  198. self.liste_poids.append( uniform( inf, sup ) )
  199. # Debug : affichage des données
  200. if debug :
  201. print( "\nPoids des synapses :")
  202. print( len( self.liste_poids ), " poids enregistrés.\n" )
  203. print( "Poids :" )
  204. for syn in self.liste_poids :
  205. print( syn )
  206. print("\n> Fin de génération des poids.")
  207. def donner_poids( self, liste_poids, debug = False ): # Jovian 21 mars 2017
  208. """
  209. Si une structure est chargée, assigne les poids donnés en argument.
  210. """
  211. # Vérification du format
  212. if len(self.liste_synapses) != len(liste_poids) :
  213. if debug : print( "\n>Taille de la nouvelle liste de poids incorrecte." )
  214. return None
  215. # Détruire les anciens poids
  216. if debug : print( "\n> Génération des poids pour ", self.nom_structure, ".")
  217. if self.liste_poids != [] :
  218. if debug : print("Destruction des anciens poids.")
  219. self.liste_poids = []
  220. elif debug :
  221. print("Poids inexistants : attribution manuelle.")
  222. # Copie
  223. self.liste_poids = list( liste_poids )
  224. # Debug : affichage des données
  225. if debug :
  226. print( "\nPoids des synapses :")
  227. print( len( self.liste_poids ), " poids enregistrés.\n" )
  228. print( "Poids :" )
  229. for syn in self.liste_poids :
  230. print( syn )
  231. print("\n> Fin d'attribution des poids.")
  232. def build_network( self, debug = False ): # Jovian 7 février 2017
  233. """
  234. Construit le réseau à partir des poids précedemment chargés.
  235. """
  236. self.network.build( self.liste_synapses, self.liste_poids, Cerveau.inSize, len( self.liste_intermed ), debug = debug )