marpis
2010-07-31 14:56:02
I made this little route finder based on Dijkstra but this one is really slow. Takes seconds to find the path. But the main thing is that it really does find a short path. What makes it slow is the continuous PointInTPA, but I couldn't figure a way round that.
I think we could try to make this a bit faster and then use it in MSI. It will make adding new areas quite painful, but would allow us to easily handle the existing ones. It would find a route from anywhere to anywhere, thus allowing switching scripts. :)
How does it work:
the TPA you are inspecting as a map must be solid! No gaps, or it will not work!
Test by running this on Simba:
EDIT:Hmm I think I try making is also work with NE, SE, SW, NW but I hope it doesn't make this way too slow.
EDIT: Done! Now checks all 8 neighbors. Will soon make a RS pathmaker.
EDIT: Dang… wasn't as easy as I thought…
I think we could try to make this a bit faster and then use it in MSI. It will make adding new areas quite painful, but would allow us to easily handle the existing ones. It would find a route from anywhere to anywhere, thus allowing switching scripts. :)
How does it work:
the TPA you are inspecting as a map must be solid! No gaps, or it will not work!
- Create a NodeArray from PointArray, which allows us to see which of the point's neighbors exist. Store info in booleans.
- Start inspecting all neighbors of CurrentNode. If we haven't already been on neighbor X, take it into consideration.
- Compare distances between the next node candidates (current's neighbors) and our goal point. Choose closest one as next node.
- Repeat.
Test by running this on Simba:
program new;
{.include srl/srl.scar}
{.include srl/srl/misc/debug.scar}
const
NORTH = 0;
EAST = 1;
SOUTH = 2;
WEST = 3;
NORTHEAST = 4;
SOUTHEAST = 5;
SOUTHWEST = 6;
NORTHWEST = 7;
type
Node = record
X, Y: integer;
N, E, S, W: boolean;
NE, SE, SW, NW: boolean;
end;
NodeArray = array of Node;
function AnalyzeNodes(TPA: TPointArray): NodeArray;
var
i, H: integer;
P: TPoint;
begin
H := High(TPA);
SetLength(Result, H + 1);
for i := 0 to H do
begin
// North
P.X := TPA[i].X;
P.Y := TPA[i].Y - 1;
Result[i].X := P.X;
Result[i].Y := P.Y;
Result[i].N := PointInTPA(P, TPA);
// East
P.X := TPA[i].X +1;
P.Y := TPA[i].Y;
Result[i].X := P.X;
Result[i].Y := P.Y;
Result[i].E := PointInTPA(P, TPA);
// South
P.X := TPA[i].X;
P.Y := TPA[i].Y + 1;
Result[i].X := P.X;
Result[i].Y := P.Y;
Result[i].S := PointInTPA(P, TPA);
// West
P.X := TPA[i].X - 1;
P.Y := TPA[i].Y;
Result[i].X := P.X;
Result[i].Y := P.Y;
Result[i].W := PointInTPA(P, TPA);
// North-East
P.X := TPA[i].X + 1;
P.Y := TPA[i].Y - 1;
Result[i].X := P.X;
Result[i].Y := P.Y;
Result[i].NE := PointInTPA(P, TPA);
// South-East
P.X := TPA[i].X + 1;
P.Y := TPA[i].Y + 1;
Result[i].X := P.X;
Result[i].Y := P.Y;
Result[i].SE := PointInTPA(P, TPA);
// South-West
P.X := TPA[i].X - 1;
P.Y := TPA[i].Y + 1;
Result[i].X := P.X;
Result[i].Y := P.Y;
Result[i].SW := PointInTPA(P, TPA);
// North-West
P.X := TPA[i].X - 1;
P.Y := TPA[i].Y - 1;
Result[i].X := P.X;
Result[i].Y := P.Y;
Result[i].NW := PointInTPA(P, TPA);
end;
end;
function PointToNode(P: TPoint; Nodes: NodeArray): Node;
var
i, H: integer;
begin
H := High(Nodes);
for i := 0 to H do
if (Nodes[i].X = P.X) and (Nodes[i].Y = P.Y) then
begin
Result := Nodes[i];
Exit;
end;
end;
function FindNodeInArray(X, Y: integer; Nodes: NodeArray): Node;
var
i, H: integer;
begin
H := High(Nodes);
for i := 0 to H do
if (Nodes[i].X = X) and (Nodes[i].Y = Y) then
begin
Result := Nodes[i];
Exit;
end;
Result.X := -1;
Result.Y := -1;
end;
function FindRoute(P1, P2: TPoint; TPA: TPointArray): TPointArray;
var
Nodes: NodeArray;
Visited: TPointArray;
CurrentNode: Node;
Finished: boolean;
i, ii, tmpMIN, N: integer;
Dists: array [0..7] of Integer;
begin
Nodes := AnalyzeNodes(TPA);
CurrentNode := PointToNode(P1, Nodes);
SetLength(Result, 9999);
SetLength(Visited, 9999);
repeat
Dists[NORTH] := 9999999;
Dists[EAST] := 9999999;
Dists[SOUTH] := 9999999;
Dists[WEST] := 9999999;
Dists[NORTHEAST] := 9999999;
Dists[SOUTHEAST] := 9999999;
Dists[SOUTHWEST] := 9999999;
Dists[NORTHWEST] := 9999999;
if CurrentNode.N then
if not PointInTPA(Point(CurrentNode.X, CurrentNode.Y - 1), Visited) then
Dists[NORTH] := Distance(CurrentNode.X, CurrentNode.Y - 1, P2.X, P2.Y);
if CurrentNode.E then
if not PointInTPA(Point(CurrentNode.X + 1, CurrentNode.Y), Visited) then
Dists[EAST] := Distance(CurrentNode.X + 1, CurrentNode.Y, P2.X, P2.Y);
if CurrentNode.S then
if not PointInTPA(Point(CurrentNode.X, CurrentNode.Y + 1), Visited) then
Dists[SOUTH] := Distance(CurrentNode.X, CurrentNode.Y + 1, P2.X, P2.Y);
if CurrentNode.W then
if not PointInTPA(Point(CurrentNode.X - 1, CurrentNode.Y), Visited) then
Dists[WEST] := Distance(CurrentNode.X - 1, CurrentNode.Y, P2.X, P2.Y);
if CurrentNode.NE then
if not PointInTPA(Point(CurrentNode.X + 1, CurrentNode.Y - 1), Visited) then
Dists[NORTHEAST] := Distance(CurrentNode.X + 1, CurrentNode.Y - 1, P2.X, P2.Y);
if CurrentNode.SE then
if not PointInTPA(Point(CurrentNode.X + 1, CurrentNode.Y + 1), Visited) then
Dists[SOUTHEAST] := Distance(CurrentNode.X + 1, CurrentNode.Y + 1, P2.X, P2.Y);
if CurrentNode.SW then
if not PointInTPA(Point(CurrentNode.X - 1, CurrentNode.Y + 1), Visited) then
Dists[SOUTHWEST] := Distance(CurrentNode.X + 1, CurrentNode.Y - 1, P2.X, P2.Y);
if CurrentNode.NW then
if not PointInTPA(Point(CurrentNode.X - 1, CurrentNode.Y - 1), Visited) then
Dists[NORTHWEST] := Distance(CurrentNode.X - 1, CurrentNode.Y - 1, P2.X, P2.Y);
tmpMIN := NORTH;
for ii := EAST to NORTHWEST do
if (Dists[ii] < Dists[tmpMIN]) then
tmpMIN := ii;
Visited[N] := Point(CurrentNode.X, CurrentNode.Y);
case tmpMIN of
NORTH : CurrentNode := FindNodeInArray(CurrentNode.X, CurrentNode.Y - 1, Nodes);
EAST : CurrentNode := FindNodeInArray(CurrentNode.X + 1, CurrentNode.Y, Nodes);
SOUTH : CurrentNode := FindNodeInArray(CurrentNode.X, CurrentNode.Y + 1, Nodes);
WEST : CurrentNode := FindNodeInArray(CurrentNode.X - 1, CurrentNode.Y, Nodes);
NORTHEAST: CurrentNode := FindNodeInArray(CurrentNode.X + 1, CurrentNode.Y - 1, Nodes);
SOUTHEAST: CurrentNode := FindNodeInArray(CurrentNode.X + 1, CurrentNode.Y + 1, Nodes);
SOUTHWEST: CurrentNode := FindNodeInArray(CurrentNode.X - 1, CurrentNode.Y + 1, Nodes);
NORTHWEST: CurrentNode := FindNodeInArray(CurrentNode.X - 1, CurrentNode.Y - 1, Nodes);
end;
Result[N] := Point(CurrentNode.X, CurrentNode.Y);
Inc(N);
Finished := Dists[tmpMIN] <= 1;
//WriteLn('Current='+ToStr(N)+', Dist='+ToStr(Dists[tmpMIN])+', tmpMIN='+ToStr(tmpMIN));
until(Finished);
SetLength(Result, N);
ClearDoubleTPA(Result);
end;
////////////////////////////////////////////////////////////////////////////////
// THIS IS JUST FOR YOU TO TEST
var
aTPA: array [0..1] of tpointarray;
t, x, y, N: integer;
begin
SetLength(aTPA[0], 9999);
for x := 10 to 40 do
for y := 15 to 40 do
begin
aTPA[0][N] := Point(x, y);
Inc(N);
end;
for x := 41 to 70 do
for y := 25 to 50 do
begin
aTPA[0][N] := point(x, y);
Inc(N);
end;
for x := 71 to 100 do
for y := 10 to 40 do
begin
aTPA[0][N] := point(x, y);
Inc(N);
end;
SetLength(aTPA[0], N);
//DebugTPA(aTPA[0], '');
t := GetSystemTime;
aTPA[1] := FindRoute(aTPA[0][0], aTPA[0][HIGH(aTPA[0])], aTPA[0]);
t := GetSystemTime - t;
WriteLn('Time : '+ToStr(t));
DebugATPA(aTPA, '');
end.
EDIT:
EDIT: Done! Now checks all 8 neighbors. Will soon make a RS pathmaker.
EDIT: Dang… wasn't as easy as I thought…