SRL Archive - MSI Suggestions

When mining multiple rocks


Coh3n

2010-08-30 23:23:50

This issue came up a while back, but nothing was ever done about it, probably because we couldn't think of an effective way to go about it. So say I have my player setup like so:
Objects := [[ORE_TIN, ORE_IRON, ORE_CLAY]];
Well, what it's suppose to do is find and click the rock that's closest to the player. More often than not, the closest rock is the same one every time. For example, if the script clicked the tin rock first, it would most likely click the tin rocks for the rest of the load.

What should happen is the script mine all the rocks the user wants to mine (possibly giving priority to the ones that come first in the array). What I was thinking is using some sort of chance function that determines which rock to look for first depending on the player's Objects array.

We would have to find the right spot to insert this function (MSI_MineRock maybe?), and if it could be used for other skills. I really don't see why it couldn't be used in most gathering skills (woodcutting, mining, fishing, etc.).

Thoughts? Ideas?

NCDS

2010-08-30 23:52:04

We could have a 'PriorityMining' boolean constant and if it's set to false we could just do RandomRange(Low(Objects), High(Objects)); I think it's 'Low()' anyways..?

Just an idea, I've got to get to bed pretty soon as I've got work in the morning but if you agree with that, or have another idea, I should be able to do it tomorrow afternoon.

HyperSecret

2010-08-30 23:58:29

When I made my personal miner way back. I had it switch between ores each load. 1 load of tin, then 1 load of copper. So then I didn't have to manually stop and switch ores for the script. I could leave an army going for a long time and be ready to smelt whenever I wanted.

Could think of that too.

Have it just search in priority of the array. [Tin, Copper, Iron] would look for tin rocks 1st, then copper if tin not found, the iron if tin and copper not found. OR if the tin/copper locations are too busy move to iron, etc…and wait until that person banks or w/e.

Coh3n

2010-08-31 00:05:05

From: NCDS
We could have a 'PriorityMining' boolean constant and if it's set to false we could just do RandomRange(Low(Objects), High(Objects)); I think it's 'Low()' anyways..?

Just an idea, I've got to get to bed pretty soon as I've got work in the morning but if you agree with that, or have another idea, I should be able to do it tomorrow afternoon.

Nice, I like the PriorityMining thing, except since it can be used for more should be called PriorityAutoing or something more general. And yes, it is Low(), except we wouldn't even need to use it because they all start at 0, but it could just be used a precaution.

But yeah, I like that idea plus the chance function for priority.

From: HyperSecret
When I made my personal miner way back. I had it switch between ores each load. 1 load of tin, then 1 load of copper. So then I didn't have to manually stop and switch ores for the script. I could leave an army going for a long time and be ready to smelt whenever I wanted.

I also like that idea. It may take a little extra coding to make that work properly though.

From: HyperSecret

Have it just search in priority of the array. [Tin, Copper, Iron] would look for tin rocks 1st, then copper if tin not found, the iron if tin and copper not found. OR if the tin/copper locations are too busy move to iron, etc…and wait until that person banks or w/e.

Yep, that's pretty much what I had in mind (sort of). I was just thinking it would have a greater chance of mining Tin because it's first in the array, but there's still a smaller chance of mining the others. That way it WILL mine all the ores, not just the one.

Also, it's hard to determine if the rocks are busy. The only way I can think of is by using tiles and checking for players (on the minimap) near the tiles of the certain rocks.

NCDS

2010-08-31 00:07:27

From: Coh3n

Also, it's hard to determine if the rocks are busy. The only way I can think of is by using tiles and checking for players (on the minimap) near the tiles of the certain rocks.


Could possibly use GetMiniMapDotsIn() ? Would take a bit of work to make it reliable though.

Coh3n

2010-08-31 00:10:21

From: NCDS
Could possibly use GetMiniMapDotsIn() ? Would take a bit of work to make it reliable though.

May work, but we're making a simple suggestion complicated. :p Let's worry about one thing at a time.

NCDS

2010-08-31 00:11:51

From: Coh3n
May work, but we're making a simple suggestion complicated. :p Let's worry about one thing at a time.


Yeah, alright :redface:

I'll check back on this thread tomorrow after work and see if something has been decided upon. Then I can get started.

NCDS

2010-08-31 20:14:52

If we can decide on a method to use here, I'll be able to get started/do this tonight.

Coh3n

2010-08-31 20:18:38

