fdPathfinder.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. unit fdPathfinder;
  2. interface
  3. uses
  4. System.SysUtils,
  5. System.Types,
  6. System.UITypes,
  7. System.Classes,
  8. System.Variants,
  9. System.Generics.Collections,
  10. FMX.Types,
  11. FMX.Controls,
  12. FMX.Forms,
  13. FMX.Graphics,
  14. FMX.Dialogs,
  15. FMX.Objects,
  16. FMX.Controls.Presentation,
  17. FMX.StdCtrls,
  18. FMX.Layouts,
  19. FMX.Edit,
  20. uGBEPathFinder;
  21. type
  22. TFormPathfinder = class(TForm)
  23. layIHM: TLayout;
  24. btnTrouverChemin: TButton;
  25. layGrille: TLayout;
  26. rectangleModele: TRectangle;
  27. lblTotal: TLabel;
  28. tDistanceD: TText;
  29. tDistanceA: TText;
  30. lblInfos: TLabel;
  31. cbDiagonale: TCheckBox;
  32. layOptions: TLayout;
  33. ScrollBox: TScrollBox;
  34. eNbColonne: TEdit;
  35. SpinEditButton1: TSpinEditButton;
  36. lblNbColonne: TLabel;
  37. lblLigne: TLabel;
  38. eNbLigne: TEdit;
  39. SpinEditButton2: TSpinEditButton;
  40. btnCreerGrille: TButton;
  41. gbOptions: TGroupBox;
  42. cbPremiereEtape: TCheckBox;
  43. cbModeCout: TCheckBox;
  44. procedure FormCreate(Sender: TObject);
  45. procedure rectangleModeleClick(Sender: TObject);
  46. procedure FormDestroy(Sender: TObject);
  47. procedure btnTrouverCheminClick(Sender: TObject);
  48. procedure SpinEditButton1DownClick(Sender: TObject);
  49. procedure SpinEditButton1UpClick(Sender: TObject);
  50. procedure SpinEditButton2UpClick(Sender: TObject);
  51. procedure SpinEditButton2DownClick(Sender: TObject);
  52. procedure btnCreerGrilleClick(Sender: TObject);
  53. private
  54. procedure creerGrille;
  55. procedure mettreAJourCase(position: TPoint; couleur: TAlphaColor;
  56. dDistance, aDistance: integer);
  57. procedure celluleClick(Sender: TObject);
  58. procedure initialiserGrille;
  59. function getPoint(indice: integer): TPoint;
  60. function getIndice(point: TPoint): integer;
  61. procedure dessinerResultat;
  62. public
  63. noeudDepart, noeudArrivee: TGBENoeud;
  64. lGrille, hGrille: integer;
  65. PathFinder: TGBEPathFinder;
  66. end;
  67. var
  68. fFormPathfinder: TFormPathfinder;
  69. implementation
  70. {$R *.fmx}
  71. procedure TFormPathfinder.FormCreate(Sender: TObject);
  72. begin
  73. randomize;
  74. PathFinder := TGBEPathFinder.Create;
  75. PathFinder.CoutDeplacementCote := 10;
  76. PathFinder.CoutDeplacementDiagonal := 15;
  77. eNbColonne.Text := '12';
  78. eNbLigne.Text := '10';
  79. creerGrille;
  80. initialiserGrille;
  81. end;
  82. procedure TFormPathfinder.FormDestroy(Sender: TObject);
  83. begin
  84. FreeAndNil(PathFinder);
  85. end;
  86. procedure TFormPathfinder.creerGrille;
  87. var
  88. x, y, indice: integer;
  89. begin
  90. indice := 0;
  91. hGrille := strtointdef(eNbLigne.Text, 8);
  92. lGrille := strtointdef(eNbColonne.Text, 8);
  93. layGrille.Width := rectangleModele.Width * lGrille;
  94. layGrille.Height := rectangleModele.Height * hGrille;
  95. // La grille est constituée de TRectangle clonés à partir du rectangleModele
  96. for y := 0 to hGrille - 1 do
  97. begin
  98. for x := 0 to lGrille - 1 do
  99. begin
  100. if indice >= 1 then
  101. begin
  102. with rectangleModele.Clone(nil) as TRectangle do
  103. begin
  104. parent := layGrille;
  105. name := 'rectangle' + indice.ToString;
  106. position.x := x * Width;
  107. position.y := y * Height;
  108. tag := indice;
  109. // le tag va contenir l'indice de la case, ce qui permettra d'avoir ensuite les coordonnées X et Y de la case
  110. onClick := celluleClick;
  111. end;
  112. end;
  113. inc(indice);
  114. end;
  115. end;
  116. end;
  117. procedure TFormPathfinder.initialiserGrille;
  118. var
  119. iRectangle: TFMxObject;
  120. begin
  121. // On dessine toutes les cases de la grille en blanc et sans texte
  122. for iRectangle in layGrille.Children do
  123. begin
  124. (iRectangle as TRectangle).Fill.Gradient.Color := TAlphaColorRec.White;
  125. ((iRectangle as TRectangle).Children[0] as TLabel).Text := '';
  126. ((iRectangle as TRectangle).Children[1] as TText).Text := '';
  127. ((iRectangle as TRectangle).Children[2] as TText).Text := '';
  128. end;
  129. // On détermine aléatoirement les cases de départ et d'arrivée
  130. noeudDepart.position.x := random(lGrille);
  131. noeudDepart.position.y := random(hGrille);
  132. noeudDepart.coutDeplacement := 0;
  133. noeudArrivee.position.x := random(lGrille);
  134. noeudArrivee.position.y := random(hGrille);
  135. // Si la case d'arrivée est adjacente à la case de départ, on recherche une autre position pour la case d'arrivée
  136. while (abs(noeudArrivee.position.x - noeudDepart.position.x) <= 1) and
  137. (abs(noeudArrivee.position.y - noeudDepart.position.y) <= 1) do
  138. begin
  139. noeudArrivee.position.x := random(lGrille);
  140. noeudArrivee.position.y := random(hGrille);
  141. end;
  142. // On intialise les coûts des cases de départ et d'arrivée
  143. noeudArrivee.coutDeplacement := PathFinder.calculerCoutArrivee
  144. (noeudDepart.position);
  145. noeudArrivee.heuristique := 0;
  146. noeudDepart.heuristique := noeudArrivee.coutDeplacement;
  147. // On dessine les cases de départ et d'arrivée
  148. mettreAJourCase(noeudDepart.position, TAlphaColorRec.Cyan, 0,
  149. noeudDepart.heuristique);
  150. mettreAJourCase(noeudArrivee.position, TAlphaColorRec.Cyan,
  151. noeudArrivee.coutDeplacement, 0);
  152. lblInfos.Text := 'Click on white squares to generate obstacles';
  153. end;
  154. // Dessine les différentes cases de la grille
  155. procedure TFormPathfinder.mettreAJourCase(position: TPoint;
  156. couleur: TAlphaColor; dDistance, aDistance: integer);
  157. var
  158. indice, valeur: integer;
  159. unNoeud: TGBENoeud;
  160. begin
  161. indice := getIndice(position);
  162. if (position = noeudDepart.position) or (position = noeudArrivee.position)
  163. then
  164. begin
  165. if (position = noeudDepart.position) then
  166. ((layGrille.Children[indice] as TRectangle).Children[0] as TLabel)
  167. .Text := 'D'
  168. else
  169. ((layGrille.Children[indice] as TRectangle).Children[0] as TLabel)
  170. .Text := 'A';
  171. ((layGrille.Children[indice] as TRectangle).Children[0] as TLabel)
  172. .TextSettings.FontColor := TAlphaColorRec.Red;
  173. ((layGrille.Children[indice] as TRectangle).Children[1] as TText).Text :=
  174. aDistance.ToString;
  175. ((layGrille.Children[indice] as TRectangle).Children[2] as TText).Text :=
  176. dDistance.ToString;
  177. (layGrille.Children[indice] as TRectangle).Fill.Gradient.Color :=
  178. TAlphaColorRec.Cyan;
  179. end
  180. else
  181. begin
  182. unNoeud.position := position;
  183. ((layGrille.Children[indice] as TRectangle).Children[0] as TLabel)
  184. .TextSettings.FontColor := TAlphaColorRec.Black;
  185. if PathFinder.listeNoeudsObstacles.TryGetValue(unNoeud.position, unNoeud)
  186. then
  187. begin
  188. ((layGrille.Children[indice] as TRectangle).Children[0] as TLabel)
  189. .Text := '';
  190. (layGrille.Children[indice] as TRectangle).Fill.Gradient.Color := couleur;
  191. end
  192. else
  193. begin
  194. valeur := dDistance + aDistance;
  195. if valeur = 0 then
  196. ((layGrille.Children[indice] as TRectangle).Children[0] as TLabel)
  197. .Text := ''
  198. else
  199. ((layGrille.Children[indice] as TRectangle).Children[0] as TLabel).Text
  200. := valeur.ToString;
  201. if aDistance = 0 then
  202. ((layGrille.Children[indice] as TRectangle).Children[1] as TText)
  203. .Text := ''
  204. else
  205. ((layGrille.Children[indice] as TRectangle).Children[1] as TText).Text
  206. := aDistance.ToString;
  207. if dDistance = 0 then
  208. ((layGrille.Children[indice] as TRectangle).Children[2] as TText)
  209. .Text := ''
  210. else
  211. ((layGrille.Children[indice] as TRectangle).Children[2] as TText).Text
  212. := dDistance.ToString;
  213. (layGrille.Children[indice] as TRectangle).Fill.Gradient.Color := couleur;
  214. end;
  215. end;
  216. end;
  217. procedure TFormPathfinder.rectangleModeleClick(Sender: TObject);
  218. begin
  219. celluleClick(Sender);
  220. end;
  221. procedure TFormPathfinder.SpinEditButton1DownClick(Sender: TObject);
  222. begin
  223. if strtointdef(eNbColonne.Text, 0) > 0 then
  224. eNbColonne.Text := (strtointdef(eNbColonne.Text, 0) - 1).ToString;
  225. end;
  226. procedure TFormPathfinder.SpinEditButton1UpClick(Sender: TObject);
  227. begin
  228. if strtointdef(eNbColonne.Text, 0) > 0 then
  229. eNbColonne.Text := (strtointdef(eNbColonne.Text, 0) + 1).ToString;
  230. end;
  231. procedure TFormPathfinder.SpinEditButton2DownClick(Sender: TObject);
  232. begin
  233. if strtointdef(eNbLigne.Text, 0) > 0 then
  234. eNbLigne.Text := (strtointdef(eNbLigne.Text, 0) - 1).ToString;
  235. end;
  236. procedure TFormPathfinder.SpinEditButton2UpClick(Sender: TObject);
  237. begin
  238. if strtointdef(eNbLigne.Text, 0) > 0 then
  239. eNbLigne.Text := (strtointdef(eNbLigne.Text, 0) + 1).ToString;
  240. end;
  241. procedure TFormPathfinder.btnTrouverCheminClick(Sender: TObject);
  242. var
  243. iRectangle: TFMxObject;
  244. unNoeud: TGBENoeud;
  245. begin
  246. PathFinder.LargeurGrille := lGrille;
  247. PathFinder.HauteurGrille := hGrille;
  248. PathFinder.noeudDepart := noeudDepart;
  249. PathFinder.noeudArrivee := noeudArrivee;
  250. PathFinder.QuePremiereEtape := cbPremiereEtape.IsChecked;
  251. PathFinder.AutoriserDeplacementDiagonal := cbDiagonale.IsChecked;
  252. if cbModeCout.IsChecked then
  253. PathFinder.Mode := TGBEPathFinderMode.coutMinimum
  254. else
  255. PathFinder.Mode := TGBEPathFinderMode.deplacementsMinimum;
  256. // Réinitialise les celleules de la grille qui ne sont ni les cases de départ et d'arrivée, ni les obstacles
  257. for iRectangle in layGrille.Children do
  258. begin
  259. unNoeud.position := getPoint(iRectangle.tag);
  260. if (not(PathFinder.listeNoeudsObstacles.ContainsKey(unNoeud.position))) and
  261. (not(unNoeud.position = PathFinder.noeudArrivee.position)) and
  262. (not(unNoeud.position = PathFinder.noeudDepart.position)) then
  263. begin
  264. (iRectangle as TRectangle).Fill.Gradient.Color := TAlphaColorRec.White;
  265. ((iRectangle as TRectangle).Children[0] as TLabel).Text := '';
  266. ((iRectangle as TRectangle).Children[1] as TText).Text := '';
  267. ((iRectangle as TRectangle).Children[2] as TText).Text := '';
  268. end;
  269. end;
  270. if PathFinder.RechercherChemin then
  271. begin
  272. dessinerResultat; // Si on a trouvé un chemin, on le dessine
  273. end
  274. else
  275. lblInfos.Text := 'No path found';
  276. end;
  277. // Création d'une nouvelle grille
  278. procedure TFormPathfinder.btnCreerGrilleClick(Sender: TObject);
  279. begin
  280. rectangleModele.parent := fFormPathfinder;
  281. layGrille.DeleteChildren;
  282. rectangleModele.parent := layGrille;
  283. creerGrille;
  284. initialiserGrille;
  285. end;
  286. // Gestion du clic sur les cellules pour définir ou non la cellule comme obstacle
  287. procedure TFormPathfinder.celluleClick(Sender: TObject);
  288. var
  289. noeudObstacle: TGBENoeud;
  290. begin
  291. noeudObstacle.position := getPoint((Sender as TRectangle).tag);
  292. if PathFinder.listeNoeudsObstacles.ContainsKey(noeudObstacle.position) then
  293. begin
  294. PathFinder.listeNoeudsObstacles.Remove(noeudObstacle.position);
  295. mettreAJourCase(noeudObstacle.position, TAlphaColorRec.White, 0, 0);
  296. end
  297. else
  298. begin
  299. PathFinder.listeNoeudsObstacles.Add(noeudObstacle.position, noeudObstacle);
  300. mettreAJourCase(noeudObstacle.position, TAlphaColorRec.Darkslategrey, 0, 0);
  301. end;
  302. end;
  303. // Permet de récupérer les coordonnées en X et Y d'une cellule de la grille en fonction de son indice
  304. function TFormPathfinder.getPoint(indice: integer): TPoint;
  305. begin
  306. result.x := indice mod lGrille;
  307. result.y := indice div lGrille;
  308. end;
  309. // Permet de récupérer l'indice d'une cellule depuis ses coordonnées X et Y
  310. function TFormPathfinder.getIndice(point: TPoint): integer;
  311. begin
  312. result := point.y * lGrille + point.x;
  313. end;
  314. // Permet de dessiner le chemin trouvé
  315. procedure TFormPathfinder.dessinerResultat;
  316. var
  317. coutDeplacement: integer;
  318. point: TPoint;
  319. begin
  320. coutDeplacement := 0;
  321. for point in PathFinder.listeChemin.Keys do
  322. begin
  323. mettreAJourCase(PathFinder.listeChemin.Items[point].position,
  324. TAlphaColorRec.Cyan, PathFinder.listeChemin.Items[point].coutDeplacement,
  325. PathFinder.listeChemin.Items[point].heuristique);
  326. coutDeplacement := coutDeplacement + PathFinder.listeChemin.Items[point]
  327. .coutDeplacement;
  328. end;
  329. lblInfos.Text := 'The shortest path is represented by the blue boxes' +
  330. sLineBreak + sLineBreak + 'Move : ' + (PathFinder.listeChemin.Count - 1)
  331. .ToString + sLineBreak + 'Cost : ' + coutDeplacement.ToString;
  332. end;
  333. end.