Advertisement
WarPie90

Heap Queue - A-Star

Dec 12th, 2013
479
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Delphi 6.34 KB | None | 0 0
  1. program new;
  2.  
  3. type
  4.   _TOpenItem = record
  5.     Loc:TPoint;
  6.     Weight:Integer;
  7.   end;
  8.   _TOpenSet = array of _TOpenItem;
  9.  
  10.   _AStarData = record
  11.     open, closed:Boolean;
  12.     scoreA, scoreB: Integer;
  13.     Trace:TPoint;
  14.     Isset:Boolean;
  15.   end;
  16.   _AStarDataArr = array of array of _AStarData;
  17.  
  18.  
  19. // A heap-structure based on Python's heap implementation.
  20. procedure _TOpenSet._siftUp(pos: Integer);
  21. var
  22.   endpos, startpos, childpos, rightpos: Integer;
  23.   newitem: _TOpenItem;
  24. begin
  25.   endpos := Length(Self);
  26.   startpos := pos;
  27.   newitem := Self[pos];
  28.   // Move the smaller child up until hitting a leaf.
  29.   childpos := 2 * pos + 1;    // leftmost child
  30.   while (childpos < endpos) do
  31.   begin
  32.     // Set childpos to index of smaller child.
  33.     rightpos := childpos + 1;
  34.     if (rightpos < endpos) and (Self[childpos].Weight >= Self[rightpos].Weight) then
  35.       childpos := rightpos;
  36.     // Move the smaller child up.
  37.     Self[pos] := Self[childpos];
  38.     pos := childpos;
  39.     childpos := 2 * pos + 1;
  40.   end;
  41.   // This (`pos`) node/leaf is empty. So we can place "newitem" in here, then
  42.   // push it up to its final place (by sifting its parents down).
  43.   Self[pos] := newitem;
  44.   Self._siftDown(startpos, pos);
  45. end;
  46.  
  47. // Follow the path to the root, moving parents down until finding a place newitem `pos` fits.
  48. procedure _TOpenSet._siftDown(startpos, pos: Integer);
  49. var
  50.   parentpos: Integer;
  51.   parent,newitem: _TOpenItem;
  52. begin
  53.   newitem := Self[pos]
  54.   while pos > startpos do
  55.   begin
  56.     parentpos := (pos - 1) shr 1;
  57.     parent := Self[parentpos];
  58.     if (newitem.Weight < parent.Weight) then
  59.     begin
  60.       Self[pos] := parent;
  61.       pos := parentpos;
  62.       continue;
  63.     end;
  64.     Break;
  65.   end;
  66.   Self[pos] := newitem;
  67. end;
  68.  
  69. // Push the item onto heap, maintaining the heap invariant
  70. procedure _TOpenSet.HeapPush(Item:_TOpenItem);
  71. var L:Integer;
  72. begin
  73.   L := Length(Self);
  74.   SetLength(Self, L+1);
  75.   Self[L] := item;
  76.   Self._siftDown(0, L);
  77. end;
  78.  
  79. function _TOpenSet.Pop(): _TOpenItem;
  80. begin
  81.   Result := Self[High(Self)];
  82.   SetLength(Self, High(Self));
  83. end;
  84.  
  85. // Pop the smallest item off the heap, maintaining the heap invariant.
  86. function _TOpenSet.HeapPop(): _TOpenItem;
  87. var
  88.   lastelt:_TOpenItem;
  89. begin
  90.   lastelt := Self.pop();
  91.   if Length(self) > 0 then
  92.   begin
  93.     Result := Self[0];
  94.     Self[0] := lastelt;
  95.     Self._siftup(0);
  96.   end else
  97.     Result := lastelt;
  98. end;
  99.  
  100.  
  101.  
  102. // Calculate the distance between p1, and p2 using Squared euclidean.
  103. function SqEuclidean(p1, p2:TPoint): Integer;
  104. begin
  105.   Result := Round(Sqr(p1.x-p2.x) + Sqr(p1.y-p2.y));
  106. end;
  107.  
  108.  
  109. // Fills AdjArr with the 4 adjacent points
  110. procedure GetAdjacent(var AdjArr:TPointArray; Pt:TPoint);
  111. begin
  112.   AdjArr[0] := Point(Pt.x-1,pt.y);
  113.   AdjArr[2] := Point(Pt.x+1,pt.y);
  114.   AdjArr[1] := Point(Pt.x,pt.y-1);
  115.   AdjArr[3] := Point(Pt.x,pt.y+1);
  116. end;
  117.  
  118.  
  119.  
  120. (*
  121.  Walks the path we came from backwards from current (goal) until it can't walk
  122.  further. When it reaches "the end" it will return the path reversed (Start->Goal).
  123. *)
  124. function BacktracePath(Paths:_AStarDataArr; Curr:TPoint): TPointArray;
  125. var L:Integer;
  126. begin
  127.   SetLength(Result, 1);
  128.   Result[0] := Curr;
  129.   L := 1;
  130.   while True do
  131.   begin
  132.     Curr := paths[curr.y][curr.x].Trace;
  133.     SetLength(Result, L+1);
  134.     Result[L] := Curr;
  135.     Inc(L);
  136.     if (paths[curr.y][curr.x].Isset = False) then Break;
  137.   end;
  138.   InvertTPA(Result); //Reverse the list (bad name)
  139. end;
  140.  
  141.  
  142.  
  143. (*
  144.  AStar search algorithm modified to explicity work with Image-maze.
  145.  This implementation is mostly ment as an example, there is multiple ways
  146.  to notably speed it up, and possibly simplify it.
  147. *)
  148. function AStar(Maze: Integer; Start, Goal:TPoint; Color:Integer): TPointArray;
  149. var
  150.   hsize,score,i,j,W,H: Integer;
  151.   Data: _AStarDataArr;
  152.   OpenSet: _TOpenSet;
  153.   HPt: _TOpenItem;
  154.   Adj,Pt:TPoint;
  155.   Neighbors:TPointArray;
  156. begin
  157.   SetLength(Neighbors, 4);
  158.   GetBitmapSize(Maze, W,H);
  159.   // init 2D array to keep track of data
  160.   SetLength(Data, H, W);
  161.   // start distace Start -> Goal.
  162.   Data[start.y][start.x].ScoreB := SqEuclidean(Start, Goal);
  163.   // init the openset with Start-coord
  164.   SetLength(OpenSet, 1);
  165.   OpenSet[0].Loc := start;
  166.   OpenSet[0].Weight := Data[start.y][start.x].ScoreB;
  167.   HSize := 1;
  168.  
  169.   Dec(W); Dec(H);
  170.   //while openset is not empty
  171.   while (HSize > 0) do
  172.   begin
  173.     // pop the smallest item from openset
  174.     HPt := OpenSet.HeapPop(); //PopMin(OpenSet);
  175.     Pt := HPt.Loc;
  176.     Dec(HSize);
  177.     //if current = goal then return path.
  178.     if (Pt.x = Goal.x) and (Pt.y = Goal.y) then
  179.     begin
  180.       Result := BacktracePath(Data, Goal);
  181.       Exit;
  182.     end;
  183.     // "remove" current point
  184.     Data[Pt.y][Pt.x].Open := False;
  185.     Data[Pt.y][Pt.x].Closed := True;
  186.  
  187.     // for each neighbor check if we can/should walk it
  188.     GetAdjacent(Neighbors, Pt)
  189.     for j:=0 to 3 do
  190.     begin
  191.       Adj := Neighbors[j];
  192.       // out of range, or wall
  193.       if not(InRange(Adj.x, 0, W) and InRange(Adj.y, 0, H)) or
  194.          (FastGetPixel(Maze, Adj.x, Adj.y) <> Color) then
  195.         Continue;
  196.  
  197.       Score := (Data[Pt.y][Pt.x].ScoreA + 1);
  198.       if (Data[Adj.y][Adj.x].Closed = True) and (Score >= Data[Adj.y][Adj.x].ScoreA) then
  199.         Continue;
  200.  
  201.       if ((Data[Adj.y][Adj.x].Open = False) or
  202.          (Score < Data[Adj.y][Adj.x].ScoreA)) then
  203.       begin
  204.         Data[Adj.y][Adj.x].Trace := Pt;     //Used when we backtrace.
  205.         Data[Adj.y][Adj.x].Isset := True;   //Used when we backtrace.
  206.         // keep track over scores || used to evaluate best direction.
  207.         Data[Adj.y][Adj.x].ScoreA := Score;
  208.         Data[Adj.y][Adj.x].ScoreB := (Data[Adj.y][Adj.x].ScoreA + SqEuclidean(Adj, Goal));
  209.         // if not already added to the openset then add it and Increase HSize
  210.         if (Data[Adj.y][Adj.x].Open = False) then
  211.         begin
  212.           HPt.Loc := Adj;
  213.           HPt.Weight := Data[Adj.y][Adj.x].ScoreB;
  214.           OpenSet.HeapPush(HPt);
  215.           Data[Adj.y][Adj.x].Open := True;
  216.           Inc(HSize);
  217.         end;
  218.       end;
  219.     end;
  220.   end;
  221. end;
  222.  
  223.  
  224.  
  225. //------------
  226.  
  227. var
  228.   bmp, W, H: Integer;
  229.   Path:TPointArray;
  230. begin
  231.   bmp := LoadBitmap(AppPath + 'tests/maze.png');
  232.   Path := AStar(bmp, Point(0,44), Point(148,0), $FFFFFF);
  233.   DrawTPABitmap(bmp, path, $FF);
  234.  
  235.   GetBitmapSize(BMP, W,H);
  236.   DisplayDebugImgWindow(W,H);
  237.   DrawBitmapDebugImg(bmp);
  238.   FreeBitmap(bmp);
  239. end.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement