Java AI Interface for Spring - Page 16

Java AI Interface for Spring

Here is where ideas can be collected for the skirmish AI in development

Moderators: hoijui, Moderators

User avatar
hughperkins
AI Developer
Posts: 836
Joined: 17 Oct 2006, 04:14

Re: Java AI Interface for Spring

Post by hughperkins »

btw, i did the refactor and the java interface. it is written all over the place ;-)
Nice! :-) I'm very impressed so far, although I'll be more impressed once I get it compiling / running :-DDD
The main problem with the java interface is, that if the java code fails to compile, it wont retry to compile it again. this has to do with CMakes inability to handle Java sources well (SCons is not really better there, though).
so if it fails, you best trigger a rebuild with touch AI/Interfaces/Java/src/native/JavaBridge.c.
if you dont want to/have to build all of spring, this should be enough:
An issue I have is that to actually build Spring itself seems to need a lot of dependencies, about 500 meg or so, which is a lot with a 4gigabyte ssd hard-disk ;-)

Is there some way of getting cmake to run without needing to bring in the whole set of dependencies for Spring? I just need to build the AI stuff right? which probably doesn't need 10 billion boost libraries and so on I guess?
User avatar
hoijui
Former Engine Dev
Posts: 4344
Joined: 22 Sep 2007, 09:51

Re: Java AI Interface for Spring

Post by hoijui »

:D ouh man!
ein leidensgenosse!! ;-)

i am also cronically short on resources like memory, HD space and CPU time. soo.. lucky you, i can understand you there ;-)
you are right, in that for only compiling the Java interface, and probably one of the Java AIs for testing, you would not need most of the dependencies that spring needs (no boost, no SDL, no openAL, ...).
The build system is just not made for this situation :/
There are two possibilities t ochange that:

1. We allow cmake configure on spring to succeed, even if the system does not meet springs requirements, but only the java interfaces, or any other part of it that can be compiled stand alone.
As 99.9% of the ppl compiling spring, want to compile the engine, this approach is only going to cause more confusion for users, i think; the other devs would not like it. ;-)
2. The whole build system (i am thinking on CMake) would have ot be refactored. Globally interesting configure stuff, like initializing variables like SYNCDEBUG or BINDIR and LIBDIR, plus CMAKE_CXX_FLAGS and CMAKE_CXX_FLAGS_DEBUG, would have to be moved to separate files, which could be included by submodules (like the java interface) when they are configured stand alone.

the second approach seems to be better to me, though i dont know how much trouble it would casue if you use the global and the submodule configure in the same repo.

anyway.. neither of the two is going to happen soon, i think, so you are still not so lucky, sorry. the best option for you, it seems, is to beg the repository maintainer to include the java interface. for Ubuntu, it is YokoZar, and the related topic is here:
http://springrts.com/phpbb/viewtopic.ph ... 26#p372126
... i did hte begging myself already, as you can see.
User avatar
hughperkins
AI Developer
Posts: 836
Joined: 17 Oct 2006, 04:14

Re: Java AI Interface for Spring

Post by hughperkins »

Contents:
1. building without needing spring dependencies
2. dynamic java loader

1. building without needing spring dependencies

Ok, so in the end I hacked the CMakeLists.txt files: the one in spring root, and the one in rts. The one in rts is needed to build streflop.

And that worked.

As you say, I suppose the long-term solution is to have the Java interface as part of all distributions of a release.

Otherwise, perhaps we could be able to turn on a switch, maybe through some kind of environment variable, like BUILD_AI_ONLY or something perhaps, so that only the AIs are built?

Here are the hacked cmakelist files in case they are useful:

From root: http://pastebin.com/f3fbdd13c
From rts: http://pastebin.com/f6d9e4ac5

2. dynamic java loader

It takes several minutes for Spring to start up on my machine, so it makes development quite tedious. It would be nice to be able to reload an AI dynamically, and here is a draft proof-of-concept solution.

Once loaded, one just types in chat:

+reload-ai

... to reload the ai, after tweaking and rebuilding the underlying ai.

Longer-term, I see there as being two possibilities for making this generally available and stable:

1. just publish it as an ai, and specify the path to another ai in the chat:

+reload-ai MySuperAI 0.1

Possibly, one could specify a default ai in a config file.

2. Possibly integrate it into the java interface itself.

