Page 1 of 2

Using the resource maps

Posted: 16 Mar 2009, 15:52
by LoidThanead
Hi all,

Since the release of the Java AI interface I've been working to create a simple Java AI. So far, it can build units and structurs, and order some units around. It even reacts to enemy units entering radar range and tries to destroy them.
Now I'm trying to get the AI to build metal extractors. The building itself is not the problem, but finding the proper locations to build at is.
I have looked, but could not find any documentation on the subject in the wiki, nor on the forums. Below is what I (think I) know so far.

The callback has a method 'getResourceMap,' but rather than a 2D or even 3D map, all I get is is a 1D array, a list, of byte values.
My first problem is mapping the entries in the list to map coordinates. I already discovered that the resource map resolution is different from the actual map resolution. The difference is an (apparently arbitrary) factor 2.
Assuming the first entry in the list corresponds to coordinates (0, 0), the second entry corresponds to either (0, 2) or (2, 0), right? Can anyone tell me which of the two is correct?

Besides the above problem, I am also unsure what the actual values in the list represent. a byte has either value 0 or 1, so I'm guessing it means that either a resource is present or it is not.

Is there anyone that can shed some light on the subject?
I'm also looking for an algorithm that will find the most efficient locations to build the extractors. I know there is a C++ implementation available, but I work in Java. In an ideal world, the metalspot finder algorithm would also be accessible through the Java interface... but that is probably wishful thinking.
If anyone can tell me the algorithm the C++ implementation uses, I may be able to find or build it in Java, for use for any other Java AI developers.

Thanks in advance,
Loid

Re: Using the resource maps

Posted: 16 Mar 2009, 16:21
by slogic
For example take a look at AI\Global\KAIK-0.13\MetalMap.cpp, method GetNearestMetalSpot().

Re: Using the resource maps

Posted: 16 Mar 2009, 16:33
by Kloot
The callback has a method 'getResourceMap,' but rather than a 2D or even 3D map, all I get is is a 1D array, a list, of byte values.
There is only one such "resource map" at present (the metal map), and as was already explained here, all the maps requestable through the interface are one-dimensional. (Any higher-dimensional arrays are mapped to 1D at the machine level anyway.)
Assuming the first entry in the list corresponds to coordinates (0, 0), the second entry corresponds to either (0, 2) or (2, 0), right? Can anyone tell me which of the two is correct?
The maps are all stored in row-major order.
My first problem is mapping the entries in the list to map coordinates. I already discovered that the resource map resolution is different from the actual map resolution. The difference is an (apparently arbitrary) factor 2.
Correct. Another way to think about it is to take the height-map as a baseline and sample it down by a factor of two along each dimension to get the size of the metal-map, by a factor of eight to get the radar-map resolution, etc.
Besides the above problem, I am also unsure what the actual values in the list represent. a byte has either value 0 or 1, so I'm guessing it means that either a resource is present or it is not.
A byte has 256 possible values. For the metal-map, such a value <v> means "this square produces <v> * the map's metal-scale amount of metal" if claimed by an extractor. From there you should really be able to devise your own algorithm that outputs a list of extractor positions that maximize the global income. ;)

Re: Using the resource maps

Posted: 16 Mar 2009, 16:36
by hoijui
i just commited a lot of docu for the AI Interface this morning.
The resource map fetch function for example, was one of the things that got documented.
so you can get a recent buildbot installer, which should then contain the new java sources including the new docu.

yeah.. about the metal spot finder..
i will try to add it to the interface

edit: when i compare the docu with what Kloot just wrote, i seem to have some factors wrong though.
could you possibly have an eye at rts/ExternalAI/SSkirmishAICallback.h please, Kloot?
search for "getHeightMap", thats where these functions start

Re: Using the resource maps

Posted: 16 Mar 2009, 17:19
by LoidThanead
Thanks for your replies, everyone.
A byte has 256 possible values. For the metal-map, such a value <v> means "this square produces <v> * the map's metal-scale amount of metal" if claimed by an extractor. From there you should really be able to devise your own algorithm that outputs a list of extractor positions that maximize the global income. ;)
D'oh. Of course you're right. That was a really silly mistake of me. It makes much more sense now.
And sure, I could probably devise my own algorithm. However, it'd be silly to re-invent the wheel, especially since I'd screw up in the process.
I'll look at the C++ code, but I'm pretty bad at interpretting correctly what it does, since I have very little C++ experience.
There is only one such "resource map" at present (the metal map), and as was already explained here, all the maps requestable through the interface are one-dimensional. (Any higher-dimensional arrays are mapped to 1D at the machine level anyway.)
I can get a map for each resource from the Java callback. Haven't looked at the other maps' contents though. They might be empty.
Also, how they are stored at machine level really should be inconsequential to a programmer. The compiler takes care of that. My point of view is that if something is called a map, it should be a map, not a list.
(I understand there are probably reasons for making it a list, and I'm not looking to bash any of them. I'm just a big believer in ease of use.)