I say the chance function. The first object in the array has the highest chance, then it steadily gets lower. The function would generate the object integer to look for based on the chances or rather, a rearranged array. If you make it rearrange the array every them then that's all we would have to do.

Call the function before looking for the objects and that should be it. Simpler than I thought (almost too simple…).

Sir R. M8gic1an

2010-08-31 22:47:04

Barely read.

objects := [Rock_Tin, Rock_Copper, Rock_Iron];
Priority := [30, 30, 40];

Function addTIA(TIA: TIntegerArray): integer;
var i:integer;
begin
for i:= High(TIA) DownTo 0 do
Result := Result + TIA[i];
end;

function ChooseRock(Priorities: TIA): integer;
var
TIA: TIntegerArray;
i, total, curr: integer;
begin
total := addTIA(Priorities);

for i := 0 to High(Priorities) do
begin
curr := curr + Priorities[i];
TIA[i] := curr;
end;

total := Random(total) + 1;

for Result:= 0 to high(TIA) do
if total <= TIA[i] then
break;

end;

This should choose the rock for you. You can put any number you wish as priority, so you can have like
[50, 150, 100] as priority

~RM

NCDS

2010-09-01 19:26:48

That would be quite simple to implement. Anyone else have any comments before I do this?

3Garrett3

2010-09-01 22:54:22

From: NCDS
That would be quite simple to implement. Anyone else have any comments before I do this?


Just try it and whatever comes from it, will happen :p

I like what RM did, I say make that happen?

Sir R. M8gic1an

2010-09-03 07:30:29

I think we can go ahead with this.

~RM

NCDS

2010-09-03 09:03:17

Ok I'll have this done as soon as I'm finished with work then.

One thing, this will obviously be called in MineRock, but where should this code go?

I'm thinking that since it has potential to be used in other skills, we could place it in the original Skills.Simba file.

Anyone object/have another suggestion?

Coh3n

2010-09-03 14:24:28

I like the idea of putting in Skills.simba, since it isn't being used for anything else. Also, RM, what you posted, would that mean adding another attribute for the player to setup? I'd rather not have that, but I guess it adds more customization. And maybe change the name of addTIA? I thought you were combining integer arrays (could just be me though). SumTIA, maybe? I don't know, just a thought.

If we're to add a Priorities option to DeclarePlayers it would have to be a 3D array (just like Objects), and we would need a default Priorities in the Scripts type. Or instead of another attribute in TScript, just have the priorities equal if the user didn't fill it out, so that it will still mine all the rocks.

Last thing, in RM's function, I feel like Players[].Objects should be used somewhere, but as long as Players[].Priorities is the same length I think it should be okay.

NCDS

2010-09-03 17:40:50

I'm going to bring my PC with me this weekend so I can have a bit of extra time.

I'll check back with this thread once I get where I'm going and hopefully will be able to get something committed by the end of the day.

Coh3n

2010-09-03 18:40:18

Okay, and in my above post I said 3D array. That's my bad, should only need to be 2D. :)

NCDS

2010-09-03 20:39:32

From: Coh3n
Okay, and in my above post I said 3D array. That's my bad, should only need to be 2D. :)


*sigh* I forgot an ethernet cable to connect my PC to the internet, so any commits will have to be done tomorrow.

I'm going to try and see what I can get done tonight anyways though.

Sir R. M8gic1an

2010-09-03 23:03:50

Coh3n, I'm just providing the idea :) The exactedness of how/what to do was not the point of it :p

~RM

NCDS

2010-09-04 18:11:17

For the PriorityAutoing boolean, should it be a constant, or should it be in DeclarePlayers.

If that latter users could obviously choose it separately for each character they are running, which seems like it could be nice to have.

Sir R. M8gic1an

2010-09-05 01:03:56

I'd say declarePlayers.

~RM

NCDS

2010-09-05 18:40:20

Ok, so here's what I'm thinking right now…


What if 'Priority' was an Integer?

We could have the user fill out the Objects array in order of importance. Then, the 'Priority' attribute could be set to 0 to use priority, 1 to choose randomly, and 3 to just find the closest rock.

OR

We could do how RM has it, and if the array is not filled out then choose randomly, or just choose the closest rock.

Fast input would be nice as I've got some time to do this finally tonight :)

Coh3n

2010-09-05 19:03:47

From: NCDS
For the PriorityAutoing boolean, should it be a constant, or should it be in DeclarePlayers.