I kind of feel the first solution may be the best, because it keeps the java interface KISS, which is generally a good thing on the whole.

Anyway, here is a draft, proof-of-concept solution, and I'll just put this out there, and maybe someone might pick this up and make it into something more rigorous and stable?

http://manageddreams.com/Hugh1.tar.bz2

Architecture:

Java Interface -> SkirmishAI.jar -> (via Loader.loadOOAI) UnderlyingAI.jar

Both SkirmishAI.jar and UnderlyingAI.jar contain a derivative of AAOI.

SkirmishAI's AAOI delegates all the methods through to UnderlyingAI's AAOI implementation, at least, it should. At the moment, about half the AAOI methods are delegated through, eg:

Code: Select all

	@Override
	public int unitCreated(Unit unit, int builder) {
      try {
         if( underlyingAI != null ) {
             return underlyingAI.unitCreated( unit, builder );
         }
      } catch( Exception e ) {
          System.out.println("exception on unitCreated:");
          e.printStackTrace();
      }
      return 0;
	}
User avatar
hoijui
Former Engine Dev
Posts: 4344
Joined: 22 Sep 2007, 09:51

Re: Java AI Interface for Spring

Post by hoijui »

:D
this already exists ;-)

there is no aireload, but there is /aikill and /aicontrol, which issued in this order, are equal to an ai reload. actually i will make an /aireload, is very easy to do of course, with these two commands already there.

as said, you should autojoin the #ai channel in the lobby, it will save you time ;-)

there are also some Java AI projects that do it similarly as you do it. though they are not as general as your idea.
i dont see much advantages in your solution, except that it saves the time to reload the JVM when reloading an AI, but this could be easyly prevented through running a NullJavaAI on an other team, and just reloading your own AI with /aikill /aicontrol.

to use these commands, just use them in the ingame chat. you should get a short command description, explaining the arguments.
User avatar
hughperkins
AI Developer
Posts: 836
Joined: 17 Oct 2006, 04:14

Re: Java AI Interface for Spring

Post by hughperkins »

there is /aikill and /aicontrol, which issued in this order, are equal to an ai reload
Oh! Cool! That sounds good!

It doesn't seem to work for me. I guess I need a later version of the Java Interface? I'm using the source-code in the 0.79.1.2 tarball.
imbaczek
Posts: 3629
Joined: 22 Aug 2006, 16:19

Re: Java AI Interface for Spring

Post by imbaczek »

upgrade to 0.80.2.
User avatar
hughperkins
AI Developer
Posts: 836
Joined: 17 Oct 2006, 04:14

Re: Java AI Interface for Spring

Post by hughperkins »

Hi Robin,

Contents
======

1. Impressed with Java Interface
2. Drawing functions
3. Stability of OOAI interface

1. Impressed with Java Interface
====================

Wow, so far I find the Java OO interface extremely impressive. I'm very impressed with it. I don't know how efficient it is (haven't checked or tested yet), but I really like that the events come down containing "Unit"s rather than integer ids. I really like that you've factored things down, so we do aicallback.getMap().something or aicallback.getGame().something-something.

2. Drawing functions
=============

Could we possibly add the following two functions please? I use them in my C# AI, that I am in the process of porting to Java. I've ported 33 classes, there are 25 left, so it's well in progress.

The two functions, which I find very useful, are:

Code: Select all

aicallback.DrawUnit(targetunitname.toUpperCase(), buildsite, 0.0, 200, aicallback.getGame().getMyAllyTeam(), true, true);
and:

Code: Select all

figuregroup = aicallback.CreateLineFigure(new AIFloat3((x + 1) * multiplier, elevation, y * multiplier), new AIFloat3((x + 1) * multiplier, elevation, (y + 1) * multiplier), 10, false, 200, figuregroup);
3. Stability of OOAI interface
==================

One other point: I notice that a couple of the OOAI events have had WeaponDef weaponDef, boolean paralyzed added to them. Don't suppose... could it be possible to start providing backwards compatibility on the interface from this point on? In other words, in the future, if something changes, let's say we have a new parameter "int photonPower", then the interface could change from:

Code: Select all

OOAI{
 ...
int UnitAttacked( Unit unit, Unit attacker, ... , boolean paralyzed );
 ...
}
to:

Code: Select all

OOAI{
 ...
int UnitAttacked( Unit unit, Unit attacker, ... , boolean paralyzed );
int UnitAttacked( Unit unit, Unit attacker, ... , boolean paralyzed, int photonPower );
 ...
}
... this way, as long as we extend the adapter class AbstractOOAI, then AIs written in Java will be stable, and won't break at each release, which is the opposite of the current situation with C++ AIs et al.

If you could do this, then the number of working Java AIs would naturally increase over time, which would make the Java AI interface look really good, and make AI developers like myself really happy!

Eventually the old events would probably need to be deleted of course, but maybe if you could keep them around for two years before doing so, then it could make developing AIs in Java really fulfilling, rather than spending one's life continually keeping up with interface changes ;-)

Hugh
User avatar
hoijui
Former Engine Dev
Posts: 4344
Joined: 22 Sep 2007, 09:51

Re: Java AI Interface for Spring

Post by hoijui »

thanks! :-)
i actually am a Java programer, and i though if i do all this ugly work, then the interface should really be Java like in the end, and not look like a java accessible C interface. Of course there are still things that are not properly Java, and they will remain most likely. Some data types in hte C interface are unsigned, and csaue Java has no unsinged types, you will have to take the twos compliment of these values in java, and not forget to use a bigger data type (eg int instead of short). This is the csae for the raw resource map, for example.

the two draw things you miss are already possible. You have to do that through commands.
You find them under the oo subpackage, and you have to send them to the engine through the callback.HandleCommand() message.

In case you are interested, and for reference, the Java interface is nearly exclusively generated from three C++ headers. If someone asks me what function does what, i usually search in these three files:
rts/ExternalAI/Interface/SSkirmishAICallback.h.h
rts/ExternalAI/Interface/AISCommands.h
rts/ExternalAI/Interface/AISEvents.h

The other thing...
it wont happen. The Java interface is auto generated for mthe C interface, and auto generating backwards compatibility is not possible.
It could work with putting the backwards compatibility into the C interface, but.. that will siply not happen casue .. spring devs (including me) will never adhere to such a strategy, cause we would be too bored, or simply casue we will not remember to do it.
Anyway, if you want your AI to stay compatible, you will have to update it anyway, spring is too dynamic to do something like real backwards compatibility. The way to go is detecting incompatibility, which is still not implemented for AIs. I know i should do that one day, but it is boring work, and needs lots of testing(-time) and in the end will not really make you think like you did somethign worthwhile.
If your AI is good, an used by lots of ppl, then it will easily find maintainers even if you are not around to do this all the time. This works well for the current AIs.
User avatar
hughperkins
AI Developer
Posts: 836
Joined: 17 Oct 2006, 04:14

Re: Java AI Interface for Spring

Post by hughperkins »

Hi Robin,

Contents:

1. AI Maintenance
2. Overriding OOAIFactory.handleEvent
3. Line drawing functions

1. AI Maintenance
If your AI is good, an used by lots of ppl, then it will easily find maintainers even if you are not around to do this all the time. This works well for the current AIs.
Ok, that makes a certain amount of sense, and is fairly reassuring to me.

2. Overriding OOAIFactory.handleEvent

There is a function in OOAIFactory called "handleEvent". I think that if I just copy and paste this into my OOAI instance, then any additions to the Event objects will not affect the ability of the code to compile? And then the method calls themselves will be effectively "frozen" into my code and immune to future modfiications?

However, at the moment I can't do that since OOAIFactory is final ;-) To what extent could it be possible to change it to no longer be final?

3. Line drawing functions
the two draw things you miss are already possible
Ok super! The drawing unit function works great!

I have an issue with the line drawing functions at the moment.

- if I use new AddLineDrawAICommand(startpos, endpos), then the line stays on the map for the duration of the game ;-) which means that functions to overlay various maps on the game-map, like metal, movement type categorizations (sea vs land vs steep land vs impassable land), tend to very quickly become an illegible mess.

- on the other hand CreateLineFigureDrawerAICommand( startpos, endpos, 2, false, 200, -1, -1) , crashes for me:

http://pastebin.org/13683

To what extent could it be possible to tweak CreateLineFigureDrawerAICommand so that it doesn't give a segmentation fault?

Alternatively, what options are available for deleting lines created using AddLineDrawAICommand, or for deleting everything drawn on the screen, for example, once a second?

Hugh
User avatar
hoijui
Former Engine Dev
Posts: 4344
Joined: 22 Sep 2007, 09:51

Re: Java AI Interface for Spring

Post by hoijui »

If i get you right with the OOAIFactory, what you want is to not it at all.
Have a look at NullJavaAI. You should be able to mix the Two concepts, eg useing the Event receiving style of NullJavaAI together with the OOAICallback.
though... i have not yet tried it.
maybe i got you wrong.
come to #ai ;-)


For removing a line, use the RemovePointDrawAICommand and give it one of the two end-points of the line.
I can see that this is not obvious ;-)
The reason it works this way, is that it simply woorks hte same way as for humans:
You erase in a location, and then all things aroudn there are deleted (points, lines markers.. i think).
So there is possibility for improvement in the AI API here, but .. dont expect this to change anywhere soon ;-)


about CreateLineFigureDrawerAICommand:
the docu says:

Code: Select all

	/// how many frames a figure should live before being auto-removed, 0 means no removal
	int lifeTime;
	/// use 0 to get a new group
	int figureGroupId;
	/// the new group
	int ret_newFigureGroupId;
so you should use 0 for the second last param, not -1, and if this does not help, use width = 1 (no idea if different width's are supported).
If both of this does not help, i would need further info from you. i cnat do anything wiht the stack trace you gave me.
...well maybe i could, if i had the eaxct smae version of spring you have, and would try translating the addresses by hand...
would like not to do that ;-)
User avatar
hughperkins
AI Developer
Posts: 836
Joined: 17 Oct 2006, 04:14

Re: Java AI Interface for Spring

Post by hughperkins »

1. Re: Erasing line drawing
2. Performance: Eek!

1. Re: Erasing line drawing

Ok, well as a workaround for now, I've turned off all dynamic line drawing, for example, I no longer display the AI's losmap as it changes, but only on demand, and then I have a command to erease the lines from the whole map. It's not ideal, but it's ok for now.

I'll give the changes in parameters a whirl, but I did try a couple of values before, and I suspect it's something to do with the parameter named "ret_somethingsomething" being some sort of allocation parameter, that's not being allocated, and hence the segmentation fault?

2. Performance: Eek!

Ok, so I got my ai entirely ported from C# into java, and started to tinker with it. It was very slow, and I started to look into why.

I wrote a performance test, and here is the test and the results:

http://pastebin.com/f29ddf1a0

The result is the number of iterations of the test that were completed in one second.

The control test shows that the basic timing loop runs 50 million times a second, which is fast enough that other test results can be taken directly at face value.

The test of aicallback.getUnitDefs() was excellent. I assume you are caching the results of this somewhere since it is static data?

The test of aicallback.getFriendlyUnits() was pretty low, but I'm going to leave that for the moment, and move on to:

unit.getPos(). This ran 5000 times a second, for a duration of 200 microseconds per getPos call :-O

To put that into perspective, imagine we have a single loop like this, and 200 friendly mobile units:

Code: Select all

for( Unit unit : friendlymobileunits ) {
   AIFloat3 pos = unit.getPos();
   // do nothing here for now
}
This loop, on its own, with nothing inside it would take:

200 * 200 microseconds = 40000 microseconds = 40 milliseconds to run

30 frames, just for this loop would take 30 * 40 milliseconds = 1.2 seconds :-O

That's .... fairly slow!

The same is true for getUnitDef, but I can cache that per unit. Caching the pos is not really an option.

I assume the speed is a fundamental limitation of JNA, and there is nothing that can be done about it?