I'll go and download the latest installer, Hoijui. Documentation is good. :)

Re: Using the resource maps

Posted: 18 Mar 2009, 11:36
by LoidThanead
After some playing around, I now have a nice and visualized metal map on my screen. It all looks good, except... for some reason, at the center of metalspots I find extremely low values. I've attached a contour map so you can see.

If I interpret things correctly, wouldn't this mean that the centers of the metal spots would produce very low amounts of metal? Or am I getting something wrong?

Re: Using the resource maps

Posted: 18 Mar 2009, 12:19
by Auswaschbar
unsigned char, nuf said

Re: Using the resource maps

Posted: 18 Mar 2009, 12:38
by LoidThanead
As far as I know, there's no such thing in Java. I'm using the Java AI interface. I guess this means there's a small bug in the conversion from the C++ unsigned char to the Java byte value? Hoijui should be able to say more about that.

Re: Using the resource maps

Posted: 18 Mar 2009, 13:34
by imbaczek
add 128 to the byte value, then.

BTW what do you use to make that plot?

Re: Using the resource maps

Posted: 18 Mar 2009, 15:42
by LoidThanead
I tried that, but that seems to have some odd effects. The main area outside the metal spots has value 0, as it should have, so bumping it up to 128 probably isn't correct. For now, I'm just using the absolute values, which is also not the proper solution, but it works for my purposes.

I made that plot using Matlab. After saving the metalmap in a text file, I loaded it in Matlab and made a simple contour plot.

Re: Using the resource maps

Posted: 18 Mar 2009, 15:52
by Peet
Since java only has signed bytes, the range is from -128 to 127 as opposed to 0 to 255 in an unsigned byte. You could convert to a positive int by casting and then inverting bits 0-7 and adding 1 when the input is negative if you wanted to keep the full range (and waste 23 bits per metal pixel [..mexel?])...but I suggest you just shift the input by one bit to the right. Precision will be halved but if it's for a metal spot finding algorithm that won't make much of a difference.

Re: Using the resource maps

Posted: 18 Mar 2009, 15:54
by Auswaschbar
Know shit about java, so here in C++:

Code: Select all

int realValue = oldValue;
if (realValue < 0)
    realValue += 256;
Where int needs to be big enough to store all values in [-128, 256]

Re: Using the resource maps

Posted: 18 Mar 2009, 16:02
by hoijui
yeah.. as Java does not support signed/unsigned types, the JNA people decided to treat all unsigned types the same way as the signed ones.
i think you have to use the two complement.
http://en.wikipedia.org/wiki/Two's_complement
if you do not know it already.. it seems to be explained complex on wiki...
works like this: take the number in binary format, change 1's to 0's and vice versa, and add 1 to the resulting number (i think it works like this, and i guess that is waht would give you the real numbers).

though.. it would probably be a good idea if i try to get rid of signed types on the C interface.

would it be acceptable to return an int[] instead of unsinged char[]?
performance wise, that should not really matter, only memory wise, right?
and even there only for native AIs (as in Java, it is all int anyway).

Re: Using the resource maps

Posted: 18 Mar 2009, 16:15
by lurker
Does using java corrupt people's minds when it comes to how signed numbers work? ADD 256 DONE

Re: Using the resource maps

Posted: 18 Mar 2009, 16:18
by Pxtl
lurker wrote:Does using java corrupt people's minds when it comes to how signed numbers work? ADD 256 DONE
Reading is fundamental. RTFT.

Re: Using the resource maps

Posted: 18 Mar 2009, 16:22
by lurker
Pxtl wrote:
lurker wrote:Does using java corrupt people's minds when it comes to how signed numbers work? ADD 256 DONE
Reading is fundamental. RTFT.
What are you on about? I'm talking about the very specific problem of converting a signed number into unsigned. As aus's post has, one if, one +, THAT'S IT.
Maybe it was unclear that Peet and Hoijui are the java users in that block of posts ending in mine.
Peet wrote:and then inverting bits 0-7 and adding 1 when the input is negative
hoijui wrote:i think you have to use the two complement.
http://en.wikipedia.org/wiki/Two's_complement
if you do not know it already.. it seems to be explained complex on wiki...
works like this: take the number in binary format, change 1's to 0's and vice versa, and add 1 to the resulting number (i think it works like this, and i guess that is waht would give you the real numbers).

