Pathfinding.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. import numpy as np
  2. def suppr (t, a) : # Elric et Baptiste 09/2016
  3. """ On supprime tout les elements valant a dans le tableau t """
  4. for i in t :
  5. if i == a :
  6. t.remove(i)
  7. def cases_voisines(plateau, x, y) : # Elric et Baptiste 09/2016
  8. """ Retourne les cases adjacentes à la case (x,y) """
  9. # On commence par tester les valeurs de x, puis on fait de même sur y.
  10. if x == 0 :
  11. if y == 0 :
  12. return [(0, 1), (1, 0)]
  13. elif y == plateau.ht - 1 :
  14. return [(1, y), (0, y-1)]
  15. else :
  16. return [(0, y-1), (0, y+1), (1, y)]
  17. elif x == plateau.lg - 1 :
  18. if y == 0 :
  19. return [(x, 1), (x-1, 0)]
  20. elif y == plateau.ht - 1 :
  21. return [(x-1, y), (x, y-1)]
  22. else :
  23. return [(x, y-1), (x, y+1), (x-1, y)]
  24. else :
  25. if y == 0 :
  26. return [(x-1, y), (x+1, y), (x, 1)]
  27. elif y == plateau.ht - 1 :
  28. return [(x-1, y), (x+1, y), (x, y-1)]
  29. else :
  30. return [(x-1, y), (x+1, y), (x, y-1), (x, y+1)]
  31. def cases_accessibles(plateau, x, y) : # Elric et Baptiste 09/2016
  32. """ Prend en compte les barrières """
  33. t = cases_voisines ( plateau, x , y )
  34. n = len(plateau.liste_barrieres)
  35. b = plateau.liste_barrieres
  36. for bar in b :
  37. if bar.x == x :
  38. if bar.y == y :
  39. if bar.orientation == "v" :
  40. suppr (t, (x+1, y))
  41. else:
  42. suppr (t, (x, y+1))
  43. elif bar.y == y-1 :
  44. if bar.orientation == "v" :
  45. suppr (t, (x+1, y))
  46. else:
  47. suppr (t, (x, y-1))
  48. elif bar.x == x-1 :
  49. if bar.y == y :
  50. if bar.orientation == "v" :
  51. suppr (t, (x-1, y))
  52. else:
  53. suppr (t, (x, y+1))
  54. elif bar.y == y-1 :
  55. if bar.orientation == "v" :
  56. suppr (t, (x-1, y))
  57. else:
  58. suppr (t, (x, y-1))
  59. return (t)
  60. def path_finding_mapping( plateau, xi, yi, yf ) : # Jovian : 4 octobre 2016
  61. """
  62. Calcule le chemin le plus court pour gagner.
  63. Argument plateau : argument self de python.
  64. Argument xi : abscisse de départ.
  65. Argument yi : ordonnée de départ.
  66. Argument yf : ordonnée qui désigne la ligne d'arrivée.
  67. Retour :
  68. Une liste des couples (x, y) des positions successives.
  69. Position de départ inclue.
  70. Liste vide si aucun chemin possible.
  71. """
  72. # Cartographie les cases accessibles
  73. carte = map_distance( plateau, xi, yi )
  74. w = plateau.lg
  75. h = plateau.ht
  76. # Position variable
  77. x = -1
  78. y = yf
  79. # Détermination du point d'arrivée
  80. dist = -1
  81. for k in range( w ) :
  82. if carte[ k ][ yf ] > 0 :
  83. if x == -1 or carte[ k ][ yf ] < dist :
  84. x = k
  85. dist = carte[ x ][ yf ]
  86. # Arrivée non joignable
  87. if x == -1 :
  88. return []
  89. # Reconstitution du chemin
  90. rep = [(x, y)]
  91. while x != xi or y != yi :
  92. # Case actuelle
  93. x, y = rep[ len(rep) - 1 ]
  94. dist = carte[ x ][ y ]
  95. # Récupère les cases accessibles
  96. access = cases_accessibles( plateau, x, y )
  97. # Recherche de la case la plus proche
  98. while len( access ) > 0 :
  99. u, v = access.pop(0)
  100. if carte[ u ][ v ] < dist :
  101. x = u
  102. y = v
  103. rep.append( (x, y) )
  104. break
  105. # Renvoie le chemin
  106. return rep
  107. def map_distance( plateau, xi, yi ) : # Jovian : 4 octobre 2016
  108. """
  109. Calcule un tableau contenant la distance de chaque case par rapport au joueur.
  110. Tient compte de l'éloignement dû aux barrières.
  111. La case contient 0 si inaccessible.
  112. Argument plateau : argument self de python.
  113. Argument xi : abscisse de départ.
  114. Argument yi : ordonnée de départ.
  115. Retour : un tableau d'entiers indexé (x, y).
  116. """
  117. # Création du tableau
  118. w = plateau.lg
  119. h = plateau.ht
  120. rep = np.zeros( (w, h) )
  121. rep[ xi ][ yi ] = 1
  122. # Création de la file
  123. fifo = [(xi, yi)]
  124. # Balayage du plateau depuis la position de départ
  125. while len( fifo ) > 0 :
  126. # Point sur un bord
  127. x, y = fifo.pop(0)
  128. dist = rep[ x ][ y ]
  129. # Récupère les cases accessibles
  130. access = cases_accessibles( plateau, x, y )
  131. # Traitement des cases adjacentes
  132. while len( access ) > 0 :
  133. u, v = access.pop(0)
  134. if rep[ u ][ v ] == 0 :
  135. rep[ u ][ v ] = dist + 1
  136. fifo.append( (u, v ) )
  137. # Retourne le tableau
  138. return rep
  139. ## Version A*
  140. from heapq import *
  141. def get_path(start, target, parent) : # Jovian : 25 novembre 2016
  142. """ Construit et retourne le chemin solution.
  143. """
  144. path = [target]
  145. x, y = target
  146. while (x, y) != start:
  147. x, y = parent[x, y]
  148. path.append((x, y))
  149. return list( reversed(path) )
  150. def path_finding_a_star( plateau, xi, yi, yf, hcap = 0 ) : # Jovian : 7 décembre 2016 : éviter le contact barrières
  151. """
  152. Calcule le chemin le plus court pour gagner.
  153. Argument plateau : argument self de python.
  154. Argument xi : abscisse de départ.
  155. Argument yi : ordonnée de départ.
  156. Argument yf : ordonnée qui désigne la ligne d'arrivée.
  157. Argument hcap : valeur malus ajoutée si le pion est en contact avec une barrière.
  158. Retour :
  159. Une liste des couples (x, y) des positions successives.
  160. Position de départ inclue.
  161. Liste vide si aucun chemin possible.
  162. """
  163. # Variables
  164. open_set = []
  165. closed = np.zeros( (plateau.lg, plateau.ht), dtype = bool )
  166. parent = np.zeros( (plateau.lg, plateau.ht, 2), dtype = int )
  167. cost_so_far = np.ones((plateau.lg, plateau.ht)) * np.inf
  168. # Estimation distance restante
  169. def heuristic( z ):
  170. # Distance directe
  171. x, y = z
  172. dist = abs(y - yf)
  173. return dist
  174. # Malus si contact avec une barrière
  175. def malus( z ):
  176. x , y = z
  177. if plateau.contact( x, y ) :
  178. return hcap
  179. else :
  180. return 0
  181. # Ajoute un élément qui sera traité plus tard
  182. def push(open_set, z):
  183. priority = cost_so_far[z] + heuristic(z) + malus(z)
  184. heappush(open_set, (priority, z) )
  185. # Retire la case potentiellement la plus proche
  186. def pop(open_set):
  187. _, current = heappop(open_set)
  188. return current
  189. # Initialisation
  190. start = xi, yi
  191. cost_so_far[start] = 0
  192. push(open_set, start)
  193. current = (0, 0)
  194. # Parcours
  195. while open_set != []:
  196. # Case (x, y) en cours de traitement
  197. current = pop(open_set)
  198. x, y = current
  199. # Si on a atteint l'arrivée
  200. if current[1] == yf :
  201. break
  202. # Si la case a déjà été vue
  203. if closed[current] :
  204. continue
  205. # On dit qu'on a traité cette case
  206. closed[current] = True
  207. # Parcours des voisins
  208. for neighbor in cases_accessibles( plateau, x, y ):
  209. # Voisin déjà traité
  210. if closed[neighbor]:
  211. continue
  212. new_cost_neighbor = cost_so_far[current] + malus(neighbor) + 1
  213. # Si notre chemin est plus court pour accéder à cette case
  214. if new_cost_neighbor < cost_so_far[neighbor]:
  215. cost_so_far[neighbor] = new_cost_neighbor
  216. push(open_set, neighbor)
  217. parent[neighbor] = current
  218. # Fin de parcours
  219. if current[1] == yf :
  220. # Chemin abouti
  221. return get_path(start, current, parent)
  222. else :
  223. # Chemin inexistant
  224. return []
  225. ## Alias du pathfinding
  226. def path_finding( plateau, xi, yi, yf ) : # Jovian : 7 décembre 2016 : éviter le contact barrières
  227. """
  228. Pour éviter le contact avec les barrières : mettez hcap = 0.05
  229. Sinon hcap = 0.0
  230. """
  231. return path_finding_a_star( plateau, xi, yi, yf, hcap = 0.05 )