Kind of a shame though .... :-(((((

Hugh
User avatar
hughperkins
AI Developer
Posts: 836
Joined: 17 Oct 2006, 04:14

Re: Java AI Interface for Spring

Post by hughperkins »

Robin,

Addendum: there seems to be something called "JNA Direct Mapping:

https://jna.dev.java.net/#performance
https://jna.dev.java.net/#direct

... it looks like this gives roughly a x10 boost in performance, which sounds definitely worth having!

From the links above: "The calling overhead for a single native call using JNA interface mapping can be an order of magnitude (~10X) greater time than equivalent custom JNI [...]. In raw terms, the calling overhead is on the order of hundreds of microseconds instead of tens of microseconds."

That seems to correspond roughly to the a posteriorae getPos execution time in the tests above?

A couple of questions:
- to what extent does JavaInterface v0.1 use JNA Direct Mapping?
- don't suppose... how realistic could it be for you to add in JNA Direct Mapping, at least for certain frequently called functions, like, at a minimum for example, unit.getPos() ?

Hugh
User avatar
hoijui
Former Engine Dev
Posts: 4344
Joined: 22 Sep 2007, 09:51

Re: Java AI Interface for Spring

Post by hoijui »

...and again, you could probably have saved yourself quite some time if you had talked to me in advance ;-)
!!!! #ai !!!!

I already had a look at JNA's direct mapping, and i remember it beeing unusable for the interface, but i will have an other look at it maybe.

The slowdown, i suppose, does not come from the use of JNA, but from the extensive logging of the Java AI Interface that is on by default.
I did this cause it is still kind of in beta state (as there are no release AIs yet, except the Null*JavaAI ones, which are hardly a good test, and not used for testing by the masses anyway). You can disable/lower all the logging from the config files in your spring install under:

Code: Select all

AI/Interfaces/Java/0.1/interface.properties
AI/Interfaces/Java/0.1/jvm.properties
In the first, make sure to have a log level of 5 or lower. i think only 8 or more would cause anything near performance relevant output.
The JVM properties is where yo want to disable all logging and debugging options (both ";" and "#" can be used to out-comment stuff).
I am pretty sure that this will speed up the slow things at factors in the range of 10x or more.
cranphin
Posts: 136
Joined: 13 Jun 2005, 16:37

Re: Java AI Interface for Spring

Post by cranphin »

Heeey!! :)

I was just about to say something about unit.getPos, and unit.getDef XD
These seem to eat up by far the biggest chunk of time in my kaik port, on a quick initial profiler run (See the attached pic. :) ).

I'll cache positions per frame I guess, this will atleast lessen the damage a bit :)
UnitDefs can be cached for as long as a unit lives, which should help too :)

Might be usefull integrating this in the Unit class or so though, OOAI, something :)

But yes, it'd be very nice if this can be solved/improved in the jna layer too, mayby the jna devs have some ideas?

This is important, the performance hit this gets can kill the whole idea of a Java AI :D

Cheers! :)
Attachments
profiler.JPG
(164.2 KiB) Downloaded 42 times
User avatar
hoijui
Former Engine Dev
Posts: 4344
Joined: 22 Sep 2007, 09:51

Re: Java AI Interface for Spring

Post by hoijui »

disabling all the logging and debugging gave about 20% performance increase (tested by hughperkins). so not nearly enough :/

so there are two possible ways to go:
1. make the java->native calls faster
2. caching

caching is already implemented to some extend, eg in unitDef, but not in unit. the reason is that it is much more complex there, as said, some things can be stored as long as the unit is alive, others only per frame...
all those things have in common that they need input from the engine, eg the events have to be connected to the callback, which adds complexity again, plus we have to manually define for each function how long it┬┤s values can be cached, and then there is dependency on the parameters of the function, and we will have to prevent cache fro mbeeing to big, ...
i see a lot of work, and a lot of bugs in this approach.

making the calls faster could be achieved through the mentioned direct mapping, perhaps. It would need some changes in the interface, as direct mapping can only be done when callign functions, but we are currently directly using the function pointers the engine supplies. so ther would have to be a function to function pointer mapping on the C side of the interface, and then changes on the Java side to do function calls instead of function-pointer calls, and then little changes to do direct mapping. The problem there is, that the interface uses String[] in some places, which is not supported by JNA direct mapping.

Before doing anything, we should be very clear where we loose the most performance.

.. omg. suddenly got very tired, get back tomorow again.
User avatar
hoijui
Former Engine Dev
Posts: 4344
Joined: 22 Sep 2007, 09:51

Re: Java AI Interface for Spring

Post by hoijui »

i never did performance tuning of code, ever, and never did performance messurements either....

cranphin, how did you get that image? it looks Eclipse style to me. I think Netbeans as something for performance messurement too...
anyone having more experience in this area?

i though about the caching again...
The only half way clean way to do it, is to further annotate the functions in the C AI callback, the one defined in rts/ExternalAI/Interface/SSkirmishAICallback.h. The current "Annotation system" i am using there, looks like this:

Code: Select all

