SRL Archive - SRL Snippets

function FindTPACluster(): array of TPointArray


Runaway

2012-05-14 11:51:40

This is an interesting bit of code I dreamt up after I saw beginner5's function. Also check out function FindTPointCluster(): TPointArray if you're looking for a faster, less precise version.

function FindTPACluster(Colors: TIntegerArray; Tol: Integer; SortFrom: TPoint; Filter, Trim: Integer; MinDist, MaxDist: Extended; Strict: Boolean; x1, y1, x2, y2: Integer): array of TPointArray;

You can input as many colors as you need and it will only return clusters it finds where ALL colors specified are within the given distance of each other. This is done by looping through each point for every color found and comparing it to every point of the other found colors. A set of points is only stored in the result array if a point from each color is found within the specified distance of every other color's point. This is really useful for areas that require a high color tolerance, as well as objects, npcs, and I'm sure many others.

E1: I've cleaned up the code a bit and done ALOT of testing. so far it's super accurate and can support up to ~6-8 colors before it starts to lose it's speed. Here are some of my tests, as well as the code I used:

MM Walking:

NPC Finding:

Item Finding:

Usage:

Arr := FindTPACluster(Colors, Tol, SortFrom, FilterArr, TrimArr, 0, MaxDist, True, Area[0], Area[1], Area[2], Area[3]);
SMART_ClearCanvas;
for a := 0 to High(Arr) do
SMART_DrawCircle(False, MiddleTPA(Arr[a]), MaxDist, False, clred);

It's 100% failsafe'd, and it's got a great debug if you have any problems. It currently takes between 200-450 ms to find and parse all of the colors/points. I'd love to see some people test this out in scripts and see how it goes :)

Descripton:

(*
FindTPACluster
~~~~~~~

.. code-block:: pascal

function FindTPACluster(Colors: TIntegerArray; Tol: Integer; SortFrom: TPoint; Filter, Trim: Integer; MinDist, MaxDist: Extended; Strict: Boolean; x1, y1, x2, y2: Integer): array of TPointArray;

Results an array of TPointArray which holds "clusters" of the specified colors in
each TPointArray. It can search for an unlimited number of colors in x1, y1, x2, y2.
* Colors: The colors to be found and grouped depending on proximity.
* Tol: The tolerance on each color.
* SortFrom: The point to sort each TPoint from.
* Filter: The maximum distance between SortFrom and each TPoint. -1 to not use.
* Trim: The maximum length of each TPointArray. -1 to not use.
* MinDist, MaxDist: The minimum and maximum distances that specify how far apart
each color can be in each cluster.
* Strict: True to exit the function if not all of the specified colors are found.

.. note::

by Runaway

Example:

.. code-block:: pascal

Colors := [16514558, 5062187, 4664874];
Arr := FindTPACluster(Colors, 20, Point(MMCX, MMCY), -1, 200, 0, 30, True, MMX1, MMY1, MMX2, MMY2);
for i := 0 to High(Arr) do
begin
x := MiddleTPA(Arr[i]).x;
y := MiddleTPA(Arr[i]).y;
if (not rs_OnMinimap(x, y)) then
Continue;
Mouse(x, y, 3, 3, mouse_left);
if DidClick(True, RandomRange(750, 1250)) then
Break;
end;
*)

Function:

function FindTPACluster(Colors: TIntegerArray; Tol: Integer; SortFrom: TPoint; Filter, Trim: Integer; MinDist, MaxDist: Extended; Strict: Boolean; x1, y1, x2, y2: Integer): array of TPointArray;
var
TPA, Res: TPointArray;
aPoints: array of TPointArray;
Dist: array of Extended;
Which: array of Integer;
hDist, hPoints, hColors: Integer;
Error, Len: Integer;
r, d, t: Integer;
w, ww: Integer;
i, ii, iii: Integer;
Match, Count: Integer;
Duplicate: Boolean;
begin
MarkTime(t);
SetLength(aPoints, Length(Colors));
hColors := High(Colors);
Error := 0;
r := 0;
for i := 0 to hColors do
begin
if (FindColorsTolerance(TPA, Colors[i], x1, y1, x2, y2, Tol)) then
begin
SortTPAFrom(TPA, SortFrom);
if (Filter <> -1) then
FilterPointsDist(TPA, 0, Filter, SortFrom.x, SortFrom.y);
if (Trim <> -1) and (Length(TPA) > Trim) then
SetLength(TPA, Trim);
aPoints[i - Error] := TPA;
end else
begin
if Strict then
begin
Writeln('Error: not enough colors found!');
Exit;
end;
Len := Length(aPoints) - 1;
SetLength(aPoints, Len);
Inc(Error);
end;
if (Error >= hColors) then
begin
Writeln('Error: not enough colors found!');
Exit;
end;
end;
SetLength(Dist, Length(aPoints) - 1);
SetLength(Which, Length(aPoints) - 1);
hPoints := High(aPoints);
hDist := High(Dist);
for i := 0 to hPoints do
begin
Count := 0;
for w := 0 to hDist do
begin
if (hPoints - w = hPoints - i) or (hPoints - w < hPoints - i) then
Which[w] := w + 1
else
Which[w] := w;
end;
for ii := 0 to High(aPoints[i]) do
begin
Match := 0;
for iii := 0 to hDist do
begin
if (Count > High(aPoints[Which[iii]])) then
begin
Inc(Count);
Break;
end;
Dist[iii] := Distance(aPoints[i][ii].x, aPoints[i][ii].y, aPoints[Which[iii]][Count].x, aPoints[Which[iii]][Count].y);
if (Dist[iii] >= MinDist) and (Dist[iii] <= MaxDist) then
Inc(Match)
else
begin
Inc(Count);
Break;
end;
end;
if (Match >= hPoints) then
begin
SetLength(Res, Length(aPoints));
Res[0] := aPoints[i][ii];
for ww := 0 to hDist do
Res[ww + 1] := aPoints[Which[ww]][Count];
SortTPAByX(Res, True);
if (Length(Result) >= 1) then
begin
Duplicate := False;
for d := 0 to High(Result) do
begin
if (Result[d] = Res) then
Duplicate := True;
end;
if Duplicate then
begin
Inc(Count);
Continue;
end;
end;
SetLength(Result, r + 1);
Result[r] := Res;
Inc(r);
Res := [];
end;
Inc(Count);
end;
end;
Writeln('Took: '+IntToStr(TimeFromMark(t))+' ms, found '+IntToStr(Length(Result))+' unique cluster(s)');
end;

Example Debug:

Searching: color 1 of 4...
Found: 119 point(s) of color 5723995
Using: first 100 point(s) of color 5723995
Searching: color 2 of 4...
Found: 100 point(s) of color 9474455
Using: first 100 point(s) of color 9474455
Searching: color 3 of 4...
Found: 68 point(s) of color 13751003
Using: first 68 point(s) of color 13751003
Searching: color 4 of 4...
Found: 113 point(s) of color 9079440
Using: first 100 point(s) of color 9079440
Searching... done
Sorting: array 1 of 4...
Found: 2 new cluster(s)
Sorting: array 2 of 4...
Found: 0 new cluster(s)
Sorting: array 3 of 4...
Found: 36 new cluster(s)
Sorting: array 4 of 4...
Found: 8 new cluster(s)
Sorting... done
Took: 219 ms, found 46 unique cluster(s)
Successfully executed.

If you want to test it out for yourself, I've attached a testing script with debug, paint, and all sorts of options all set up and ready to go. All you have to do is select the area, colors, etc. :)

Abu

2012-05-14 12:01:03

Looks very useful, will be testing in the near future :)


EDIT: I'm an amateur with TPA's, what does the 'From' variable do exactly?
The point to sort each found color from doesn't make sense to me :(

Runaway

2012-05-14 12:10:24

From: abu_jwka
EDIT: I'm an amateur with TPA's, what does the 'From' variable do exactly?
The point to sort each found color from doesn't make sense to me :(


Let's take a look at the example:


Colors := [16514558, 5062187, 4664874];
Arr := FindTPACluster(Colors, 20, Point(45+MMCX, 60+MMCY), 100, 0, 30, MMX1, MMY1, MMX2, MMY2);
for i := 0 to High(Arr) do
begin
x := MiddleTPA(Arr[i]).x;
y := MiddleTPA(Arr[i]).y;
if (not rs_OnMinimap(x, y)) then
Continue;
Mouse(x, y, 3, 3, mouse_left);
if DidClick(True, RandomRange(750, 1250)) then
Break;
end;

From is: Point(45+MMCX, 60+MMCY)

I'm searching the MM in the example, so I chose the From point to be the place around where I want to click. In this case, it's 45 pixels to the right from the MM center, and 60 pixels up from the MM center. FindTPACluster takes this specified point and uses:

SortTPAFrom(TPA, From);

This takes every point in the TPA found by FindColorsTolerance and sorts them in order from closest to From, to farthest away. That way all of the earliest points in the array are the ones closest to where I want to eventually be clicking.

Hope that clears it up :)

Abu

2012-05-14 12:14:14

Yes it does. Thanks :)

Word

2012-05-14 12:46:45

For finding duplicate colours, it seems to use the distance to determine whether to eliminate them or not (correct me if I'm wrong) - or does it actually get the (x,y)?

Ignore this if the latter: could you not have to points equal distances from the testing point with the same colour (or within the tolerance) which would mean they're not actually duplicates?

Runaway

2012-05-14 13:15:43

From: Word
For finding duplicate colours, it seems to use the distance to determine whether to eliminate them or not (correct me if I'm wrong) - or does it actually get the (x,y)?

Ignore this if the latter: could you not have to points equal distances from the testing point with the same colour (or within the tolerance) which would mean they're not actually duplicates?


I'll try to give a quick overview of what happens:


if (Match >= hPoints) then
begin
SetLength(Res, Length(aPoints));
Res[0] := aPoints[i][ii];
for ww := 0 to hDist do
Res[ww + 1] := aPoints[Which[ww]][Count]; // stores one point from each color into the Res[] array
SortTPAByX(Res, True); // sorts the Res[] array by x value from lowest to highest
if (Length(Result) > 1) then // if points have already been stored in the Result[] array
begin
Duplicate := False;
for d := 0 to High(Result) do
begin
if (Result[d] = Res) then // since the Res[] arrays points have been sorted the same way every time, if there is a TPA in Result[] that matches the current Res[] array it is a duplicate
Duplicate := True;
end;
if Duplicate then
begin
if Debug then Writeln('Removed: duplicate points '+ToStr(Res)); // this doesn't actually remove a duplicate, it simply ignores the cluster that was just found above
Inc(Count);
Continue;
end;
end;
SetLength(Result, r + 1);
Result[r] := Res; // if the duplicate check fails, the current Res[] array is stored as a TPA in the Result[] array
Inc(r);
if Debug then Inc(rr);
Res := []; // the Res[] array is then reset
end;

Duplicates are possible because my current sorting system ends up comparing the same points multiple times throughout the process. I'm trying to work out logic to not even compare points that have already been compared, but it seems that it's really difficult :confused:

Runaway

2012-05-14 23:33:14

I've updated the code, done ALOT of testing, and posted all of the pictures on the first post!

I also uploaded a new testing script that is really easy to use :)

cause

2012-05-15 13:18:45

This is 100% awesome! I'll rep you when I get to a computer :)

Abu

2012-05-19 11:05:02

Just started using this in my Miner and it's fabulous, thanks a ton!

Sin

2012-05-19 11:20:10

Goddamn it, I want to test this out but can't :(

J J

2012-05-19 15:18:04

Great function! I haven't really used this in a script yet, but I have been playing around with some colors. This is really useful for finding certain monsters/npc/items.

Runaway

2012-05-19 17:11:50

From: cause
This is 100% awesome! I'll rep you when I get to a computer :)


Thanks! Means alot coming from the righteous dude :biggrin:

From: abu_jwka
Just started using this in my Miner and it's fabulous, thanks a ton!


Good to hear, I've used this in my miner as well. Great for rock-finding!

From: Sin
Goddamn it, I want to test this out but can't :(



From: J J
Great function! I haven't really used this in a script yet, but I have been playing around with some colors. This is really useful for finding certain monsters/npc/items.


Thanks :) This definitely makes NPC finding a whole lot easier.

Element17

2012-05-19 19:35:37

Is this what you use in your Miner script? To detect the npc?

Runaway

2012-05-19 19:59:00

From: 14578
Is this what you use in your Miner script? To detect the npc?


Yep, I used a variation of this.

Element17

2012-05-21 01:59:09

Sweet, I think I am going to try this as well!

Roflme

2012-11-28 21:27:31

Does FindObjTPAMulti work similarly to this function? What are the differences (if any)?

footballjds

2012-12-05 13:45:32

I made a function that did the exact same thing for a few scripts(mining and combat IIRC). I wasn't super fond of the speed so I found alternatives. I'll take a look at your code and see if you did anything different that'd make it faster.

EDIT: lol, you did it quite differently then I did. Hmm, tiss interesting. :)

rep+'d