mixster
2009-10-06 02:09:12
Path finder, much like my previous A* stuff, but without heuristic. I've tried to streamline it and speed it up as much as I can - there's still more and I can always try experimental stuff. This is mainly just for Ray though, so he can laugh at my bad coding skills :)
Excuse the slow bitmap loading, but I know how much people hate downloading bitmaps/having large bitmap strings to use with this stuffs and it should normally be fine, but atm pastebin seems to be a bit slow, so may take a bit :)
It is also somewhat badly commented, so if you do read it, then they should provide a little help for understanding my twisted logic.
Note that it does also have my A* stuff in it, commented, and you can uncomment if you have the accompanying plugin to see the difference in the paths. Likewise, if you have the benchmarking bitmap I think I gave out, then you can use that rather than my silly pastebin parser.
Excuse the slow bitmap loading, but I know how much people hate downloading bitmaps/having large bitmap strings to use with this stuffs and it should normally be fine, but atm pastebin seems to be a bit slow, so may take a bit :)
It is also somewhat badly commented, so if you do read it, then they should provide a little help for understanding my twisted logic.
program PathFinding;
type
TNode = record
tp: TPoint;
prt: Integer;
dis: Integer;
s,a: Boolean;
end;
function TPDist(a, b: TPoint): Integer;
begin
Result := Trunc(Sqrt(Sqr(a.x - b.x) + Sqr(a.y - b.y)) * 10.0);
end;
function Dijkstra(st, en: TPoint; input: array of TPoint; inDis: Integer): array of TPoint;
var
h, i, t, d: Integer;
n: array of TNode;
a, s: TIntegerArray; // (Available, Set, both) node indexes
nh, ah, sh: Integer;
lv, li: Integer; // lowest (value, index)
begin
SetLength(Result, 0);
h := Length(input) + 1;
SetLength(input, h);
input[h - 1] := st;
SortTPAFrom(input, st); // *Should* improve performance by leaving sorted this way
SetLength(n, h);
SetLength(a, h);
SetLength(s, h);
h := h - 1;
nh := 0;
ah := -1;
sh := 0;
for i := 0 to h do
begin
n[i].tp := input[i];
n[i].s := False;
n[i].a := False;
end;
n[0].prt := -1;
n[0].dis := 0;
n[0].s := True;
n[0].a := True;
s[0] := 0;
inDis := inDis * 10;
lv := inDis + 10;
li := 0;
repeat
{ begin newly discovered node search }
for i := 0 to h do
begin
if n[i].s then
continue;
d := TPDist(n[s[sh]].tp, n[i].tp);
// Get distance from current node to last set node
if d > inDis then // if it's not in range
begin
//Writeln(IntToStr(d) + '/' + IntToStr(inDis));
continue; // go onto next point
end;
d := n[s[sh]].dis + d; // Get distance to point using current nodes path
if n[i].a then // if point is already added
begin
if d > n[a[i]].dis then // If it's longer path
d := n[a[i]].dis
else // else if it's a shorter path
begin
n[a[i]].dis := d;
n[a[i]].prt := s[sh];
end;
if d < lv then // If it is the current, 'closet' unset node
begin
lv := d; // Reassign lv and li
for li := 0 to ah do // loop through to find out which a[] it links with
if i = a[li] then // if it's the node
break; // stop - li retains correct value
end;
end
else // if unadded, setup a couple of things
begin
ah := ah + 1;
a[ah] := i; // Add it onto the available node list
n[i].a := True;
n[i].prt := s[sh]; // and assign the node values
n[i].dis := d;
if d < lv then // If it is the current, 'closet' unset node
begin
lv := d; // Reassign lv and li to this node
li := ah;
end;
end;
end;
{ End of node searching }
sh := sh + 1;
//Writeln(IntToStr(li) + '/' + IntToStr(h));
s[sh] := a[li]; // Add the 'closest' unset node to the set list
n[a[li]].s := True; // Set the node to set
Swap(a[li], a[ah]); // Switch it to the end
ah := ah - 1; // and remove it from the available list by cutting it off
if ah = -1 then // if we have run out of open nodes
begin
Writeln('Unable to find path.');
//exit; // then there is no path
break;
end;
lv := n[a[0]].dis; // Set default lowest value and lowest index
li := a[0];
if TPDist(en, n[s[sh]].tp) <= inDis then
break; // then stop searching
until false;
SetLength(Result, h + 3); // +1 due to high, +2 due to origin and destination point
h := 0;
Result[0] := en;
i := s[sh];
t := 1;
repeat
Result[t] := n[i].tp;
t := t + 1;
i := n[i].prt;
until i = -1;
Result[t] := st;
h := t + 1;
SetLength(Result, h);
InvertTPA(Result);
end;
function ParsePastebinBitmap: string;
var
s: string;
begin
Result := '';
Writeln('Getting page - may take a bit');
s := GetPage('http://pastebin.com/f485ae5c0');
Writeln('Got page! Now parsing');
Delete(s, 1, PosEx('beNrsvV+MHfd156mZx', s, Pos('beNrsvV+MHfd156mZx', s) + 1) - 1);
Delete(s, Pos('=', s) + 1, Length(s));
s := Replace(s, #13 + #10, '');
if s = '' then
begin
Writeln('Parsing failed, try again and double check pastebin link is valid');
TerminateScript;
end;
Result := s;
end;
var
b, t, w, h, c, i: Integer;
pts, temp: TPointArray;
ptar: array of TPointArray;
beg, fin: TPoint;
s: string;
begin
Writeln('Begin');
w := 745;
h := 450;
DisplayDebugImgWindow(w, h);
t := GetSystemTime;
try
//b := LoadBitmap(AppPath + 'pics/benchmark.bmp');
b := BitmapFromString(w, h, ParsePastebinBitmap);
except
Writeln('Loading epicly large bitmap failed :<');
TerminateScript;
end;
t := GetSystemTime - t;
Writeln('Bitmap loading took ' + IntToStr(t) + 'ms');
SetTargetBitmap(b);
GetDebugCanvas.Rectangle(0, 0, w, h);
c := BitmapFromString(w, h, '');
FastDrawTransparent(0, 0, b, c);
beg := IntToPoint(110, 330);
SafeCopyCanvas(GetBitmapCanvas(c), GetDebugCanvas, 0, 0, w, h, 0, 0, w, h);
Writeln('About to perform search...');
FindColorsTolerance(pts, GetColor(beg.x, beg.y), 0, 0, w, h, 5);
SetLength(temp, 0);
fin := pts[Random(Length(pts))];
ptar := TPAtoATPA(pts, 2);
SetLength(pts, Length(ptar));
for t := 0 to High(ptar) do
begin
pts[t] := MiddleTPA(ptar[t]);
FastSetPixel(c, pts[t].x, pts[t].y, clRed);
end;
GetBitmapCanvas(c).Brush.Color := clAqua;
GetBitmapCanvas(c).Ellipse(beg.x - 3, beg.y - 3, beg.x + 3, beg.y + 3);
GetBitmapCanvas(c).Ellipse(fin.x - 3, fin.y - 3, fin.x + 3, fin.y + 3);
SafeCopyCanvas(GetBitmapCanvas(c), GetDebugCanvas, 0, 0, w, h, 0, 0, w, h);
Writeln('Performing A* search now with ' + IntToStr(Length(pts)) + ' points as input...');
t := GetSystemTime;
SortTPAFrom(pts, beg);
SetLength(pts, Length(pts) + 1);
pts[High(pts)] := fin;
AStarCus(temp, pts, 0, High(pts), 4, 4);
if Length(temp) <> 0 then
begin
t := GetSystemTime - t;
Writeln('Success! Path is ' + IntToStr(Length(temp)) + ' nodes long');
GetBitmapCanvas(c).Pen.Color := clFuchsia;
GetBitmapCanvas(c).MoveTo(temp[0].x, temp[0].y);
for i := 0 to High(temp) do
begin
GetBitmapCanvas(c).LineTo(temp[i].x, temp[i].y);
end;
SafeCopyCanvas(GetBitmapCanvas(c), GetDebugCanvas, 0, 0, w, h, 0, 0, w, h);
end
else
begin
t := GetSystemTime - t;
Writeln('Failed');
end;
Writeln('Took ' + IntToStr(t) + 'ms');
Writeln('Performing Dijkstra search now with ' + IntToStr(Length(pts)) + ' points as input...');
t := GetSystemTime;
temp := Dijkstra(beg, fin, pts, 5);
if Length(temp) <> 0 then
begin
t := GetSystemTime - t;
Writeln('Success! Path is ' + IntToStr(Length(temp)) + ' nodes long');
GetBitmapCanvas(c).Pen.Color := clLime;
GetBitmapCanvas(c).MoveTo(temp[0].x, temp[0].y);
for i := 0 to High(temp) do
begin
GetBitmapCanvas(c).LineTo(temp[i].x, temp[i].y);
end;
SafeCopyCanvas(GetBitmapCanvas(c), GetDebugCanvas, 0, 0, w, h, 0, 0, w, h);
end
else
begin
t := GetSystemTime - t;
Writeln('Failed');
end;
Writeln('Took ' + IntToStr(t) + 'ms');
FreeBitmap(b);
FreeBitmap(c);
Writeln('End');
end.Note that it does also have my A* stuff in it, commented, and you can uncomment if you have the accompanying plugin to see the difference in the paths. Likewise, if you have the benchmarking bitmap I think I gave out, then you can use that rather than my silly pastebin parser.