NTai XE10.1b - Page 63

NTai XE10.1b

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

Moderators: hoijui, Moderators

User avatar
AF
AI Developer
Posts: 20687
Joined: 14 Sep 2004, 11:32

Post by AF »

I used todo that with distance, and I'll see about countering in distance again.
User avatar
DJ
Posts: 355
Joined: 17 Jan 2007, 13:26

Post by DJ »

wicked :-)
User avatar
lale
Posts: 73
Joined: 29 Apr 2007, 08:36

Post by lale »

AF:
Now that your room has cool a little, I├óÔé¼Ôäóm warming this thread with a possible solution to Solobuild and SingleBuild :-)

But first I need to clear some assumptions and misconceptions about the flow during a UnitIdle call.
I├óÔé¼Ôäóve tried to reconstruct the flow of Unitidle:

Code: Select all

CManifacture:UnitIdle (unit)
 If unit has NO tasklist then goto ├óÔé¼┼ôhandle attacker├óÔé¼┬Ø // CActions
 If unit has unit name in tasklist then Goto CUnitConstructionTask::Init

 CKeywordConstructionTask::Init
 CUBuild::operator
 Planning::feasable
 CKeywordConstructionTask::Build
 If (CUBuild::OkBuildSelection == false) //check for solobuild
 {
       Push(CMD_REPAIR);
       exit; //Exit unitidle, do new order
 }
	Boost::Thread CManifacture->BuildPlacement(unit, unitpos, UnitDef, target, spacing)

 If  (CKeywordConstructionTask::RecieveMessage(CManifacture::BuildPlacement) == false) {
		Return; // No BuildPos, exit Unitidle, do Next task
 }

 CKeywordConstructionTask::RecieveMessage: "G->OrderRouter -> Giveorder"
 CAICallback::GiveOrder
 CKeywordConstructionTask::RecieveMessage: "creating plan."
 Push(G->Cached->solobuilds[name]) //lock solobuild
	Return; //Wait for new idle
Is this a reasonable representation of the flow, for an idle construction unit with b_power as task :?: (Don't correct names, just tell me if I got the general idea)

Also I├óÔé¼Ôäóm assuming that a new thread is started every time a unit needs to find a position for building. An alternative might be that a task-thread is started during NTAI init, and this thread has a stack of position hunting orders, it process in FIFO.
(Eg. the Boost::Thread in above is substituted for SendMessage/SendOrder/SendParameters)
So which solution does NTAI use, one thread for each position hunt, or one single thread (not a spring thread, separate), doing all position hunting in sequence :?:
User avatar
AF
AI Developer
Posts: 20687
Joined: 14 Sep 2004, 11:32

Post by AF »

One thread for each search. 5 searches 5 threads and so on.

CManufcturer is now simply a helper class that manages construction plans and so on.

NTai recieves a UnitIdle() call so it looks up an array attatched to the "unitidle" string, creates a "unitidle" message and fires it off to each object in that array. These objects are all inherited from IModule, they could be tasks, helpers, etc.

Every unit has an instance of CUnit which is registered to recieve these events. CUnit manages the task cycle.

It will keep passing the first task in its list events untill that task is flagged as valid=false, after which it is discarded and the next task is moved up and initialized.

For a task to build a "corvlab" a core vehicle lab, it would spawn a CUnitConstructionTask.
It would then call Init() on that task.
Inside Init it would sort out exactly what needs building and wether to go ahead with it taking into account exclusion zones and other tags based on the builders location.

When that is done the task calls a function in the build placement algorithm passing a smart pointer, and the details necessary to find a build position.

The build algorithm then runs a series of checks to determine the placement algorithm, radar placement, mex placement, geothermal placement.

When the algorithm is finished it sends a message containing the position to the task object and exits.

If none of the checks checkout and no algorithm is ran, all the parameters are passed to a boost thread object and a buildalgorithm thread is spawned. The function then exits.

When the algorithm completes it sends the position back to the task and the thread ends.

When the task recieves the final position, it does one or two final checks incase an error value has been returned. It then prepares a command object with all the necessary data and sends it off to the command handler. If the command handler returns true a plan is created. If it returns false the task is flagged as invalid and gets deleted.

When the command handler recieves the command object it places it in a queue after a set of checks. If the command fails these checks then the command is discarded and a false is returned. At set intervals NTai will send a set amount of commands to NTai to prevent overflowing the engine with unit commands.

The task then waits. During the tasks Init function it will have registered itself to recieve unitidle and unitmovefailed and unitdestroyed events. It will wait for these events. When unitidle or unitmovefailed is recieved it will assume the task has completed and exit. If unitdestroyed is recieved and matches the unit it of the builder then the task ends too. The task is then flagged as invalid and CUnit moves onto the next task.

Perhaps it would be more efficient to handle build algorithm searches using a set of worker threads that works through a shared pool of points. But I havent given it much thought as NTai rarely gives my cpu much to think about, possibly one of the downsides to having a core 2 duo e6600.
User avatar
lale
Posts: 73
Joined: 29 Apr 2007, 08:36

Post by lale »

Thanks for the very elaborate answer, I just need to clear the question in red below, so I can surgest a solution.
AF wrote: One thread for each search. 5 searches 5 threads and so on.

CManufcturer is now simply a helper class that manages construction plans and so on.
OK so I got the general concept of the task flow, but messed all the names.
AF wrote: For a task to build a "corvlab" a core vehicle lab, it would spawn a CUnitConstructionTask.
When you say "spawn" do you mean a new boost::thread, or just an event/class call?
(I need to know how many threads are involved in the task flow, from unitidle to new unit command, as each thread can lead to relaxed solobuild.)
AF wrote: The build algorithm then runs a series of checks to determine the placement algorithm, radar placement, mex placement, geothermal placement.

When the algorithm is finished it sends a message containing the position to the task object and exits.
Basically it tries to do a simple buildplacment find, and if failed, will spawn a thread doing some serious buildplacment hunting.
AF wrote: If none of the checks checkout and no algorithm is ran, all the parameters are passed to a boost thread object and a buildalgorithm thread is spawned. The function then exits.

When the algorithm completes it sends the position back to the task and the thread ends.
OK and it is this thread, than can be overtaken by another construction unit, leading to imperfect solobuild, right :?:
AF wrote: Perhaps it would be more efficient to handle build algorithm searches using a set of worker threads that works through a shared pool of points. But I havent given it much thought as NTai rarely gives my cpu much to think about, possibly one of the downsides to having a core 2 duo e6600.
Each solution has good and bad points, the current implementation works with 5% CPU usage on a 2GHz Athlon single core, this is GOOD performance.
User avatar
AF
AI Developer
Posts: 20687
Joined: 14 Sep 2004, 11:32

Post by AF »

One thread for each search. 5 searches 5 threads and so on.
As I said, it creates a brand new thread each time it searches for a build position.

That means a NEW boost thread object. 5 searches 5 threads, 10 sarches 10 threads, N searches N threads. Each search is inside its own independant thread.
User avatar
lale
Posts: 73
Joined: 29 Apr 2007, 08:36

Post by lale »

AF wrote:5 searches 5 threads
OK I├óÔé¼Ôäóm going to assume :!: , that the only thread(s) "relaxing" solo- / singlebuild is the BuildPlacement SEARCH thread(s).

I├óÔé¼Ôäóve got 2 solutions for you:

1) Based on Boost::Mutex array
Bad:
  • ├óÔé¼┬ó Only 1 thread of BuildPlacement per unittype seeking construction.
    ├óÔé¼┬ó Possible larger footprint of NTAI.dll (few kilobytes)
    ├óÔé¼┬ó Static max number of unit types. (eg. max 5000 different units in mod)
    ├óÔé¼┬ó Slightly less performance on quad cores.
Good:
  • ├óÔé¼┬ó 100% reliable Solo and Singlebuild
    ├óÔé¼┬ó No deadlocks eg. No solobuild becomes singlebuild.
    ├óÔé¼┬ó Slightly less stutters on single core.
    ├óÔé¼┬ó No wasted BuildPlacment searches on solo- or singlebuilds.
Feature:
  • ├óÔé¼┬ó Reduced sequential Solobuilding of unittype as default behavior.
    ├óÔé¼┬ó Reduced assist; only some units trying to do a solo- or singebuilds will assist (others will do next task instead).
    ├óÔé¼┬ó Possible to implement Singlebuild, as: Make new one, if old is destroyed.


2) Fake/pre-emptive solobuild, using double bookkeeping.
Bad:
  • ├óÔé¼┬ó Possible deadlock, eg. Solobuild becomes singlebuild, Singlebuild becomes zerobuild.
    ├óÔé¼┬ó Double size array needed for solo- and singlebuild (eg. Cached->singlebuilds with two elements per unitid)
    ├óÔé¼┬ó More complex to debug and error hunt.
    ├óÔé¼┬ó Massive sequential Solobuilding of unittype as ONLY behavior
Good:
  • ├óÔé¼┬ó 99.9% reliable Solo- and Singlebuild
    ├óÔé¼┬ó No CPU usage hit/change in respect to current NTAI
    ├óÔé¼┬ó No wasted BuildPlacment searches on solo- or singlebuilds
Feature:
  • ├óÔé¼┬ó Possible to change Singlebuild into ├óÔé¼┼ôbuild no more than x├óÔé¼┬Ø
    ├óÔé¼┬ó Massive assist; all unit trying to do a solo- or singlebuild will assist.
    ├óÔé¼┬ó Possible to implement Singlebuild, as: Make new one, if old is destroyed or not finished.


I'll post solution 1 right away. Solution 2 needs some touch-up.
User avatar
lale
Posts: 73
Joined: 29 Apr 2007, 08:36

Post by lale »

Solution 1:
Consider this example:

Code: Select all

#include <unistd.h>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/bind.hpp>
#include <iostream>

boost::mutex io_mutex[5000];  //5000 equals max number of different units in a any given spring mod.


char *targetname[4]={"none","corsolar","corwin  ","corllt  "};
char *targetnum[5]={"zero", "1st ","2nd ","1st ","1st "};
//Above strings , are just for readability

bool OkBuildSelection(string name);
{
 // FAKE! do NOT use this, instead use routine OkBuildSelection from ubuild.cpp
     if (name == ├óÔé¼┬Øname_of_unit_to_solobuild├óÔé¼┬Ø)
	return false;
return true;
}

void GetBuildPlacment(int unit, int targetid)
{
  boost::mutex::scoped_lock lock(io_mutex[targetid]);
  // The below routine does a second check, within a mutexed thread, to test if solobuild or singlebuild
  if ((OKBuildSelection(targetname[targetid]) == false)
  {
	// SendMessage(UpVector); // replace with how u normally exit buildplacment thread with a: no buildpos found.
	exit; //exit (kill) current thread;
  }

  for (int i = 0; i < 10; ++i)
  {
  usleep((300*unit*unit*unit*unit));
    std::cout << "constructor number: " << unit << " find buildpos for: " << targetnum[unit] << targetname[targetid] << " trying pos: " << i << std::endl;
  }
}

int main(int argc, char* argv[]) 
{
  //Below call is how CUnitConstructionTask. Should spawn the BuildPlacement thread. The boost:bind is IMPORTANT!
  boost::thread thrd1(boost::bind(&GetBuildPlacment, 1, 1));
  boost::thread thrd2(boost::bind(&GetBuildPlacment, 2, 1));
  boost::thread thrd3(boost::bind(&GetBuildPlacment, 3, 2));
  thrd1.join();
  thrd2.join();
  thrd3.join();
  return 0;
}
BEWARE, I added the OkBuildSelection, after doing the final compile, so there might be syntax errors in this part.
It outputs:
  • constructor number: 2 find buildpos for: 2nd corsolar trying pos: 0
    constructor number: 2 find buildpos for: 2nd corsolar trying pos: 1

    constructor number: 3 find buildpos for: 1st corwin trying pos: 0
    constructor number: 3 find buildpos for: 1st corwin trying pos: 1
    constructor number: 3 find buildpos for: 1st corwin trying pos: 2

    constructor number: 2 find buildpos for: 2nd corsolar trying pos: 2
    constructor number: 3 find buildpos for: 1st corwin trying pos: 3
    constructor number: 2 find buildpos for: 2nd corsolar trying pos: 3
    constructor number: 2 find buildpos for: 2nd corsolar trying pos: 4
    constructor number: 2 find buildpos for: 2nd corsolar trying pos: 5

    constructor number: 3 find buildpos for: 1st corwin trying pos: 4
    constructor number: 2 find buildpos for: 2nd corsolar trying pos: 6
    constructor number: 2 find buildpos for: 2nd corsolar trying pos: 7
    constructor number: 2 find buildpos for: 2nd corsolar trying pos: 8

    constructor number: 3 find buildpos for: 1st corwin trying pos: 5
    constructor number: 2 find buildpos for: 2nd corsolar trying pos: 9
    constructor number: 1 find buildpos for: 1st corsolar trying pos: 0
    constructor number: 1 find buildpos for: 1st corsolar trying pos: 1

    constructor number: 3 find buildpos for: 1st corwin trying pos: 6
    constructor number: 1 find buildpos for: 1st corsolar trying pos: 2
    constructor number: 1 find buildpos for: 1st corsolar trying pos: 3
    constructor number: 1 find buildpos for: 1st corsolar trying pos: 4

    constructor number: 3 find buildpos for: 1st corwin trying pos: 7
    constructor number: 1 find buildpos for: 1st corsolar trying pos: 5
    constructor number: 1 find buildpos for: 1st corsolar trying pos: 6
    constructor number: 1 find buildpos for: 1st corsolar trying pos: 7

    constructor number: 3 find buildpos for: 1st corwin trying pos: 8
    constructor number: 1 find buildpos for: 1st corsolar trying pos: 8
    constructor number: 1 find buildpos for: 1st corsolar trying pos: 9

    constructor number: 3 find buildpos for: 1st corwin trying pos: 9
Notice how thread 1 does NOT start before thread 2 completes. This combined with a OkBuildSelection call, just before thread starts to try positions, will make solebuild and singlebuild thread-safe.
User avatar
lale
Posts: 73
Joined: 29 Apr 2007, 08:36

Post by lale »

3 Steps to realize this solution for NTAI
Important!. Please note that I do NOT know the exact name and accurate reference to BuildPlacement algorithme entry in the following, I call it: "GetBuildPlacement"
  • 1) Insert a:

    Code: Select all

    boost::mutex io_mutex[5000];  //5000 equals max number of different units (max unit.id) in any given spring mod.
    Into the headerfile of BuildPlacement algorithm.

    2) If you do NOT already use ├óÔé¼┼ôbind├óÔé¼┬Ø in boost::thread GetBuildPlacement. Then modify it to use this:

    Code: Select all

     boost::thread thrdBP(boost::bind (&GetBuildPlacement, unit, unitpos, UnitDef, target, spacing));
    3) Change start of BuildPlacment thread to include:

    Code: Select all

     void GetBuildPlacment(unit, unitpos, UnitDef, target, spacing)
    {
      boost::mutex::scoped_lock lock(io_mutex[target.id]);
      if ((OKBuildSelection(targetname[targetid]) == false)
    	// Code here to send UpVector and end (terminate) thread)
     }
    The "OkBuildSelection", above should be replace with an accurate reference to this rutine in ubuild.cpp, probably somthing like G->CUBuild->OkBuildSelection
Presto! compile and done. (I hope :? )
User avatar
AF
AI Developer
Posts: 20687
Joined: 14 Sep 2004, 11:32

Post by AF »

At the moment the build algorithm thread is a functor.

However I've made adjustments based on what you've said and here's a test build.

http://www.darkstars.co.uk/randomfiles/NTaiLALE.rar
User avatar
lale
Posts: 73
Joined: 29 Apr 2007, 08:36

Post by lale »

OK The mux thing seems to work. But somehow the call from inside the functor to OkBuildSelection makes OkBuildSelection always return false for everything but mex├óÔé¼Ôäós ??

I├óÔé¼Ôäóv tested with this simple setup:
  • [AI]
    {
    singlebuild=corlab,corck;
    solobuild=corhp;
    antistall=0;
    [NORMAL]
    {
    Corcom=corcom;
    }
    [LISTS]
    {
    corcom=b_mex,b_radar,b_factory,corlab,corhp;
    }
The above config contains more default settings, but this is the significant parts.

This gives the log:
LOG wrote: [-] < Frame: 0 >loading contents of tasklist :: corcom :: filling tasklist with #5 items
[-] < Frame: 0 >CUnitConstructionTask::CUnitConstructionTask building :: CORLAB using builder::CORCOM
[-] < Frame: 0 >CUnitConstructionTask::CUnitConstructionTask building :: CORHP using builder::CORCOM
[-] < Frame: 0 >loaded contents of tasklist :: corcom :: loaded tasklist at 5 items
[-] < Frame: 0 >CKeywordConstructionTask::Init b_mex
[-] < Frame: 0 >CKeywordConstructionTask::Build() :: CORMEX
[-] < Frame: 0 >CKeywordConstructionTask::RecieveMessage G->OrderRouter->GiveOrder(tc)== true :: CORMEX
[-] < Frame: 0 >CKeywordConstructionTask::RecieveMessage wiping and creaiing the plan :: CORMEX
[-] < Frame: 3 >issuing command in update()
[-] < Frame: 3 >Command: ID: -271 Timeout: 374 params: 328, 40.6084, 536, 0, source of command: CBuild
[-] < Frame: 3 >issuing command in update() succeeded
[00:15] < Frame: 476 >Value tasklists\normal\cormex missing in file buffer
[00:15] < Frame: 476 >Value tasklists\lists\cormex missing in file buffer
[00:15] < Frame: 476 > error loading tasklist :: CORMEX :: buffer empty, most likely because of an empty list
[00:18] < Frame: 541 >next task?
[00:18] < Frame: 541 >CKeywordConstructionTask::Init b_radar
[00:18] < Frame: 541 >CKeywordConstructionTask::Build() :: CORRAD
[00:18] < Frame: 541 >CKeywordConstructionTask::RecieveMessage BuildPlacement returned UpVector or some other nasty position
[00:18] < Frame: 555 >next task?
[00:18] < Frame: 555 >CKeywordConstructionTask::Init b_factory
[00:18] < Frame: 555 >CKeywordConstructionTask::Build() :: CORSY
[00:18] < Frame: 555 >CKeywordConstructionTask::RecieveMessage BuildPlacement returned UpVector or some other nasty position
[00:19] < Frame: 595 >next task?
[00:19] < Frame: 595 >CUnitConstructionTask::Init :: CORLAB
[00:19] < Frame: 595 >BuildPlacement returned UpVector or some other nasty position, a build location wasn't found!
[00:19] < Frame: 597 >next task?
[00:19] < Frame: 597 >CUnitConstructionTask::Init :: CORHP
[00:19] < Frame: 597 >BuildPlacement returned UpVector or some other nasty position, a build location wasn't found!
[00:23] < Frame: 719 >next task?
[00:23] < Frame: 719 >CUnitConstructionTask::Init :: CORHP
[00:23] < Frame: 719 >loading contents of tasklist :: corcom :: filling tasklist with #5 items
[00:23] < Frame: 719 >CUnitConstructionTask::CUnitConstructionTask building :: CORLAB using builder::CORCOM
[00:23] < Frame: 719 >CUnitConstructionTask::CUnitConstructionTask building :: CORHP using builder::CORCOM
[00:23] < Frame: 719 >loaded contents of tasklist :: corcom :: loaded tasklist at 5 items
[00:23] < Frame: 719 >CKeywordConstructionTask::Init b_mex
[00:23] < Frame: 719 >CKeywordConstructionTask::Build() :: CORMEX
[00:23] < Frame: 719 >CKeywordConstructionTask::RecieveMessage G->OrderRouter->GiveOrder(tc)== true :: CORMEX
[00:23] < Frame: 719 >CKeywordConstructionTask::RecieveMessage wiping and creaiing the plan :: CORMEX
[00:24] < Frame: 720 >issuing command in update()
[00:24] < Frame: 720 >Command: ID: -271 Timeout: 1093 params: 664, 36.0579, 1256, 0, source of command: CBuild
[00:24] < Frame: 720 >issuing command in update() succeeded
My hunch, is that this has somthing to do with the scope of variables used inside OkBuildSelection.
Investigating.....
User avatar
AF
AI Developer
Posts: 20687
Joined: 14 Sep 2004, 11:32

Post by AF »

or a missing exclamation mark in an if statement
User avatar
AF
AI Developer
Posts: 20687
Joined: 14 Sep 2004, 11:32

Post by AF »

redownload
User avatar
lale
Posts: 73
Joined: 29 Apr 2007, 08:36

Post by lale »

Well the new redownload, solved the ├óÔé¼┼ônot build anything but mex├óÔé¼┬Ø problem.
And NTAI seems to be working as usual.
BUT:
This has had no impact on solobuild (and probably not on single build either)

First: I had expected to se some: (when CORHP is solobuild)
  • [00:19] < Frame: 597 >next task?
    [00:19] < Frame: 597 >CUnitConstructionTask::Init :: CORHP
    [00:19] < Frame: 597 >BuildPlacement returned UpVector or some other nasty position, a build location wasn't found!
But this is not entered anywhere in the log. :(

Also:
For this, and the previous 9.73 build is see this in the log:
LOG wrote:
[09:28] < Frame: 17063 >next task?
[09:28] < Frame: 17063 >CKeywordConstructionTask::Init b_estore
[09:28] < Frame: 17063 >Given the go ahead :: HeerLightVehYard
[09:28] < Frame: 17063 >Given the go ahead :: HeerGunyard
[09:28] < Frame: 17063 >Given the go ahead :: HeerMedVehYard
[09:28] < Frame: 17063 >Given the go ahead :: HeerAssaultyard
[09:28] < Frame: 17063 >Given the go ahead :: SupplyBunker
[09:28] < Frame: 17063 >CKeywordConstructionTask::Build() :: HeerMedVehYard
[09:28] < Frame: 17063 >CKeywordConstructionTask::Build solobuild HeerMedVehYard
[09:28] < Frame: 17063 >Given the go ahead :: HeerMedVehYard
[09:28] < Frame: 17063 >CKeywordConstructionTask::RecieveMessage G->OrderRouter->GiveOrder(tc)== true :: HeerMedVehYard
[09:28] < Frame: 17063 >CKeywordConstructionTask::RecieveMessage wiping and creaiing the plan :: HeerMedVehYard
[09:28] < Frame: 17064 >issuing command in update()
[09:28] < Frame: 17064 >Command: ID: -60 Timeout: 20163 params: 560, 169.602, 1264, 0, source of command: CBuild
[09:28] < Frame: 17064 >issuing command in update() succeeded
[09:28] < Frame: 17065 >next task?
Here HeerMedVehYard is solobuild ├óÔé¼┼ôdetected├óÔé¼┬Ø (eg another one is being constructed) and yet the CKeywordConstructionTask, still calls Build() with this, why :?:

Something tells me the issue with solo and single build is not only with the BuildPlacement-thread overtaking a previous solobuilded search.

Btw: in case you are interested in the mutex solution, it├óÔé¼Ôäós from this article:
http://www.ddj.com/dept/cpp/184401518
User avatar
AF
AI Developer
Posts: 20687
Joined: 14 Sep 2004, 11:32

Post by AF »

Code: Select all

t = building->name;
	trim(t);
	tolowercase(t);
	if(G->Cached->solobuilds.find(t)!= G->Cached->solobuilds.end()){
		NLOG("CKeywordConstructionTask::Build  G->Cached->solobuilds.find(name)!= G->Cached->solobuilds.end()");
		G->L.print("CKeywordConstructionTask::Build  solobuild " + building->name);
		if(G->Actions->Repair(unit,G->Cached->solobuilds[t])){// One is already being built, change to a repair order to go help it!
			End();
			return;
		}
	}
User avatar
lale
Posts: 73
Joined: 29 Apr 2007, 08:36

Post by lale »

OK I got the message, :| the problem is still due to multithreading (though I do not know what the ├óÔé¼┼ôEnd()├óÔé¼┬Ø statement does)

So basically in the log:
[09:28] < Frame: 17063 >CKeywordConstructionTask::Build() :: HeerMedVehYard
[09:28] < Frame: 17063 >CKeywordConstructionTask::Build solobuild HeerMedVehYard
[09:28] < Frame: 17063 >Given the go ahead :: HeerMedVehYard
[09:28] < Frame: 17063 >CKeywordConstructionTask::RecieveMessage G->OrderRouter->GiveOrder(tc)== true :: HeerMedVehYard
Just after the ├óÔé¼┼ôBuild() solobuild├óÔé¼┬Ø, another thread must have overtaken, and the ├óÔé¼┼ôGiven the go ahead├óÔé¼┬Ø must come from this thread. :(

Anyway I owe you solution nr 2, though I├óÔé¼Ôäóm not very optimistic about this one.
- In fact I would suggest you do NOT spend time implementing this unless you think it has a good chance of success.

Solution nr 2.
(in the below, target refers to a unit that ntai tries to construct)

When CKeywordConstructionTask::Build() is called, it will first do the normal solobuild check.
After this it will check if the target is in solobuild list, and if, then always push the target to solobuld cache, this way making a pre-emptive reservation.
Then, if necessary, run the build algorithm thread.
If the build algorithm returns a UpVector or other BadPos messages then CKeywordConstructionTask will clear the solobuild cache item for the target. This way making it possible for other constructores to try to build the target again.

However the above has a big hole!
What happens if the construction unit is destroyed during build placement hunting (thread), then the solobuild will never be cleared!
The solution is to have UnitDestroyed remove this target from the solobuild cache.
But since UnitDestroyed does not know the target name, the solobuild cache will have to be expanded with a new element: the unit number of the constructor constructing the target.
This means that every part of ntai code that pushed a new element to the solobuild cache, must also push the unitnumber of the constructor to the cache.
User avatar
AF
AI Developer
Posts: 20687
Joined: 14 Sep 2004, 11:32

Post by AF »

not necessarily.

If I specify the unit id of the unit being built for solobuild as -1, and switch a flag in the task then the task can listen for unitdeath and perform the removal if the value is still -1.

If the unit does manage to start construction though, then the targets ID will overwrite the -1.

This would eb an ideal solution because threading isnt the only delay were this could occur, ti could occur while the units enroute to the construction site.
User avatar
AF
AI Developer
Posts: 20687
Joined: 14 Sep 2004, 11:32

Post by AF »

End() flags the task as invalid and removes the task form all listening modules
User avatar
lale
Posts: 73
Joined: 29 Apr 2007, 08:36

Post by lale »

Ok I├óÔé¼Ôäóm going back to solution 1 for a moment.
In the below I made two important assumptions:

Code: Select all

struct GetBuildPlacement
{
GetBuildPlacement(int unit,pos unitpos,unitdef UnitDef,int target,int spacing) : unit(unit){}
void operator()() 
{ 
  boost::mutex::scoped_lock lock(io_mutex[target.id]); 
  if ((OKBuildSelection(targetname[targetid]) == false) 
   // Code here to send UpVector and end (terminate) thread) 
 }
First the boost::mutex MUST be followed by a test of solobuild, which sends a UpVector message and do return; (return from thread). Also the boost:mutex must be ├óÔé¼┼ôearly├óÔé¼┬Ø (first) in the thread, so no ├óÔé¼┼ôGiveorder├óÔé¼┬Ø is messaged from this thread before the boost:mutex is performed.

Second the ├óÔé¼┼ôtarget.id├óÔé¼┬Ø is the same number for every corsolar ever built in the game, and NOT a unit number.
Said in another way: the target.id is related directly to the target.name (corsolar). This is on a one-to-one basis eg. exactly one target.id for each target name.

eg:

Code: Select all

First  corsolar build has unitnumber 235 and target.id: 344
Second corsolar build has unitnumber 244 and target.id 344
First  corllt    build has unitnumber 254 and target.id 235
Second corllt    build has unitnumber 276 and target.id 235
321├óÔé¼Ôäóth corllt    build has unitnumber xxx and target.id 235
This second assumption is VERY important. Is it correct in respect to the current implimentation? :?:
User avatar
AF
AI Developer
Posts: 20687
Joined: 14 Sep 2004, 11:32

Post by AF »

Like this?

Code: Select all

	void operator()(){
		boost::mutex::scoped_lock lock(io_mutex[building->id]);
		if(!reciever->IsValid()) return;
		CUBuild b;
		b.Init(G,builder,0);
		if(!b.OkBuildSelection(building->name)){
			CMessage m("buildposition");
			m.AddParameter(UpVector);
			reciever->RecieveMessage(m);
			return;
		}
taken from the build already uploaded and tested by you.
Post Reply

Return to “AI”