If that latter users could obviously choose it separately for each character they are running, which seems like it could be nice to have.

We wouldn't need a Priority boolean/ integer if we use an array in the DeclarePlayers. If the user doesn't set it, it would use the default priority (which would be it randomly chooses a rock from the array).

with MSI_Players[1] do
begin
Name := '';
Pass := '';
Nick := '';
Pin := '';
Active := True;
Member := False;
Location := ;
Scripts := [];
Objects := [[]];
Priorities := [[]]; // Like objects, leave blank to use default
Loads[0] := ;
Loads[1] := ;
end;

with MSI_Players[1] do
begin
Name := 'Coh3n';
Pass := 'MyPassword123';
Nick := 'oh3';
Pin := '';
Active := True;
Member := False;
Location := LOC_VE_BANK;
Scripts := [SCRIPT_VE_MINER];
Objects := [[ROCK_TIN, ROCK_COPPER]];
Priorities := [[80, 20]];
Loads[0] := 10;
Loads[1] := 200;
end;
So in the example I filled out, there would be an 80% chance of mining tin, and only a 20% chance of mining copper. If the priorities array wasn't filled out, then it would just randomly choose either tin or copper. Like Objects, Priorities would have to be a variant array (interpreter doesn't like the '[[]]' declaration of anything but variants).

NCDS

2010-09-05 19:09:30

From: Coh3n
We wouldn't need a Priority boolean/ integer if we use an array in the DeclarePlayers. If the user doesn't set it, it would use the default priority (which would be it randomly chooses a rock from the array).

Yeah, I know, I just forgot to mention that in my last post :p

From: Coh3n


with MSI_Players[1] do
begin
Name := '';
Pass := '';
Nick := '';
Pin := '';
Active := True;
Member := False;
Location := ;
Scripts := [];
Objects := [[]];
Priorities := [[]]; // Like objects, leave blank to use default
Loads[0] := ;
Loads[1] := ;
end;

with MSI_Players[1] do
begin
Name := 'Coh3n';
Pass := 'MyPassword123';
Nick := 'oh3';
Pin := '';
Active := True;
Member := False;
Location := LOC_VE_BANK;
Scripts := [SCRIPT_VE_MINER];
Objects := [[ROCK_TIN, ROCK_COPPER]];
Priorities := [[80, 20]];
Loads[0] := 10;
Loads[1] := 200;
end;
So in the example I filled out, there would be an 80% chance of mining tin, and only a 20% chance of mining copper. If the priorities array wasn't filled out, then it would just randomly choose either tin or copper. Like Objects, Priorities would have to be a variant array (interpreter doesn't like the '[[]]' declaration of anything but variants).

Alright sounds good, this will be done soon. My friend is up from virginia and just stopped by so give me an hour or 2 ;)

Coh3n

2010-09-05 19:17:33

Take all the time you need. I just made a commit, and I may be making more this afternoon, so just remember to update. :)

Have fun.

NCDS

2010-09-06 18:25:35

The RandomizeArray function is done, but the more I sit here thinking about the ChooseRock function, the more I wonder why are we doing it this way?

Why don't we just have a Priority boolean in DeclarePlayers, then have the user fill out Objects in the order starting with highest priority.

All we would really need code wise for that is a simple if..then to determine the Priority boolean, and a for..to..do loop to loop through the Objects to search for if Priority was true.

We could still use the RandomizeArray as Default as well.

Coh3n

2010-09-06 20:43:16

From: NCDS
The RandomizeArray function is done, but the more I sit here thinking about the ChooseRock function, the more I wonder why are we doing it this way?

Why don't we just have a Priority boolean in DeclarePlayers, then have the user fill out Objects in the order starting with highest priority.

All we would really need code wise for that is a simple if..then to determine the Priority boolean, and a for..to..do loop to loop through the Objects to search for if Priority was true.

We could still use the RandomizeArray as Default as well.

If the priorities are set, then the user can choose the percentage of which object to find. If they just put it in order of priority it would just just like it is now (find the first object in the array 95% of the time).

When they're set, the user can set whatever priorities they want.

NCDS

2010-09-06 20:46:06

From: Coh3n
If the priorities are set, then the user can choose the percentage of which object to find. If they just put it in order of priority it would just just like it is now (find the first object in the array 95% of the time).

When they're set, the user can set whatever priorities they want.


But using this:

for Result:= 0 to high(TIA) do
if total <= TIA[i] then
break;
would generally have the same outcome we have now as the one with the highest priority would always be picked first?

marijuana

2010-09-06 21:14:54

How about maintaining the index of the rock type between iterations? That way, all specified rock types get a roughly equal chance of getting mined.

NCDS

2010-09-06 21:21:07

From: marijuana
How about maintaining the index of the rock type between iterations? That way, all specified rock types get a roughly equal chance of getting mined.


Just earlier today we took care of a function to randomly sort the array of objects to simulate a random choice, which should keep thing's pretty equal.

Coh3n

2010-09-06 21:45:16

From: NCDS
But using this:

for Result:= 0 to high(TIA) do
if total <= TIA[i] then
break;
would generally have the same outcome we have now as the one with the highest priority would always be picked first?

Not always, no.

for i := 0 to High(Priorities) do
begin
curr := curr + Priorities[i];
TIA[i] := curr;
end;
That part makes it so the indexes of TIA gradually get larger until the last index is the total of all the indexes of Priorities. Then it generates a random integer from the total of Priorities.

So, for the example in RM's script (Priorities := [30, 30, 40]), the TIA ends up being [30, 60, 100]. Since Random(100) is called, there's a 30% chance of getting between 1-30, 30% chance of getting between 31-60 and 40% chance of getting between 61-100.

Hopefully that clears things up. :)

NCDS

2010-09-06 22:48:13

From: Coh3n
Not always, no.

for i := 0 to High(Priorities) do
begin
curr := curr + Priorities[i];
TIA[i] := curr;
end;
That part makes it so the indexes of TIA gradually get larger until the last index is the total of all the indexes of Priorities. Then it generates a random integer from the total of Priorities.

So, for the example in RM's script (Priorities := [30, 30, 40]), the TIA ends up being [30, 60, 100]. Since Random(100) is called, there's a 30% chance of getting between 1-30, 30% chance of getting between 31-60 and 40% chance of getting between 61-100.

Hopefully that clears things up. :)


Yeah I understood that stuff, just that last loop was throwing me off a bit, but I get it now that I really think about it, with the changes.

NCDS

2010-09-07 17:16:18

Ok now how do you want me to implement this into the scripts themselves?

I can loop MSI_ChooseObject with this line:

Result := MSI_Scripts[scriptConst].AutoObjects(objects);
in AutoObjects.

Or I can implement it individually to each skill.

What's everyone want?

Coh3n

2010-09-07 22:45:33

Can you post all the code you have for this so far?

NCDS

2010-09-07 23:02:54

From: Coh3n
Can you post all the code you have for this so far?


Only thing tested is RandomizeArray;

{******************************************************************************}
{ procedure MSI_RandomizePriority(Objs: TIntegerArray); }
{ By: NCDS & Coh3n }
{ Description: Returns the Objects array in a random order }
{******************************************************************************}
function MSI_RandomizeArray(Objs: TIntegerArray): TIntegerArray;
var
i, o: Integer;
begin
SetLength(Result, Length(Objs));

while (Length(Objs) <> 0) do
for i := 0 to High(Result) do
begin
o := Random(Length(Objs));
Writeln(IntToStr(Objs[o]));

Result[i] := Objs[o];

DeleteValueInIntArray(Objs, o);
Writeln('Length = ' + IntToStr(Length(Objs)));
end;

for i := 0 to High(Result) do
Writeln(IntToStr(Result[i]));
end;

{******************************************************************************}
{ function SumTIA(TIA: TIntegerArray); }
{ By: RM }
{ Description: Returns the Sum of 'TIA' }
{******************************************************************************}
function SumTIA(TIA: TIntegerArray): Integer;
var
i: Integer;
begin
for i := High(TIA) downto 0 do
Result := Result + TIA[i];
end;

{******************************************************************************}
{ function MSI_ChooseObject(Priorities: TIntegerArray): Integer; }
{ By: RM, Coh3n, NCDS }
{ Description: Chooses an Object based on 'Priorities' }
{******************************************************************************}
function MSI_ChooseObject(Priorities: TIntegerArray): Integer;
var
TIA: IntegerArray;
i , Sum, curr: Integer;
begin
with MSI_Players[CurrentPlayer].Scripts[CurrentScript] do
begin

if (Length(Priorities) <> Length(Objects)) then
begin
MSI_RandomizeArray(Objects);
Exit;
end;

Sum := SumTIA(Priorities);

