Advertisement
WarPie90

Maze Challenge: Heuristic custom BFS

Jul 9th, 2023 (edited)
1,629
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Delphi 4.37 KB | None | 0 0
  1. program MazeSolver;
  2. {$I SRL/OSR.simba}
  3. {$R-}
  4.  
  5.  
  6. // home brewed search structure for the heuristic BFS lookup
  7. // maintains order. High cost of insertion O(n).
  8. type
  9.   TPathStruct = record
  10.     node: TPoint;
  11.     hdist: Double;
  12.     rdist: Double;
  13.   end;
  14.  
  15.   TSearchArray = record
  16.     lo, hi: Int32;
  17.     data: array of TPathStruct;
  18.   end;
  19.  
  20.  
  21. procedure TSearchArray.Init(sz: Int32);
  22. begin
  23.   SetLength(Self.data, sz);
  24.  
  25.   Self.hi := -1;
  26.   Self.lo := 0;
  27. end;
  28.  
  29. // find the current value or the closest value in the array.
  30. // Time complexity: O(log n)
  31. function TSearchArray.SearchClosest(value: Double): Int32;
  32. var
  33.   l, h: Int32;
  34. begin
  35.   l := Self.Lo;
  36.   h := Self.Hi;
  37.  
  38.   while l <= h do
  39.   begin
  40.     Result := (l + h) div 2;
  41.     if Self.Data[Result].hdist < value then
  42.       h := Result - 1
  43.     else if Self.Data[Result].hdist > value then
  44.       l := Result + 1
  45.     else
  46.       Exit(Result);
  47.   end;
  48. end;
  49.  
  50. // Insert the a value into it's correct position by searching.
  51. // Time complexity: O(n)
  52. // Average complexity for a maze-solver is O(log n).
  53. function TSearchArray.Insert(node:TPoint; hdist, rdist: Double): Int32;
  54. var
  55.   idx,i: Int32;
  56. begin
  57.   idx := Self.SearchClosest(hdist);
  58.   if Self.Data[idx].hdist > hdist then Inc(idx);
  59.  
  60.   if (self.hi+1-idx) > 0 then
  61.     Move(Self.data[idx], Self.data[idx+1], (self.hi+1-idx)*(SizeOf(TPathStruct)));
  62.  
  63.   Self.data[idx].hdist := hdist;
  64.   Self.data[idx].rdist := rdist;
  65.   Self.data[idx].node  := node;
  66.  
  67.   Inc(Self.hi);
  68. end;
  69.  
  70. // Pop the largest or smallest value depending on insertion order
  71. // Time complexity: O(1)
  72. procedure TSearchArray.Pop(out node:TPoint; out hdist, rdist: Double);
  73. begin
  74.   node  := self.data[self.hi].node;
  75.   hdist := self.data[self.hi].hdist;
  76.   rdist := self.data[self.hi].rdist;
  77.  
  78.   Dec(Self.Hi);
  79. end;
  80.  
  81. function TSearchArray.AsString(): String;
  82. var
  83.   tmp: array of TPathStruct;
  84. begin
  85.   tmp := self.data;
  86.   SetLength(tmp, self.hi+1);
  87.   Result := ToString(tmp);
  88. end;
  89.  
  90.  
  91. procedure GetAdjacent(var adj:TPointArray; n:TPoint; EightWay:Boolean);
  92. begin
  93.   adj[0] := Point(n.x-1,n.y);
  94.   adj[1] := Point(n.x,n.y-1);
  95.   adj[2] := Point(n.x+1,n.y);
  96.   adj[3] := Point(n.x,n.y+1);
  97.   if EightWay then
  98.   begin
  99.     adj[4] := Point(n.x-1,n.y-1);
  100.     adj[5] := Point(n.x+1,n.y+1);
  101.     adj[6] := Point(n.x-1,n.y+1);
  102.     adj[7] := Point(n.x+1,n.y-1);
  103.   end;
  104. end;
  105.  
  106.  
  107. function SolveMaze(Start, Stop: TPoint; Img: TMufasaBitmap): TPointArray;
  108. var
  109.   score,hdist: Double;
  110.   i,w,h: Int32;
  111.   arr: TSearchArray;
  112.   map: TSingleMatrix;
  113.   adj: TPointArray;
  114.   p,q: TPoint;
  115.  
  116.   bmp: TMufasaBitmap;
  117. begin
  118.   SetLength(adj, 4);
  119.   arr.Init(img.GetWidth() * img.GetHeight());
  120.   arr.Insert(start, Sqr(start.x-stop.x)+Sqr(start.y-stop.y), 0);
  121.  
  122.   w := img.GetWidth() - 1;
  123.   h := img.GetHeight() - 1;
  124.  
  125.   map.SetSize(w+1, h+1);
  126.  
  127.   // heuristical forward search for goal
  128.   while arr.hi >= 0 do
  129.   begin
  130.     arr.Pop(p, hdist, score);
  131.     if p = stop then break;
  132.     GetAdjacent(adj, p, Length(adj)=8);
  133.     for q in adj do
  134.       if img.PointInBitmap(q.x,q.y) and (img.GetPixel(q.x, q.y) = $FFFFFF)  then
  135.       begin
  136.         img.SetPixel(q.x,q.y, $666666);
  137.         arr.Insert(q, score + Sqr(q.x-stop.x)+Sqr(q.y-stop.y), score+1);
  138.         map[q.y, q.x] := score+1; //<--- for simple backtrace
  139.       end;
  140.   end;
  141.  
  142.   // backtrace towards 0 distance from stop using the distance map
  143.   // I backtrace 8-way
  144.   SetLength(adj, 8);
  145.   score := map[stop.y,stop.x];
  146.   Result += p;
  147.   while score > 1 do
  148.   begin
  149.     GetAdjacent(adj, p, Length(adj)=8);
  150.     for q in adj do
  151.       if InRange(q.x, 0, w) and InRange(q.y, 0, h) and
  152.          (map[q.y, q.x] < score) and (map[q.y, q.x] > 0) then
  153.       begin
  154.         score := map[q.y, q.x];
  155.         p := q;
  156.       end;
  157.     Result += p;
  158.   end;
  159.   Result += start;
  160.  
  161.   //bmp.Init();
  162.   //bmp.DrawMatrix(map);
  163.   //bmp.Debug();
  164. end;
  165.  
  166.  
  167. var
  168.   start := Point(5,0);
  169.   stop  := Point(635,480);
  170.  
  171.   img, im2: TMufasaBitmap;
  172.   TPA,white,black,path1,path2: TPointArray;
  173.   t:Double;
  174. begin
  175.   img.Init();
  176.   img.LoadFromFile('images\maze.png');
  177.  
  178.   t := PerformanceTimer;
  179.   begin
  180.     img.FindColors(black, $0);
  181.     img.DrawTPA(black.Grow(2),0);
  182.     path1 := SolveMaze(start, stop, img);
  183.   end;
  184.   WriteLn(PerformanceTimer - t, 'ms');
  185.  
  186.   WriteLn('Length: ', Length(path1));
  187.  
  188.   img.DrawClear($FFFFFF);
  189.   img.DrawTPA(black, 0);
  190.   img.DrawTPA(path1,$00FF00);
  191.  
  192.   img.Debug();
  193.   img.Free();
  194. end.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement