SRL Archive - RS3 Help

Weird behaviour


KeepBotting

2014-07-13 22:42:50

procedure TRSChooseOption.delete(var arr:TOptionArray; idx:integer);
{
Deletes the specified TOption from the specified TOptionArray.
}
var
i, j:integer;
begin
if not self.isOpen() then
exit;

if (idx = high(arr)) then
begin
setLength(arr, high(arr));
end;
if (idx >= 0) and (idx < high(arr)) then
begin
for i := idx to high(arr) - 1 do
begin
arr[i] := arr[i + 1];
end;
setLength(arr, i);
end;
writeDebug('delete(): deleted option ' + toStr(idx));
end;

function TRSChooseOption.filter(txt:TStringArray):TOptionArray;
{
Deletes TOptions from the TOptionArray that do not match the TStringArray.
}
var
theOptions:TOptionArray;
i, j:integer;
begin
if not self.isOpen() then
exit;
theOptions := chooseOption.getOptions();

if (length(theOptions) < 1) then
exit;
for i := 0 to high(theOptions) do
for j := 0 to high(txt) do
if not (pos(txt[j], theOptions[i].str) > 0) then //access violation
begin
self.delete(theOptions, i);
end;
result := theOptions;
writeDebug('filter(): ' + toStr(result));
end;

These are two functions I've written for filtering options. They work fine except for a weird exception.

Debug output:

[DEBUG] : delete(): deleted option 1
[DEBUG] : delete(): deleted option 2
[DEBUG] : delete(): deleted option 3
[DEBUG] : filter(): [{BOUNDS = {X1 = 261, Y1 = 139, X2 = 439, Y2 = 152}, STR = Attack Ankou}, {BOUNDS = {X1 = 261, Y1 = 171, X2 = 439, Y2 = 184}, STR = Examine Ankou}]


As you can see, it claims to have deleted options 1, 2, and 3 from the above. Naturally, option 0 (Attack Ankou) should remain.
However when I debug the TOptionArray, it results a second option (Examine Ankou, element 2 in the TOptionArray) as well.
This always happens, even when filtering in such a way that every TOption should be deleted–one TOption remains.

In addition to this, the script will randomly throw access violations at the indicated line (as seen below). Not a clue why.


[DEBUG] : filter(): [{BOUNDS = {X1 = 232, Y1 = 231, X2 = 410, Y2 = 244}, STR = Walk here}, {BOUNDS = {X1 = 232, Y1 = 263, X2 = 410, Y2 = 276}, STR = Cancel}]
[DEBUG] : delete(): deleted option 0
[DEBUG] : delete(): deleted option 1
[DEBUG] : delete(): deleted option 2
Exception in Script: Runtime error: "Access violation" at line 776, column 16

What's going on?

Edit: I suspect it may have something to do with the way I reset the length of my array in .delete(), here's a more verbose debug output of the filter:

[DEBUG] : delete(): deleted option Walk here at index 0
[DEBUG] : delete(): deleted option Cancel at index 1
Exception in Script: Runtime error: "Access violation" at line 776, column 12
Obviously, Walk here is not at index 0, nor is Cancel at index 1–however I don't know what I've done wrong that would cause this.

Ian

2014-07-13 23:00:36

Is there a reason that you're removing all the options except Attack instead of just using TRSChooseOption.select?

KeepBotting

2014-07-13 23:05:53

From: BMWxi
Is there a reason that you're removing all the options except Attack instead of just using TRSChooseOption.select?


This is just a test, in the future I'll want to filter several different options, that's why I'm using a TStringArray

bonsai

2014-07-14 04:58:56

From: KeepBotting
procedure TRSChooseOption.delete(var arr:TOptionArray; idx:integer);
{
Deletes the specified TOption from the specified TOptionArray.
}
var
i, j:integer;
begin
if not self.isOpen() then
exit;

if (idx = high(arr)) then
begin
setLength(arr, high(arr));
end;
if (idx >= 0) and (idx < high(arr)) then
begin
for i := idx to high(arr) - 1 do
begin
arr[i] := arr[i + 1];
end;
setLength(arr, i);
end;
writeDebug('delete(): deleted option ' + toStr(idx));
end;


I would recommend this function be TOptionArray.delete(idx). It really has nothing to do with TRSChooseOption. I think the length is getting messed up although I would think you would be short one entry not long by one.

procedure TOptionArray.delete(idx:integer);
{
Deletes the specified TOption from the specified TOptionArray.
}
var
i, hi: integer;
begin
hi := high(self);
if (idx >= 0) and (idx <= hi) then
begin
if (idx < hi) then
for i := idx to hi - 1 do
self[i] := self[i + 1];

setLength(self, hi);
writeDebug('delete(): deleted option ' + toStr(idx));
end;
end;

From: KeepBotting
function TRSChooseOption.filter(txt:TStringArray):TOptionArray;
{
Deletes TOptions from the TOptionArray that do not match the TStringArray.
}
var
theOptions:TOptionArray;
i, j:integer;
begin
if not self.isOpen() then
exit;
theOptions := chooseOption.getOptions();

if (length(theOptions) < 1) then
exit;
for i := 0 to high(theOptions) do
for j := 0 to high(txt) do
if not (pos(txt[j], theOptions[i].str) > 0) then //access violation
begin
self.delete(theOptions, i);
end;
result := theOptions;
writeDebug('filter(): ' + toStr(result));
end;


You're looping through the length of the options but then calling delete in the loop so the length is changing. If you're on i==high and delete one, the next pass through the j loop will cause the access violation.

Assuming the i loop will reevaluate high() each time (I'm not 100% sure), you could just add a break to stop looping on the texts. Only problem here is it only matches one option (i.e. if 5 options matched the string it only deletes one).

function TRSChooseOption.filter(txt:TStringArray):TOptionArray;
{
Deletes TOptions from the TOptionArray that do not match the TStringArray.
}
var
theOptions:TOptionArray;
i, j:integer;
begin
if not self.isOpen() then
exit;
theOptions := chooseOption.getOptions();

if (length(theOptions) < 1) then
exit;
for i := 0 to high(theOptions) do
for j := 0 to high(txt) do
if not (pos(txt[j], theOptions[i].str) > 0) then //access violation
begin
theOptions.delete(i);
break;
end;
result := theOptions;
writeDebug('filter(): ' + toStr(result));
end;