Re: Using the resource maps

Posted: 18 Mar 2009, 16:25
by Auswaschbar
@hoijui: you need to create a vector with the double size of the metal map, and copy the corrected values each time an AI access it. I would avoid it whenever possible and only do it for AIs that really need it

Re: Using the resource maps

Posted: 18 Mar 2009, 17:02
by imbaczek
short javaIsDumb = byteValue >= 0? byteValue : byteValue+128;

adding 256 won't solve anything...

Re: Using the resource maps

Posted: 18 Mar 2009, 17:07
by lurker
imbaczek wrote:short javaIsDumb = byteValue >= 0? byteValue : byteValue+256;

adding 256 will solve anything...
FTFY

(well, it still depends on if you have to cast to short before you add to it, I have no idea)

Re: Using the resource maps

Posted: 18 Mar 2009, 17:38
by hoijui
resource maps will be fetched once by AI per game (if they do not use a cached version anyway).

i made a list with all callback functions that contain unsinged data type. it are really few, and with the possible exception of the four map fetcher functions, i would say the memory usage difference would not have a big impact.

Code: Select all

unsigned int (CALLING_CONV *Clb_UnitDef_getCategory)(int teamId, int unitDefId);
unsigned int (CALLING_CONV *Clb_UnitDef_getNoChaseCategory)(int teamId, int unitDefId);
unsigned int (CALLING_CONV *Clb_UnitDef_WeaponMount_getBadTargetCategory)(int teamId, int unitDefId, int weaponMountId);
unsigned int (CALLING_CONV *Clb_UnitDef_WeaponMount_getOnlyTargetCategory)(int teamId, int unitDefId, int weaponMountId);
unsigned char (CALLING_CONV *Clb_Unit_CurrentCommand_getOptions)(int teamId, int unitId, int commandId);
unsigned int (CALLING_CONV *Clb_Unit_CurrentCommand_getTag)(int teamId, int unitId, int commandId);
unsigned char (CALLING_CONV *Clb_Group_OrderPreview_getOptions)(int teamId, int groupId);
unsigned int (CALLING_CONV *Clb_Group_OrderPreview_getTag)(int teamId, int groupId);
unsigned int (CALLING_CONV *Clb_Map_getChecksum)(int teamId);
int (CALLING_CONV *Clb_Map_0ARRAY1VALS0getLosMap)(int teamId, unsigned short losValues[], int losValues_max);
int (CALLING_CONV *Clb_Map_0ARRAY1VALS0getRadarMap)(int teamId, unsigned short radarValues[], int radarValues_max);
int (CALLING_CONV *Clb_Map_0ARRAY1VALS0getJammerMap)(int teamId, unsigned short jammerValues[], int jammerValues_max);
int (CALLING_CONV *Clb_Map_0ARRAY1VALS0REF1Resource2resourceId0getResourceMap)(int teamId, int resourceId, unsigned char resources[], int resources_max);
unsigned int (CALLING_CONV *Clb_WeaponDef_getOnlyTargetCategory)(int teamId, int weaponDefId);
unsigned int (CALLING_CONV *Clb_WeaponDef_Shield_getInterceptType)(int teamId, int weaponDefId);
unsigned int (CALLING_CONV *Clb_WeaponDef_getInterceptedByShieldType)(int teamId, int weaponDefId);
unsigned int (CALLING_CONV *Clb_WeaponDef_getCollisionFlags)(int teamId, int weaponDefId);
unsinged int for categories
unsinged char for command options
unsinged int for tags
unsinged int for checksum
unsinged int for types
unsinged int for flags

... ouh WAIT!
genious at work now...
i could add a second to each of these functions!
instead of:

Code: Select all

unsigned int (CALLING_CONV *Clb_UnitDef_getCategory)(int teamId, int unitDefId);
we would have:

Code: Select all

unsigned char (CALLING_CONV *Clb_Unit_CurrentCommand_0UNSIGNED0getOptions)(int teamId, int unitId, int commandId);
short         (CALLING_CONV *Clb_Unit_CurrentCommand_0SIGNED0getOptions)(int teamId, int unitId, int commandId);
though, what would we do with eg this:

Code: Select all

unsigned int (CALLING_CONV *Clb_WeaponDef_getCollisionFlags)(int teamId, int weaponDefId);
unsinged int would have to be long long i guess (JNA sais, that is always 64bit)
or.. when i look at the functions using unsinged int, they all look as if they would never need such a big range anyway.