PDA

View Full Version : Weird behaviour



KeepBotting
07-14-2014, 02:42 AM
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):TOptionAr ray;
{
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}]


http://puu.sh/aakXf.png

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
07-14-2014, 03:00 AM
Is there a reason that you're removing all the options except Attack instead of just using TRSChooseOption.select?

KeepBotting
07-14-2014, 03:05 AM
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
07-14-2014, 08:58 AM
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;


function TRSChooseOption.filter(txt:TStringArray):TOptionAr ray;
{
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):TOptionAr ray;
{
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;