PDA

View Full Version : TReflectNPC.FindSpiral



Floor66
02-12-2015, 10:27 AM
//Look for NPC with Name in spiral pattern around player, within an RxR square
function TReflectNPC.FindSpiral(Name: String; R: Integer; checkInteracting: Boolean): Boolean;
var
currTile: TTile;
i, j, n, k, di, dj, seg, segLen, buf, m, l: Integer;
tNPC: TReflectNPC;
tNPCA, tRNPCA: TReflectNPCArray;
condition: Boolean;
begin
if not isLoggedIn then
Exit;

//Get all NPCs and select only those with the right Name
Self.Null;
tNPCA.GetAll;
j := 0;
k := High(tNPCA);
SetLength(tRNPCA, k + 1);
for i := 0 to k do
if tNPCA[i].Name = Name then
begin
tRNPCA[j] := tNPCA[i];
Inc(j);
end;
l := j;

currTile := cP.GetTile; //cP is a global TReflectLocalPlayer
k := R * R;
i := 0;
j := 0;
di := 1;
dj := 0;
seg := 0;
segLen := 1;

//Spiral around the player, looking for matches between found NPCs' location and the current spiral location
for n := 0 to k do
begin
IncEx(currTile.X, di);
IncEx(currTile.Y, dj);
Inc(seg);

for m := 0 to l do
begin
if checkInteracting then
condition := (tRNPCA[m].Tile.X = currTile.X) and (tRNPCA[m].Tile.Y = currTile.Y) and (tRNPCA[m].InteractingIndex = -1)
else
condition := (tRNPCA[m].Tile.X = currTile.X) and (tRNPCA[m].Tile.Y = currTile.Y);

if condition then
begin
Self := tRNPCA[m];
Result := True;
Exit;
end;
end;

if seg = segLen then
begin
seg := 0;
buf := di;
di := -dj;
dj := buf;

if dj = 0 then
Inc(segLen);
end;
end;

Result := False;
end;


For a script I've created.
It will search the tiles around the player in a spiraling pattern for an NPC named 'Name' (todo: add ID support), in a R^2 square as max range.
The spiraling logic I've adapted from a StackOverflow answer that was in Python. The checkInteracting bit is because I'll mainly be looking for NPCs to fight, to make sure they're not already engaged.

This is my first thing since like 2009 so I'm quite rusty. Tell me what you think :)

EDIT:

Shortened it a bit, removed that first loop. Will test out when hooks are back up.

//Look for NPC with Name in spiral pattern around player, within an RxR square
function TReflectNPC.FindSpiral(Name: String; R: Integer; checkInteracting: Boolean): Boolean;
var
currTile: TTile;
x, y, n, k, dx, dy, seg, segLen, buf, i, npcCount: Integer;
tNPCA: TReflectNPCArray;
condition: Boolean;
begin
if not isLoggedIn then
Exit;

Self.Null;
tNPCA.GetAll;
npcCount := High(tNPCA);
currTile := cP.GetTile;
k := R * R;
x := 0;
y := 0;
dx := 1;
dy := 0;
seg := 0;
segLen := 1;

//Spiral around the player, looking for matches between found NPCs' location and the current spiral location
for n := 0 to k do
begin
IncEx(currTile.X, dx);
IncEx(currTile.Y, dy);
Inc(seg);

for i := 0 to npcCount do
begin
if checkInteracting then
condition := (tNPCA[i].Name = Name) and (tNPCA[i].Tile.X = currTile.X) and (tNPCA[i].Tile.Y = currTile.Y) and (tNPCA[i].InteractingIndex = -1)
else
condition := (tNPCA[i].Name = Name) and (tNPCA[i].Tile.X = currTile.X) and (tNPCA[i].Tile.Y = currTile.Y);

if condition then
begin
Self := tNPCA[i];
Result := True;
Exit;
end;
end;

if seg = segLen then
begin
seg := 0;
buf := dx;
dx := -dy;
dy := buf;

if dy = 0 then
Inc(segLen);
end;
end;

Result := False;
end;

Kyle
02-12-2015, 01:49 PM
Looks good man! Though, I'm sure it can be simplified and sped up by using Reflect.Tiles.DistanceFromTile(Tile: TTile);
It won't technically be 'spiraling,' but since it just simply uses the distance formula, and you could loop through them that way, I'd assume it'd be faster.

Floor66
02-12-2015, 01:52 PM
Yeah I'd thought about that, I'll compare and see what is fastest (and get an excuse to teach myself that spiraling algorithm :D). Using this and some other tricks I can get my bot to match the speed and sometimes even beat humans.
I just wasn't quite satisfied with the speed I was getting using a different method ^^ I've got a few more of these snippets (TileOnMS, WaitUpText) if you're interested.

the bank
02-12-2015, 03:14 PM
Step 1:
Find all NPCs of that ID and store their tiles in an array

Step 2:
Sort the array from closest to furthest from the player tile

Step 3:
Iterate this list checking each result in a spiral search pattern; this step would be made easier by using a Map or HashMap pascal variation

Step 4:
Profit!

Floor66
02-12-2015, 03:18 PM
Thing is, I'm thinking of performance here. I was targeting chickens, which move quite fast so I didn't want to waste time. Also, I don't like using more loops than I have to.