Clb_Economy_0REF1Resource2resourceId0getIncome
meta info included in the function name.
Even doh it is ugly, it has a few tiny pros, and made auto parsing it easier. But if we want a better buffering system, it would get too ugly, and we better change to something separate form the function name. I am thinking of two possible solutions:
1. an additional file for the meta data
2. appending the meta data in a comment next to the function pointer definition

Even doh 1. may be nicer in theory, i tend to 2. as it has some important pros in practice. The main advantage is, that with 2., when soemeone adds a function to the interface, or changes one, it is much mor likely that he will also add or change the meta info accordingly, if it is in the same file. Also, it would make parsing easier.

For the format of the meta data, i am thinking of a properties file format. An example:
old:

Code: Select all

/// Returns the commands currently in the queue of the Unit.
int (CALLING_CONV *Clb_Unit_0MULTI1SIZE1Command0CurrentCommand)(int teamId,
		int unitId);
new:

Code: Select all

/// Returns the commands currently in the queue of the Unit.
int (CALLING_CONV *Clb_Unit_CurrentCommand)(int teamId,
		int unitId);
/* #ANNOTATION#
 * multiFetcherPart = size
 * oo.class = Command
 * cache.time = frame
 * cache.scope = team
 */
This is just meant as a very rough design sketch.

I am open for better suggestions.. but of course it is only ME who decides what is better.. and.. i cant imagine anyone else hten me has better ideas...
;-) :P
no really, influence me pls!

btw, why not XML instead of properties format:
i want to make parsing as simple as possible. i use AWK for generating the Java and the new C++ AI interface, and parsing XML wiht AWK... would not be much fun, plus i dont see a need for it.
User avatar
DJ
Posts: 355
Joined: 17 Jan 2007, 13:26

Re: Java AI Interface for Spring

Post by DJ »

What are the specific methods causing the performance problems? Is it just the Unit objects? Just a stab in the dark here but are you fully populating a Unit object for every event call? -if you went back to unit id's which you could then populate a unit object for on demand might that regain some performance?
User avatar
hoijui
Former Engine Dev
Posts: 4344
Joined: 22 Sep 2007, 09:51

Re: Java AI Interface for Spring

Post by hoijui »

i dont know the critical methods (except the already mentioned ones).

no, a unit object is never fully populated. each property is requested from the engine right when you request it from the Unit java Object.
cranphin
Posts: 136
Joined: 13 Jun 2005, 16:37

Re: Java AI Interface for Spring

Post by cranphin »

hoijui wrote:cranphin, how did you get that image? it looks Eclipse style to me. I think Netbeans as something for performance messurement too...
anyone having more experience in this area?
I used JProfiler with a 10 day trial license :D Bit expensive to buy tho, but was the quickest for me to get something with :)

There's other tools that can do the same, I think the netbeans thing you mentioned is one, I'm more of a eclipse user tho ;)

Had to add some properties to the jvm.properties, but otherwise it's not too hard, as long as the profiler supports some way of remote debugging :)
cranphin
Posts: 136
Joined: 13 Jun 2005, 16:37

Re: Java AI Interface for Spring

Post by cranphin »

As for annotating, use whatever you feels is easiest in AWK, since you're the one doing it ;)

Thought more about caching too, I do see/feel some issues.

One is that KAIK cheats for finding enemy positions for example, and I realize that because we have cheating, we may get a different value for a unit position even within one frame:

-cheat on, enemyUnit.getPos() = (10,10,10)
-cheat off, enemyUnit.getPos() = (0,0,0) (actually, I don't know what value we get here XD)

But it's easy to overlook such things that would break caching :)
Mayby we should actually just leave caching to the AI developers, since they can be smart about this, but then, the OOAI interface with Unit class and such is the logical place for caching I think.

So, I don't know... :)

I would love if we could speed up the native calls somehow, I don't care how XD I'll happily rewrite against any new interface for that :)

A funny thing I noticed btw., it seems creating AIFloat3 (just new AIFloat3() ) is also heavy a bit, since it triggers jna code, something I have to be mindfull of (don't create a lot of AIFloat3 just for storage or so XD).


Also, I think the reason unit.getPos and unit.getUnitDef score so high, is cause they get called very often, for example to choose a target from the enemies, you may check the position and type of a lot of enemies.

Anyway, keep hope :D And don't rush a solution, this deserves some time and attention :)
Post Reply

Return to “AI”