for i := 0 to High(Priorities) do
begin
CurrSum := CurrSum + Priorities[i];
TIA[i] := CurrSum;
end;

Sum := Random(Sum) + 1;

for Result := 0 to High(TIA) do
if Sum <= TIA[i] then
break;
end;
end;

Coh3n

2010-09-07 23:18:28

Hmm. Since all the object functions use arrays, I think that we need another function (MSI_RearrangeArray) that calls MSI_ChooseObject and returns a new array based on the priorities. RearrangeArray would be a lot like RandomizeArray, except it wouldn't be random, it'd be based on the priorities array.

Now, we could make them return TVariantArrays, so we can easily set Players[].Objects[], or we can call RandomizeArray or RearrangeArray in MSI_AutoObjects.

function MSI_AutoObjects: Boolean;
var
objects: TIntegerArray;
scriptConst: Integer;
begin
if (not LoggedIn) then
Exit;

MSI_AddHeader('MSI_AutoObjects');

MakeCompass(MSI_Scripts[MSI_Players[CurrentPlayer].Scripts[CurrentScript]].CompassAng);
scriptConst := MSI_Players[CurrentPlayer].Scripts[CurrentScript];
objects := MSI_VarToIntArray(MSI_Players[CurrentPlayer].Objects[CurrentScript]);

// I added this little bit :)
with MSI_Players[CurrentPlayer] do
if (Length(Priorities[CurrentScript]) <> Length(Objects[CurrentScript])) then
MSI_RandomizeArray(objects)
else
MSI_RearrangeArray(objects);

Result := MSI_Scripts[scriptConst].AutoObjects(objects);

if (MSI_Scripts[scriptConst].BankType = NoBank) then //drop the items if not banking
begin
MSI_CountItems;
MSI_DropAll;
MSI_AddToProggy(PROG_ADD_LOADS, -1, -1, -1);
Result := True;
end;

MSI_CloseHeader('MSI_AutoObjects: ' + BoolToStr(Result));
end;

NCDS

2010-09-07 23:58:36

I like how you've implemented it into AutoObjects :)

Here's a start on RearrangeArray and how I've been testing it:

program new;

var
Priorities: TIntegerArray;

function SumTIA(TIA: TIntegerArray): Integer;
var
i: Integer;
begin
for i := High(TIA) downto 0 do
Result := Result + TIA[i];
end;

function MSI_RearrangeArray(Objs: TIntegerArray): TIntegerArray;
var
TIA: TIntegerArray;
i , Sum, CurrSum: Integer;
begin {
with MSI_Players[CurrentPlayer].Scripts[CurrentScript] do
with MSI_Players[CurrentPlayer] do}
begin
SetArrayLength(TIA, Length(Priorities));
SetArrayLength(Result, Length(Objs));
Sum := SumTIA(Priorities);

for i := 0 to High(Priorities) do
begin
IncEx(CurrSum, Priorities[i]);
TIA[i] := CurrSum;
end;

Sum := Random(Sum) + 1;

for i := 0 to High(TIA) do
if Sum <= TIA[i] then
Result[i] := Objs[i];

for i := 0 to High(Result) do
Writeln(IntToStr(Result[i]));
end;
end;


begin
Priorities := [30, 30, 40];
MSI_RearrangeArray([1, 2, 3]);
end.

For some reason, this is the output:

SRL Compiled in 16 msec
0
2
3
Successfully executed.

But it's a start I guess..

Also, the way you implemented it would mean we would have to pass the value of the array's through the params instead of making it a Result, which is fine with me.

Coh3n

2010-09-08 00:03:35

Oh yeah, my bad. Would have to be MSI_RandomizeArray(Objs: TIA; var newArr: TIA);

And I meant create a new function, no combine ChooseObject and RearrangeArray. It will be much easier if they're separated, and RearrangeArray will be very similar to RandomizeArray. But if you can make it work in one function, then go ahead. :)

NCDS

2010-09-08 00:13:14

From: Coh3n
Oh yeah, my bad. Would have to be MSI_RandomizeArray(Objs: TIA; var newArr: TIA);

And I meant create a new function, no combine ChooseObject and RearrangeArray. It will be much easier if they're separated, and RearrangeArray will be very similar to RandomizeArray. But if you can make it work in one function, then go ahead. :)


I'll have more time to work with it tomorrow as I only have to work for a couple hours, but I must be getting to bed now.

I'll let you know how it goes :)