Step 1) is a loop, checking for the NPCs' names
Step 2) is sorting, which is a bunch of nested loops
Step 3) is kind of what I have going on --> could you expand on the use of Maps/HashMaps?

Kyle
02-12-2015, 03:22 PM
Step 1:
Find all NPCs of that ID and store their tiles in an array

Step 2:
Sort the array from closest to furthest from the player tile

Step 3:
Iterate this list checking each result in a spiral search pattern; this step would be made easier by using a Map or HashMap pascal variation

Step 4:
Profit!

That's how the current TReflectNpcArray.Sort works minus the spiral at the end

Hoodz
02-12-2015, 03:28 PM
That's how the current TReflectNpcArray.Sort works minus the spiral at the end

spiral is not even necessary imo

the bank
02-12-2015, 03:31 PM
spiral is not even necessary imo

My thoughts exactly however the OP requested spiral sorting.

Kyle
02-12-2015, 03:34 PM
spiral is not even necessary imo

Hense why .Sort doesn't use it :p

Olly
02-12-2015, 03:36 PM
Sprial has it's benefits, as the spiral paths are (should?) be created randomly.

Who clicks the pixel perfect nearest npc each time?

+ you're all crazy talking about hashmaps and stuff, you're going to have like 20 npc's max on your screen.

the bank
02-12-2015, 03:37 PM
Referencing my post above, you can avoid using a Map by using a 2D array, its just not as pretty.

Java:


public class Blah {

public static void main(String[] args) {
print2dArray(getSpiralArray(5));
}

public static int[][] getSpiralArray(int dimension) {
int[][] spiralArray = new int[dimension][dimension];

int numConcentricSquares = (int) Math.ceil((dimension) / 2.0);

int j;
int sideLen = dimension;
int currNum = 0;

for (int i = 0; i < numConcentricSquares; i++) {
// do top side
for (j = 0; j < sideLen; j++) {
spiralArray[i][i + j] = currNum++;
}

// do right side
for (j = 1; j < sideLen; j++) {
spiralArray[i + j][dimension - 1 - i] = currNum++;
}

// do bottom side
for (j = sideLen - 2; j > -1; j--) {
spiralArray[dimension - 1 - i][i + j] = currNum++;
}

// do left side
for (j = sideLen - 2; j > 0; j--) {
spiralArray[i + j][i] = currNum++;
}

sideLen -= 2;
}

return spiralArray;
}

public static void print2dArray(int[][] array) {
for (int[] row : array) {
for (int elem : row) {
System.out.printf("%3d", elem);
}
System.out.println();
}
}
}

Pascal:

program Spiralmat;
type
tDir = (left,down,right,up);
tdxy = record
dx,dy: longint;
end;
tdeltaDir = array[tDir] of tdxy;
const
Nextdir : array[tDir] of tDir = (down,right,up,left);
cDir : tDeltaDir = ((dx:1;dy:0),(dx:0;dy:1),(dx:-1;dy:0),(dx:0;dy:-1));
cMaxN = 32;
type
tSpiral = array[0..cMaxN,0..cMaxN] of LongInt;

function FillSpiral(n:longint):tSpiral;
var
b,i,k, dn,x,y : longInt;
dir : tDir;
tmpSp : tSpiral;
BEGIN
b := 0;
x := 0;
y := 0;
//only for the first line
k := -1;
dn := n-1;
tmpSp[x,y] := b;
dir := left;
repeat
i := 0;
while i < dn do
begin
inc(b);
tmpSp[x,y] := b;
inc(x,cDir[dir].dx);
inc(y,cDir[dir].dy);
inc(i);
end;
Dir:= NextDir[dir];
inc(k);
IF k > 1 then
begin
k := 0;
//shorten the line every second direction change
dn := dn-1;
if dn <= 0 then
BREAK;
end;
until false;
//the last
tmpSp[x,y] := b+1;
FillSpiral := tmpSp;
end;

var
a : tSpiral;
x,y,n : LongInt;
BEGIN
For n := 1 to 5{cMaxN} do
begin
A:=FillSpiral(n);
For y := 0 to n-1 do
begin
For x := 0 to n-1 do
write(A[x,y]:4);
writeln;
end;
writeln;
end;
END.

Edit:

Above solutions will sort a "mat" of objects (in your case the mat is onscreen tiles). It will sort from outside inwards, but is fairly easy to reverse.

Both examples will run and generate + spiral sort a matrix.

Floor66
02-12-2015, 03:53 PM
Sprial has it's benefits, as the spiral paths are (should?) be created randomly.

Who clicks the pixel perfect nearest npc each time?

+ you're all crazy talking about hashmaps and stuff, you're going to have like 20 npc's max on your screen.

This is also what I was considering -> simply grabbing the nearest in a straight line vs. nearest by spiral pattern.
These are my other snippets btw:

function TReflectionText.WaitUpTextMulti(S: TStringArray; time: Integer): Boolean;
var
t: Timer;
begin
Result := False;
t.start;

while t.timeElapsed < time do
begin
if Reflect.Text.IsUpTextMulti(S) then
Exit(True);
Wait(20 + Random(20));
end;
end

function TReflectionText.WaitUpText(S: String; time: Integer): Boolean;
var
t: Timer;
begin
Result := TReflectionText.WaitUpTextMulti([S], time);
end

Hoodz
02-12-2015, 05:44 PM
My thoughts exactly however the OP requested spiral sorting.

however, its still nice he contributed.
rep+