DarkBlizz

Game On => Land of AI => STARCRAFT II: WINGS OF LIBERTY => AI Development => Topic started by: Kernel64 on March 08, 2010, 04:12:02 PM

Title: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Kernel64 on March 08, 2010, 04:12:02 PM
Moving the code to Mapster.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Kernel64 on March 09, 2010, 05:59:35 AM
Update: March 09, 2010
-----------------------

After some games, there's this:

The 13 Hatch (vs 7-8 Zeal):

>Once the pool is done:
     [we only make Spines if the enemy will likely send Zealots...]
          if so, 2 sunks vs 1-3 zeals must be the desired state.
          * do not make spines until our expo is done, and
          * some time have elapsed for the creep to expand, and
          * make sure the spines are at the expo

          [we prioritize lings instead of drones ONLY if the enemy has more than 3 zeals]
          *do so only if we have at least 18 drones complete or in production, and
          *if we have 1 Queen.

               if so, for every +1 Zeal of the basic (1 min to 3 max zeal):

                    add 4 lings.
                    if 5 zeals, only want 6 lings.
                    if 6 - 7 zeals, want 10 min, 12 max lings
                   *do not stock, train only when called for, and
                   *keep to the limits to avoid overspending.

Basically the subs are conditional. The script must prioritize getting it's 13 Hatch goal, unless military is required. 13 Hatch can then proceed to Lair Tech + muta.

That Lair Tech + Muta is actually another next state, but there are other sates, like mass ling 3rd expo. But those haven't been tested or considered yet.

This is the BO for 13hatch:

Drones 7-10
Ovie 2
Drones 11-13
   *wait 300 meaner'aalzz.
Expand
   Drones 13-15
      Pool
      Drones 15-18
      Ovie 3
optional: 2 Spine Crawlers (or Extractor 2)
         Extractor 1, Queen 1 (or if no spine crawler, gas mining 6)
            Drones 19-21 (if spine crawler, start gas mining)
optional: lings production
(we are ready for next state)
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Kernel64 on March 12, 2010, 02:38:37 PM
I've been trying these recently:

Declarations:
Quote
bool CountUnit(string UCheck, int Complete, int Progressing, int Queued, int Tplayer);
bool RCountUnit(string UCheck, int MinComplete, int MaxComplete, int MinProgressing, int MaxProgressing, int MinQueued, int MaxQueued, int Tplayer);
void FCountUnit(string UCheck, int MinComplete, int MaxComplete, int MinProgressing, int MaxProgressing, int MinQueued, int MaxQueued, bool CFeedback, bool PFeedback, bool QFeedback, int Tplayer);
int TotalUnit(string UCheck, int Tplayer);
void ZEnsureFood(int FoodLvL, int TPriority, int TownWhere, int Tplayer);
void ZEnsureWorker(int UCount, int TPriority, int TownWhere, int Tplayer); //, string PreConUnit, int PreConCount);
void ZEnsureTech(int UCount, string UTech, int TPriority, int TownWhere, int BFlag, int Tplayer);
void ZEnsureUnit(int UCount, string UTech, int TPriority, int TownWhere, int Tplayer);

string WeAreThrough(int Tplayer, string ThroughState);
string WeAreAt(int Tplayer);

The Functions Themselves:
Quote
//--------------------------------------------------------------------------------------------------
//  Checks and returns true if it is so. Within range this time.
//--------------------------------------------------------------------------------------------------
bool RCountUnit(string UCheck, int MinComplete, int MaxComplete, int MinProgressing, int MaxProgressing, int MinQueued, int MaxQueued, int Tplayer)
{
    if(  (AITechCount(Tplayer, UCheck, c_techCountCompleteOnly) >= MinComplete) &&
          (AITechCount(Tplayer, UCheck, c_techCountCompleteOnly) <= MaxComplete)  &&
         (AITechCount(Tplayer, UCheck, c_techCountInProgressOnly) >= MinProgressing) &&
          (AITechCount(Tplayer, UCheck, c_techCountInProgressOnly) <= MaxProgressing)  &&
         (AITechCount(Tplayer, UCheck, c_techCountQueuedOnly) >= MinQueued) &&
          (AITechCount(Tplayer, UCheck, c_techCountQueuedOnly) <= MaxQueued) ){
        return true;   
    }
    else { return false; }
}

//--------------------------------------------------------------------------------------------------
//  Checks and returns true if it is so. Range Specific bool setting.
//--------------------------------------------------------------------------------------------------
void FCountUnit(string UCheck, int MinComplete, int MaxComplete, int MinProgressing, int MaxProgressing, int MinQueued, int MaxQueued, bool CFeedback, bool PFeedback, bool QFeedback, int Tplayer)
{
    if( (AITechCount(Tplayer, UCheck, c_techCountCompleteOnly) >= MinComplete) &&
        (AITechCount(Tplayer, UCheck, c_techCountCompleteOnly) <= MaxComplete) ){
        CFeedback = true;
    }
    else { CFeedback = false; }

    if( (AITechCount(Tplayer, UCheck, c_techCountInProgressOnly) >= MinProgressing) &&
        (AITechCount(Tplayer, UCheck, c_techCountInProgressOnly) <= MaxProgressing) ){
        PFeedback = true;
    }
    else { PFeedback = false; }
    if( (AITechCount(Tplayer, UCheck, c_techCountQueuedOnly) >= MinQueued) &&
        (AITechCount(Tplayer, UCheck, c_techCountQueuedOnly) <= MaxQueued) ){
        QFeedback = true;
    }
    else { QFeedback = false; }   
}

//--------------------------------------------------------------------------------------------------
//  Counts the specified unit all including in progress and queued
//--------------------------------------------------------------------------------------------------
int TotalUnit(string UCheck, int Tplayer){
    int a = 0;
    a = (AITechCount(Tplayer, UCheck, c_techCountInProgressOnly) +
                AITechCount(Tplayer, UCheck, c_techCountQueuedOnly) +
                AITechCount(Tplayer, UCheck, c_techCountCompleteOnly));
    return a;
}

//--------------------------------------------------------------------------------------------------
//  Checks the total number of food if low, add one Ovie
//--------------------------------------------------------------------------------------------------
void ZEnsureFood(int FoodLvL, int TPriority, int TownWhere, int Tplayer){
int TotalFood = 0;

    TotalFood = (( AITechCount(Tplayer, c_ZU_Overlord, c_techCountInProgressOnly) +
                AITechCount(Tplayer, c_ZU_Overlord, c_techCountQueuedOnly) +
                AITechCount(Tplayer, c_ZU_Overlord, c_techCountCompleteOnly) ) * 8 );
    // adds hatchery count
    TotalFood = TotalFood + (( AITechCount(Tplayer, c_ZB_Hatchery, c_techCountInProgressOnly) +
                AITechCount(Tplayer, c_ZB_Hatchery, c_techCountQueuedOnly) +
                AITechCount(Tplayer, c_ZB_Hatchery, c_techCountCompleteOnly) ) * 2);
    // add lair count
    TotalFood = TotalFood + (( AITechCount(Tplayer, c_ZB_Lair, c_techCountInProgressOnly) +
                AITechCount(Tplayer, c_ZB_Lair, c_techCountQueuedOnly) +
                AITechCount(Tplayer, c_ZB_Lair, c_techCountCompleteOnly) ) * 2);
    // add hive count
    TotalFood = TotalFood + (( AITechCount(Tplayer, c_ZB_Hive, c_techCountInProgressOnly) +
                AITechCount(Tplayer, c_ZB_Hive, c_techCountQueuedOnly) +
                AITechCount(Tplayer, c_ZB_Hive, c_techCountCompleteOnly) ) * 2);
    // add overseers
    TotalFood = TotalFood + (( AITechCount(Tplayer, c_ZU_Overseer, c_techCountInProgressOnly) +
                AITechCount(Tplayer, c_ZU_Overseer, c_techCountQueuedOnly) +
                AITechCount(Tplayer, c_ZU_Overseer, c_techCountCompleteOnly) ) * 8 );               

    if ( TotalFood < FoodLvL ){
        AITrain (Tplayer, TPriority, TownWhere, c_ZU_Overlord, 1);
    }
}

//--------------------------------------------------------------------------------------------------
//  Checks the total number of worker and trains one if less
//--------------------------------------------------------------------------------------------------
void ZEnsureWorker(int UCount, int TPriority, int TownWhere, int Tplayer){ //, string PreConUnit, int PreConCount){
int TotalWorker = 0;

    TotalWorker = ( AITechCount(Tplayer, c_ZU_Drone, c_techCountInProgressOnly) +
                AITechCount(Tplayer, c_ZU_Drone, c_techCountQueuedOnly) +
                AITechCount(Tplayer, c_ZU_Drone, c_techCountCompleteOnly) );
   
// blocked, since even if AI queues pool first, it morphs drones. It must be: cheaper goes first
//    if (PreConUnit != "None") {
//        if ( ( TotalWorker < UCount ) && (TotalUnit(PreConUnit, Tplayer)==PreConCount) ){
//            AITrain (Tplayer, TPriority, TownWhere, c_ZU_Drone, 1);
//        }
//    }
//    else {
        if ( TotalWorker < UCount ) {
            AITrain (Tplayer, TPriority, TownWhere, c_ZU_Drone, 1);
        }
//    }
}

//--------------------------------------------------------------------------------------------------
//  Checks the total number of a certain tech and build 1 of it if low
//--------------------------------------------------------------------------------------------------
void ZEnsureTech(int UCount, string UTech, int TPriority, int TownWhere, int BFlag, int Tplayer){
int TotalTech = 0;

    TotalTech = ( AITechCount(Tplayer, UTech, c_techCountInProgressOnly) +
                AITechCount(Tplayer, UTech, c_techCountQueuedOnly) +
                AITechCount(Tplayer, UTech, c_techCountCompleteOnly) );
   
    if ( TotalTech < UCount ){
        AIBuild (Tplayer, TPriority, TownWhere, UTech, 1, BFlag);
    }
}

void ZEnsureUnit(int UCount, string UTech, int TPriority, int TownWhere, int Tplayer){
int TotalTech = 0;

    TotalTech = ( AITechCount(Tplayer, UTech, c_techCountInProgressOnly) +
                AITechCount(Tplayer, UTech, c_techCountQueuedOnly) +
                AITechCount(Tplayer, UTech, c_techCountCompleteOnly) );
   
    if ( TotalTech < UCount ){
        AITrain (Tplayer, TPriority, TownWhere, UTech, UCount);
    }
}

//---returns strings that describe states
string WeAreThrough(int Tplayer, string ThroughState){
   
    if( ThroughState == "GameStart") {
        if( (TotalUnit(c_ZB_Hatchery, Tplayer) == 1) &&
            ((TotalUnit(c_ZU_Overlord, Tplayer) >= 1) && (TotalUnit(c_ZU_Overlord, Tplayer) <= 2)) &&
            ((TotalUnit(c_ZU_Drone, Tplayer) >= 10) && (TotalUnit(c_ZU_Drone, Tplayer) <= 13)) &&
            (TotalUnit(c_ZB_SpawningPool, Tplayer) < 1) ) {
       
            return "DoneGameStart";
        }
        else {
            return "NotDoneGameStart";
        }
    }
   
    else if( ThroughState == "Block1") {
        if( (TotalUnit(c_ZB_Hatchery, Tplayer) == 1) &&
            ((TotalUnit(c_ZU_Overlord, Tplayer) >= 2) && (TotalUnit(c_ZU_Overlord, Tplayer) <= 3)) &&
            ((TotalUnit(c_ZU_Drone, Tplayer) >= 13) && (TotalUnit(c_ZU_Drone, Tplayer) <= 16)) &&
            (TotalUnit(c_ZB_SpawningPool, Tplayer) == 1) ) {
       
            return "DoneBlock1";
        }
        else {
            return "NotDoneBlock1";
        }
    }
    else {
        return "Error";
    }
}

//---returns strings that describe states
string WeAreAt(int Tplayer){
    if( (TotalUnit(c_ZB_Hatchery, Tplayer) == 1) &&
        (TotalUnit(c_ZU_Overlord, Tplayer) <= 1) &&
        (TotalUnit(c_ZU_Drone, Tplayer) <= 10) &&
        (TotalUnit(c_ZB_SpawningPool, Tplayer) < 1) ) {

        return "GameStart";
    }
    if( (TotalUnit(c_ZB_Hatchery, Tplayer) == 1) &&
        (TotalUnit(c_ZU_Overlord, Tplayer) == 2) &&
        (TotalUnit(c_ZU_Drone, Tplayer) == 13) &&
        (TotalUnit(c_ZB_SpawningPool, Tplayer) == 1) ) {

        return "Block1";
    }
    else {
        return "NoWhere";
    }
}

You can notice there two functions that returns strings, which is handy for having the AI stack blocks of actions. This is still very new to me, and I'm also trying to figure out a way to use Train and build with priority but without such things as this:

Quote
AISetDifficulty(player, c_diffNormalVision, false);

AIClearStock(player);

//this makes AI automate resourcing like gas structs
AIDefaultEconomy(player, c_ZB_Hatchery_Alias, null, c_ZU_Overlord_Alias, c_ZU_Drone, 45, c_stockIdle);

        //debug WeAreAt-------------------------------------------------
        UIDisplayMessage(PlayerGroupAll(), 1, StringToText(WeAreAt(player)));
        //queue debug:--------------------------------------------------
        IQU = AITechCount(player, c_ZU_Drone, c_techCountQueuedOnly);
        SQU = IntToString(IQU);
        UIDisplayMessage(PlayerGroupAll(), 3, StringToText(SQU  + " Drones"));
        IQU = AITechCount(player, c_ZB_SpawningPool, c_techCountQueuedOnly);
        SQU = IntToString(IQU);
        UIDisplayMessage(PlayerGroupAll(), 3, StringToText(SQU  + " Pool"));
        //--------------------------------------------------------------

    if ( (WeAreAt(player) == "GameStart") &&
        (WeAreThrough(player, "GameStart") == "NotDoneGameStart") ){
        //note: this thing works by queueing things. Train that is.
        //ZEnsureWorker(10, 10, c_townOne, player);
        AISetStock( player, 10, c_ZU_Drone );
    }

    if( WeAreThrough(player, "GameStart") == "DoneGameStart" ) {
        //choose next block here
    }

    // >>> BLOCK 1: [OVIE.2, +3 DRONES, +POOL] process start ______________________________
        // drones: complete 9-10, inprogress 0-1, queued 0-1
        if (RCountUnit(c_ZU_Drone, 9, 10, 0, 1, 0, 1, player) == true) {
            ZEnsureFood(18, 9, c_townOne, player);
            ZEnsureTech(1, c_ZB_SpawningPool, 10, c_townOne, c_nearCloseDropoff, player);
            ZEnsureWorker(13, 10, c_townOne, player);
        }
    // >>> OVIE.2, +3 DRONES, +POOL process end __________________________________________
        // we have 0 pool built, 1 inprogress, 0 queued
        //note: setstock doesn't come up in queue
        if (RCountUnit(c_ZB_SpawningPool, 0, 0, 1, 1, 0, 0, player) == true) {
            //ZEnsureWorker(13, 3, c_townOne, player);
            AISetStock( player, 16, c_ZU_Drone );
            ZEnsureTech(1, c_ZB_Extractor, 10, c_townOne, c_onVespeneGas, player);
            //AISetStock( player, 1, c_ZB_Extractor );
        }
        if (RCountUnit(c_ZU_Drone, 15, 16, 0, 1, 0, 1, player) == true) {
            //AISetStock( player, 16, c_ZU_Drone );
            ZEnsureUnit(1, c_ZU_Queen, 10, c_townOne, player);
            ZEnsureFood(26, 1, c_townOne, player);
        }
   
    // >>> BLOCK (undefined): [OVIE.2, +POOL, +3 DRONES] process start ______________________________
    // drones: complete 9-10, inprogress 0-1, queued 0-1
    //    if (RCountUnit(c_ZU_Drone, 9, 10, 0, 1, 0, 1, player) == true) {
    //        ZEnsureFood(18, 9, c_townOne, player);
    //        ZEnsureTech(1, c_ZB_SpawningPool, 10, c_townOne, c_nearCloseDropoff, player);
    //    }
    //    if (RCountUnit(c_ZB_SpawningPool, 0, 1, 1, 1, 0, 0, player) == true) {
    //        ZEnsureWorker(13, 3, c_townOne, player);
    //    }
    // >>> OVIE.2, +POOL, +3 DRONES process end __________________________________________
   

    if (AIEnableVeryEasyStockOpen(player, c_ZU_Drone)) {
        return;
    }

Now, all of these are placed inside the Zerg0.galaxy.

The commented lines up there, which goes into void ZergOpenGnd0 (int player) of zerg0.galaxy, is part of a block which I'd like the AI to have as choices later on when I get an idea how to do it.

As of now, I'm thinking of a 2dimensional array, that stores both the block name and priority. This way, we can make a function that sorts out these said blocks and allow the AI to execute script lines within those blocks also with priority.

So, each block is stored in a 2dimensional array as the condition calls for, and each line within that block is stored in another 2dimensional array that has set of conditions before the next one will be executed.

I don't exactly have a workable code yet, only a concept.

Also, adding AI personality() with variables like aggitation, passivity, calmvalue, aggrivatevalue, aggrivateMinRangeValue, aggrivateMaxRangeValue, etc, much like L4d's witch, would give the AI some bias as to what block to store in response to certain conditions.

So no two AI personalityies will respond the same. For example, a "Rusher" personality with Aggressiveness value = 150, and passivity value = 50, and AggressivenessPassivityDiffMin = 20, will most likely choose to rush early even if it's passivity value is increased by certain conditions.

Aggressiveness Value is the minimum value the AI will get even if CalmValue is deducted to it. Aggressiveness Value is added to, for example by Aggitate Value, if enemies are found within AggrivateMinRangeValue, and will be deducted by Calm value if there were no enemies there.

So, for example, if the Personality.CalmValue = 10, this personality will calm down at this rate, etc.

All of these values will affect what Block the AI will choose, and what block to discard if conditions are appropriate, and it's personality allows it or favors it, etc.

Sorry, if I sound to be in a rush. I hope these will give you guys some ideas to consider though, as I've not been coding since some 15 years ago now.

Will post some Zerg builds later.

edit: Oh, and AISetStock() doesn't seem to queue the unit. Train and Build will appear in the queued only but not items using AISetStock()
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: hd on March 12, 2010, 03:10:44 PM
I don't get why you have half the functions you do...
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Kernel64 on March 12, 2010, 04:22:58 PM
The functions are still for testing. What they do is modularize blocks of lines that tell the engine to do something.

I use AISetStock in the past, and have conditions when these stocks fire in order to have a precise order of training and building. This one makes the setting of conditions easier, and allows me to make blocks of sets of orders (e.g. AISetStock() or AITrain/AIBuild) accessible depending on certain conditions.

For example:

this block:
Quote
        if (RCountUnit(c_ZU_Drone, 9, 10, 0, 1, 0, 1, player) == true) {
            ZEnsureFood(18, 9, c_townOne, player);
            ZEnsureWorker(13, 10, c_townOne, player);
            ZEnsureTech(1, c_ZB_SpawningPool, 10, c_townOne, c_nearCloseDropoff, player);
        }

which trains +3 drones, one pool, and +1 overlord can be replaced with:

Quote
   if (RCountUnit(c_ZU_Drone, 9, 10, 0, 1, 0, 1, player) == true) {
            ZEnsureFood(18, 9, c_townOne, player);
            ZEnsureTech(1, c_ZB_SpawningPool, 10, c_townOne, c_nearCloseDropoff, player);
        if (RCountUnit(c_ZB_SpawningPool, 0, 1, 1, 1, 0, 0, player) == true) {
            ZEnsureWorker(13, 3, c_townOne, player);
        }
   }

Which builds a pool first before the +3drones.

Now, Imagine adding another condition. Say, && (AIPersonality(100,150) == true),
where bool AIPersonality(int AggMin, intAggMax); is a function that returns true if the AI's personality, after randomization is within that range.

This way, I can have multiple blocks of orders and have the AI do them within the parameters specified by the given functions, depending on the changes of its personality or any other condition we might think necessary later. Like, say, ((AIStateNow(player) == "PrepareDefense") == true), where a block of orders to train certain units will then fire.

That block of code can then clear the current queue and insert these items, etc.

Edit:

How AITrain and AIBuild works is add them to some queue, and the engine will buy the one that is most available.

Say, if it had a queue:
1 pool
4 Drones
2 ovie

it will morph drones first because they are cheaper, then ovies, then the pool.

I've also noticed this happen in AISetStock() where if you set pool first, then drone, drones still get morphed first. This is why having conditions are better for me, since each build order yields different results.

I'll post the BOs later for Zerg, and with corresponding effects and drawbacks.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: hd on March 12, 2010, 09:28:19 PM
...but why not just do...

if (AITechCount(player, c_ZU_Drone, c_techCountInProgressOrBetter) >= 10)
{
    AISetStock(player, 2, c_ZU_Overlord);
}

if (AITechCount(player, c_ZU_Overlord, c_techCountInProgressOrBetter) >= 2)
{
    AISetStock(player, 1, c_ZB_SpawningPool);
}

if (AITechCount(player, c_ZB_SpawningPool, c_techCountInProgressOrBetter) >= 1)
{
    AISetStock(player, 13, c_ZU_Drone);
}

if (AITechCount(player, c_ZU_Drone, c_techCountInProgressOrBetter) >= 13)
{
    ASetStock(player, x, whatever);
}


^ that ensures the same thing without convoluted functions. you can essentially stack those in the order in which you want. with custom states set up, you could easily set up an entire early game build order just like that and then another build order for something else and have it randomly pick between the 2 or even give each build a % chance of occurring. From there go into either specific mid games based on which early game was chosen or go in to a random mid game.

my point being, most of your functions already exist in a less complex way.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Astazha on March 13, 2010, 12:10:16 AM
I've been playing with the AIDefaultEconomy() function.  It isn't good for your initial build because the normal build management makes an overlord anytime you have less than 8 supply free.  This means right out of the gate the AI builds an overlord.  Then it builds another one after Drone 11, and another at 19.


Also, if you use c_stockIdle it will build your drones 1 at a time.  I presume this is so that the larva are available for other stuff since "stockIdle" implies only using the idle production capacity.


It appears also that the AI will only put 21 drones on your mineral piles even if you specify that you want more than that.  Once there is an expansion it will build more drones until it feels it has enough to saturate the available mineral piles.  I'm not sure if it goes to full saturation though, seems like that would be 24 drones for 8 piles.  I also haven't tested whether the AI builds a different number of workers if there are more or fewer mineral piles at the start location.


I haven't had time to read your code yet.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Kernel64 on March 13, 2010, 02:06:47 AM
Hd, it gets really complicated once you've tried a lot. But you're right, there should be a way to reduce the lines and have the variations isolated somewhere and all other similar ones kept.

But then again, different cases proved that certain sets of ordered lines are necessary. You're example is where it all starts. Once variations are discovered, I began to notice that blocks of the such as your example shows are called for.

For example,

after the exact order: 2nd ovie, +3drones, pool... you can go:

a) +4 drones (16/18 food)
   i.) fast expo followed by:
      1. Queen (17/18), Ovie3,... then choose:
           I. +2 lings
           II. +1 drone (16 drones total)
      2. (...more lings)
b) +2 drones, extractor1, +2 drones
   i.) Ovie3 (16/18 food), queen 1 (18/18)
      1. R.Warren1 (17/18)
            I. +2lings and drone (20/26)
            II. +drones
            III. +4 lings
      2. Ovie 4, +spam Roaches to 8
   
etc.

Now, this may seem trivial, but imagine in the mid-game? If the AI is not coached to maintain a certain block, or discard, or put it on hold for an emergency, it will go All-in. And that's a bad strat.

I'm looking to make the AI respond to apparent changes in the opponent's strat, unit compo, base, and respond properly that it can even make the enemy say, "I was rolled over because I had no robo for immortals. Those roaches burn."

And for example, the AI will discard that and go tech to Hydra instead, if it discovers, while it was morphing lair, that the Protoss had not started 2nd assimilator and instead had 3 gates and etc etc.

But, yes, I'd love it if someone can implement these concepts properly and much more efficiently. I'm taking the route that I know now. I will post them up when it's playable.

As of the moment, I'm working on a function that turns on and off processing of a block, suspending it to activate another block for execution, or discarding the current block altogether for a new one.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: hd on March 13, 2010, 03:59:31 PM
Quote from: Kernel64 on March 13, 2010, 02:06:47 AM
Hd, it gets really complicated once you've tried a lot. But you're right, there should be a way to reduce the lines and have the variations isolated somewhere and all other similar ones kept.

But then again, different cases proved that certain sets of ordered lines are necessary. You're example is where it all starts. Once variations are discovered, I began to notice that blocks of the such as your example shows are called for.

For example,

after the exact order: 2nd ovie, +3drones, pool... you can go:

a) +4 drones (16/18 food)
   i.) fast expo followed by:
      1. Queen (17/18), Ovie3,... then choose:
           I. +2 lings
           II. +1 drone (16 drones total)
      2. (...more lings)
b) +2 drones, extractor1, +2 drones
   i.) Ovie3 (16/18 food), queen 1 (18/18)
      1. R.Warren1 (17/18)
            I. +2lings and drone (20/26)
            II. +drones
            III. +4 lings
      2. Ovie 4, +spam Roaches to 8
   
etc.

Now, this may seem trivial, but imagine in the mid-game? If the AI is not coached to maintain a certain block, or discard, or put it on hold for an emergency, it will go All-in. And that's a bad strat.

I'm looking to make the AI respond to apparent changes in the opponent's strat, unit compo, base, and respond properly that it can even make the enemy say, "I was rolled over because I had no robo for immortals. Those roaches burn."

And for example, the AI will discard that and go tech to Hydra instead, if it discovers, while it was morphing lair, that the Protoss had not started 2nd assimilator and instead had 3 gates and etc etc.

But, yes, I'd love it if someone can implement these concepts properly and much more efficiently. I'm taking the route that I know now. I will post them up when it's playable.

As of the moment, I'm working on a function that turns on and off processing of a block, suspending it to activate another block for execution, or discarding the current block altogether for a new one.

but that is irrelevant that early in the game. You'd be better off having completely separate build orders because every time multiple choices appear where the build can diverge, once a choice is made the number of options decrease at the next divergence until eventually the AI is committed to a build. There's only a few isolated cases where you can figure out what the opponent is going for that early in the game.

The choices are further limited by the race of the opponent. I think if you were to put a timer in that alerts you when mid-game usually starts (6-7 minutes), you'd find that very little is actually done by then.

Thus, having a singular build that can diverge so many times will actually limit and complicate the AI. As stated previously, having each option as a separate build and then having it randomly choose which build to utilize would net much better results.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Astazha on March 13, 2010, 09:07:51 PM
I think initially HD's approach is going to provide fast improvement for the next version of the AI.  I think ultimately a truly good AI will take Kernel64's approach, and I'm very interested in thinking about that framework.  Having the AI continue with a build when it's being countered hard isn't the kind of thing player would do - a player will turtle and change tech, or try to get an economic advantage, expand to an island, something.  Anything other than throwing more of the same units to their deaths.


A really simple reactive change is the anti-air one that's in the current version.  Another would be to escort troops with detection when the AI is getting mauled by Dark Templars.  A further more complex iteration of that is that if your detection is overlords then you need to be able to protect them from anti-air.  Is it complicated?  Sure.  And I think reactive AI is going to be really bad at first, but writing true artificial intelligence is what excites me about this project, and it will get better over time.



Kernel64 please post stuff as you work on it if you are willing because that stimulates ideas.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: hd on March 14, 2010, 04:24:13 AM
Quote from: Astazha on March 13, 2010, 09:07:51 PM
I think initially HD's approach is going to provide fast improvement for the next version of the AI.  I think ultimately a truly good AI will take Kernel64's approach, and I'm very interested in thinking about that framework.  Having the AI continue with a build when it's being countered hard isn't the kind of thing player would do - a player will turtle and change tech, or try to get an economic advantage, expand to an island, something.  Anything other than throwing more of the same units to their deaths.


A really simple reactive change is the anti-air one that's in the current version.  Another would be to escort troops with detection when the AI is getting mauled by Dark Templars.  A further more complex iteration of that is that if your detection is overlords then you need to be able to protect them from anti-air.  Is it complicated?  Sure.  And I think reactive AI is going to be really bad at first, but writing true artificial intelligence is what excites me about this project, and it will get better over time.



Kernel64 please post stuff as you work on it if you are willing because that stimulates ideas.
I disagree. I don't think his method will have much effect at all on the game. We're talking about early game here. In early game there's not much you can react to. There just isn't enough information that can be gathered by scouting to figure out their plan... yeah, they may go hellion rush, zealot rush or whatever but by the time you get to a point where you can react, it's already mid game.

Having a build order with that many checks and rechecks to try to counter an opponent is silly. I think you guys are confusing build orders with strategies. A build order is only important early in the game to set yourself up. Once mid game kicks in, you're countering units, executing strategies or maneuvers. Early game has nothing to do with the sorts.

No player starts a build to say... rush 'lings and then goes "oh, the opponent might rush banshee's, lets scrap this entire thing and rush hydralisks."

For all they know, they're not rushing banshee's, they're rushing siege, or hellions or reapers. By the time you're even in a position to counter, it's already mid game. And if you are rushing, you HAVE to commit to it. You can't go, "I'm going to sacrifice x, y and z so I can get early 'lings effectively screwing myself for another build and then cancel the entire idea just because the terran walled himself in." A build order is just that, the order in which you build. It is not in and of itself a strategy.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Astazha on March 14, 2010, 04:55:32 AM
Yeah, I didn't mean so much during early game.  I wouldn't say that you can't react at all initially, but most of that does take place at mid-game or later.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Kernel64 on March 14, 2010, 08:07:13 AM
Quote from: Astazha on March 13, 2010, 09:07:51 PM
I think initially HD's approach is going to provide fast improvement for the next version of the AI.  I think ultimately a truly good AI will take Kernel64's approach, and I'm very interested in thinking about that framework.  Having the AI continue with a build when it's being countered hard isn't the kind of thing player would do - a player will turtle and change tech, or try to get an economic advantage, expand to an island, something.  Anything other than throwing more of the same units to their deaths.


A really simple reactive change is the anti-air one that's in the current version.  Another would be to escort troops with detection when the AI is getting mauled by Dark Templars.  A further more complex iteration of that is that if your detection is overlords then you need to be able to protect them from anti-air.  Is it complicated?  Sure.  And I think reactive AI is going to be really bad at first, but writing true artificial intelligence is what excites me about this project, and it will get better over time.



Kernel64 please post stuff as you work on it if you are willing because that stimulates ideas.

I will be posting them. Maybe tomorrow. I'm trying to make the zerg shift to expand first instead of preparing for banelings, which can be done after the Queen and third ovie are in queue, if the conditions say so.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Kernel64 on March 14, 2010, 03:56:30 PM
Here's some. It's a template of the sorts on how things work:

This is what goes in the Zerg0.galaxy
Quote
    ZEnsureWorker(10, 10, c_townOne, player);
    if ((RCountUnit(c_ZU_Drone, 8, 9, 0, 1, 0, 1, player) == true) && (CBlockStatus != "InProgress")){
        //randomize for next block here
        NextBlock = RandomInt(1,100);
        if (NextBlock <=50) {
            CBlock = "Block1";
            CBlockStatus = "NotStarted";
            EmergencyState = "Inactive";
        }
        else {
            CBlock = "Block2";
            CBlockStatus = "NotStarted";
            EmergencyState = "Inactive";
        }
    }

    if(CBlockStatus == "NotStarted") { //we've not started the Block
        //check if there are emergencies
        //check if we need to change block, if not:
        CBlockStatus = "InProgress";
   
    }//end not started
   
    if(CBlockStatus == "InProgress") { //we're currently in progress
   
        //we don't have an emergency,
        if ( EmergencyState == "Inactive") {

            //and CBlock is not Done (it could be in progress)
            if (CBlockStatus != "Done") {
                //...enter the specified block and call it
                if (CBlock == "Block1") {
                    CBlockStatus = BlockOne(player);
                }
   
                if (CBlock == "Block2") {
                    CBlockStatus =     BlockTwo(player);
                }
            }
        //Q: Do we need to check if there's an emergency here?
        //Q: Do we need to check if we need to change block here?
        }
       
        //we have an emergency
        if ( EmergencyState == "Active") {
            //do the same as above here
        }
       
       
    }// end InProgress

these are the blocks:
Quote
string BlockOne(int player) {
if (RCountUnit(c_ZU_Overlord, 0, 1, 0, 0, 0, 0, player) == true) {    //we have 1 ovie, 0 inprogres, 0 queued
    ZEnsureFood(18, 9, c_townOne, player);                            //queue 1 ovie, or compensate if ovie is dead
    return "InProgress";
    }
if (RCountUnit(c_ZU_Overlord, 1, 2, 1, 1, 0, 0, player) == true) {  //we have 1 done, 1 in progress, 0 in queue
    ZEnsureUnit(13, 3, c_ZU_Drone, 10, c_townOne, player);                        //this will not be true when the ovie is done already
    return "InProgress";
    }
if (RCountUnit(c_ZU_Drone, 10, 13, 2, 3, 0, 0, player) == true) {  //we have drones: 10-13 done, 3 in progres, 0 in queue
    ZEnsureTech(1, 1, c_ZB_SpawningPool, 10, c_townOne, c_nearCloseDropoff, player);  //all these looks like while now: we queue, while...
    return "InProgress";
    }
if (RCountUnit(c_ZB_SpawningPool, 0, 1, 1, 1, 0, 0, player) == true) {
    ZEnsureUnit(13, 1, c_ZU_Drone, 10, c_townOne, player);
    return "InProgress";
    }
if (RCountUnit(c_ZU_Overlord, 1, 2, 0, 0, 0, 1, player) == true) {
    return "InProgress";
}
if ((TotalUnit(c_ZB_SpawningPool, player) == 1)&&
    (TotalUnit(c_ZU_Drone, player) == 13) &&
    (TotalUnit(c_ZU_Overlord, player) == 2)  &&
    (TotalUnit(c_ZB_Hatchery, player) == 1)    ){
        return "Done";
    }
else {
    return "NotDone";
    }
}

string BlockTwo(int player) { //pool, ovie, +3drones
if (RCountUnit(c_ZB_SpawningPool, 0, 0, 0, 0, 0, 0, player) == true) {
    ZEnsureTech(1, 1, c_ZB_SpawningPool, 10, c_townOne, c_nearCloseDropoff, player);  //all these looks like while now: we queue, while...
    ZEnsureUnit(10, 1, c_ZU_Drone, 10, c_townOne, player);   
    return "InProgress";
    }
if ((RCountUnit(c_ZB_SpawningPool, 0, 1, 0, 0, 1, 1, player) == true) &&
    (RCountUnit(c_ZU_Overlord, 1, 2, 0, 0, 0, 1, player) == true)) {
    return "InProgress";
    }
if ((RCountUnit(c_ZB_SpawningPool, 0, 0, 1, 1, 0, 0, player) == true) &&
    (RCountUnit(c_ZU_Overlord, 1, 1, 0, 0, 0, 1, player) == true)){
    ZEnsureFood(18, 9, c_townOne, player);   
    return "InProgress";
    }
   
//for block 2, this +3drone is not automatic. Threat must be checked first.
if (RCountUnit(c_ZU_Overlord, 1, 2, 1, 1, 0, 0, player) == true) {  //we have 1 done, 1 in progress, 0 in queue
    ZEnsureUnit(13, 3, c_ZU_Drone, 10, c_townOne, player);            //this will not be true when the ovie is done already
        return "InProgress";
    }
if ((TotalUnit(c_ZB_SpawningPool, player) == 1)&&
    (TotalUnit(c_ZU_Drone, player) == 13) &&
    (TotalUnit(c_ZU_Overlord, player) == 2)  &&
    (TotalUnit(c_ZB_Hatchery, player) == 1)    ){
        return "Done";
    }
else {
    return "NotDone";
    }
}

As you can notice, queuing of the items are broken into conditions. The engine will parse lines one at a time, so these lines get noticed every pass.

As things become more diverse, changes to the return strings in those blocks might be necessary, or not.

The first code shows how an emergency can put things on hold, or a certain change in a string make another block execute.

In this example, randomization is used. I've not gotten to doing the personality just yet. The personality attributes will also be set by randomization.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Aeg1s on March 14, 2010, 04:19:57 PM
I've glanced over this thread and wanted to comment on how I'm currently programming my build logic. One of the key things I've noticed is you need to make sure you only use AISetStock when something needs to get built otherwise it will just hold up anything below it. Thus for buildings I use a couple custom functions that basically check certain conditions and if the building is already under construction (this is key because otherwise AISetStock(player, 1, c_TB_Barracks) will block anything untill the barracks is finished.


Then in the varying states my basic logic is as such:
-Buildings and tech in order with conditions (only one of these should ever be calling AISetStock at any one time)
-MakeArmy
-MakeWorkers
-BuildSupply


The last three I might change the order on but they are using resources whenever anything above it is waiting for construction etc...


MakeArmy basically checks what production queues I have available and will call SetStock with a number that will only enqueue one unit per available building (2 for terran reactors).
MakeWorkers is the same for Command Centers etc..
Finally BuildSupply will add supply buildings when needed (this isn't used in the very beginning of the game due to strict timing being necessary).


I've also added a GasBlocked flag that will check when building units if they can't be built due to a lack of gas and thus won't call AISetStock and can allow any mineral only units/buildings after that function call to be built.


Here's the current version of my framework functions if anyone wants to check them out: http://pastebin.com/xa88Q2AU (http://pastebin.com/xa88Q2AU)
I posted some of them in http://darkblizz.org/Forum2/ai-development/code-snippets-functions/ (http://darkblizz.org/Forum2/ai-development/code-snippets-functions/) but I've since changed/added some.


All of this basically adds up to help streamline builds by reducing the time the AI is waiting for anything to be built; instead of using AISetStock as a long queue of what to build in order I use it as a list of what I want built now.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Kernel64 on March 14, 2010, 07:19:08 PM
Also, here's the Zerg0.galaxy

just rename the attachment from .doc to .galaxy

What it does is first choose at random which block to run. And when that block is done, it goes to SubA. No other options are available, so both goes to SubA.

If you want to try it, you might want to keep the AI at Opening longer.

What I find a bit tedious is the condition checker. At this point it needs to be precise, just as HD had foretold. Things are so small that the level of precision may not be worth the small difference.

However, this might prove useful in the later stages of the game. And since the RCountUnit can be given ranges of values, at those times, ranges may become applicable, thus, much more comfortable to define.

edit: Aeg1s, what I've experienced from AISetStock() is that it always buys the cheapest thing. Say I SetStock: 1 barracks, then 3 scvs, it will always buy those scvs then the barracks.

So if I have 1 barracks, 3 scvs, 2 barracks, 6 scvs, none of the barracks will be built until the 6scvs are either in production or finished.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Kernel64 on March 15, 2010, 10:17:35 AM
New functions. These functions allow you to add to a list of what to   train/build. Research has not been added yet. Can accept Expansion orders.

  Declarations:
Quote
//for main blocks
string[20] UnitName;      //the unit to make                                    Expo -- for   expand
string[20] UnitType;    //build or struct?                                      hatchery
int[20] MakeTotal;        //how many use:   TotalUnit + desired addition        number of expos
int[20]   MakeWhere;        //c_townOne, etc.                                      where to start searching for expo
int[20] BuildFlag;        //build   flags for buildings                            the expanddefault
int[20]   DoNextCount;    //the count to watch for us to do next in line          same
int[20] DoNextCond;        //the condition to watch regarding   the count above    same
//for expo
int[20] MinInBank;
int[20]   GasInBank;


//for emergency blocks
string[20] EUnitName;      //the unit to make
string[20] EUnitType;    //build or struct?
int[20]   EMakeTotal;        //how many use: TotalUnit + desired addition
int[20]   EMakeWhere;        //c_townOne, etc.
int[20] EBuildFlag;          //build flags for buildings
int[20] EDoNextCount;    //the count to   watch for us to do next in line
int[20] EDoNextCond;        //the   condition to watch regarding the count above

string   ProcessOrder(int player); //returns InProgress or ListEmpty FOR MAIN   BLOCKS
string EProcessOrder(int player); //returns InProgress or   ListEmpty FOR EMERGECY BLOCKS

void InjectOrder(int player);   //just a prototype

//for main list
string InjectItem(string   UName, string UType, int Total, int TownWhere, int BFlag, int NextCount,   int Cond, int player);
//forExpo
string InjectItemExpo(string   UName, string UType, int Total, int TownWhere, int BankMin, int BankGas,   int player);
//for emergency list
string EInjectItem(string   UName, string UType, int Total, int TownWhere, int BFlag, int NextCount,   int Cond, int player);

Uses:
Quote
//--------------------------------------------------------------------------------------------------
//    Counts the specified unit all including in progress and queued
//--------------------------------------------------------------------------------------------------
int   TotalUnit(string UCheck, int Tplayer){
    int a = 0;
    a =   (AITechCount(Tplayer, UCheck, c_techCountInProgressOnly) +
                  AITechCount(Tplayer, UCheck, c_techCountQueuedOnly) +
                  AITechCount(Tplayer, UCheck, c_techCountCompleteOnly));
      return a;
}

The functions:
Quote
//---------------------------------
//   Main List Processor
string   ProcessOrder(int player){
    int i;
    int j;
    int   CurrNumOrders;
    int WantMore;
   
    point MainTownLoc1; 
      int NearTown1; 
    point NewTownLoc1;
    int ExpoCount;   

      i = 0;
    j = 0;
    CurrNumOrders = 0;
    WantMore = 0;
      // check if there's something to process
    while (   (UnitName[CurrNumOrders] != "") && (CurrNumOrders < 20) ) {
          CurrNumOrders = CurrNumOrders + 1;
    }
   
    if   (CurrNumOrders != 0) { //we have something
   
        if   (UnitType[0] != "Expo") { //this is not an expansion directive
              //check if TotalUnit of the said unit is less than what we   specified
            if ( TotalUnit(UnitName[0], player) <   MakeTotal[0] ) {
                //we have less and we want more...
                  //get how much more we want:
                WantMore =    MakeTotal[0] - TotalUnit(UnitName[0], player);
                  //check if it's a building or not...
                if ( UnitType[0]   == "Unit") { //we have a unit, use train
                    AITrain   (player, 10, MakeWhere[0], UnitName[0], WantMore);
                }
                  else if (UnitType[0] == "Struct") { //we have a   structure, use build
                    AIBuild (player, 10,   MakeWhere[0], UnitName[0], WantMore, BuildFlag[0]);
                }
                  else {
                    //do for tech here
                  }
            }
            else if (   TotalUnit(UnitName[0], player) >= MakeTotal[0] ) {
                  //we have all we wanted, they're queued or in progress, or better.
                  //now we check if we can discard this order
                  if ( AITechCount(player, UnitName[0], DoNextCond[0]) ==   DoNextCount[0] ){
                    //our limit has arrived, and in   the condition we specified
                    //it's time to take   this out of the list, and move the rest up...
                      //get the maximum number of orders. you'll know it because it's not ""
                      CurrNumOrders = 0;
                    while (   (UnitName[CurrNumOrders] != "") && (CurrNumOrders < 20) ) {
                          CurrNumOrders = CurrNumOrders + 1;
                      }
                    if ((CurrNumOrders != 20) &&   (CurrNumOrders > 0)) { //we dont have full list but not empty
                          while( j < CurrNumOrders ) {

                              if (UnitType[j+1] == "Expo") { //if the next item is an   expo
                                GasInBank[j] = GasInBank[j+1];
                                  MinInBank[j] = MinInBank[j+1];
                                  UnitName[j] = UnitName[j + 1];
                                  UnitType[j] = UnitType[j + 1];
                                  MakeTotal[j] = MakeTotal[j + 1];         
                                  MakeWhere[j] = MakeWhere[j + 1];         
                              }
                            else {
                                  UnitName[j] = UnitName[j + 1];
                                  UnitType[j] = UnitType[j + 1];
                                  MakeTotal[j] = MakeTotal[j + 1];         
                                  MakeWhere[j] = MakeWhere[j + 1];         
                                  BuildFlag[j] = BuildFlag[j + 1];     
                                  DoNextCount[j] = DoNextCount[j + 1];     
                                  DoNextCond[j] = DoNextCond[j + 1];
                              }
                            j = j + 1;
                     
                        }
                    }
                      else {//we have full list
                        while(   j < CurrNumOrders - 1) {
                            if   (UnitType[j] == "Expo") { //if the next item is still an expo
                                  GasInBank[j] = GasInBank[j+1];
                                  MinInBank[j] = MinInBank[j+1];
                                  UnitName[j] = UnitName[j + 1];
                                  UnitType[j] = UnitType[j + 1];
                                  MakeTotal[j] = MakeTotal[j + 1];         
                                  MakeWhere[j] = MakeWhere[j + 1];         
                              }
                            else {
                                  UnitName[j] = UnitName[j + 1];
                                  UnitType[j] = UnitType[j + 1];
                                  MakeTotal[j] = MakeTotal[j + 1];         
                                  MakeWhere[j] = MakeWhere[j + 1];         
                                  BuildFlag[j] = BuildFlag[j + 1];     
                                  DoNextCount[j] = DoNextCount[j + 1];     
                                  DoNextCond[j] = DoNextCond[j + 1];
                              }
                            j = j + 1;
                          }
                        UnitName[j] = "";
                      }
                }
                else {
                }
              }
            else {
            }
        }
          else { //this is an expo directive
            MainTownLoc1 =   AIGetTownLocation (player, MakeWhere[0]);
            NearTown1 =   AIGetClosestTown (player, MainTownLoc1);
            NewTownLoc1 =   AIGetTownLocation (player, NearTown1);
            if (   AIHasRes(player, MinInBank[0], GasInBank[0]) ) { //we have res
                  ExpoCount = AIExpand (player, NewTownLoc1, UnitName[0]);
                  if (ExpoCount >= MakeTotal[0]) { // the number of expo   is set and is equal to what we want to make
                    //    proceed to the next in list, take this out
                      CurrNumOrders = 0;
                    while (   (UnitName[CurrNumOrders] != "") && (CurrNumOrders < 20) ) {
                          CurrNumOrders = CurrNumOrders + 1;
                      }
                    if ((CurrNumOrders != 20) &&   (CurrNumOrders > 0)) { //we dont have full list but not empty
                          while( j < CurrNumOrders ) {

                              if (UnitType[j+1] == "Expo") { //if the next item is an   expo
                                GasInBank[j] = GasInBank[j+1];
                                  MinInBank[j] = MinInBank[j+1];
                                  UnitName[j] = UnitName[j + 1];
                                  UnitType[j] = UnitType[j + 1];
                                  MakeTotal[j] = MakeTotal[j + 1];         
                                  MakeWhere[j] = MakeWhere[j + 1];         
                              }
                            else {
                                  UnitName[j] = UnitName[j + 1];
                                  UnitType[j] = UnitType[j + 1];
                                  MakeTotal[j] = MakeTotal[j + 1];         
                                  MakeWhere[j] = MakeWhere[j + 1];         
                                  BuildFlag[j] = BuildFlag[j + 1];     
                                  DoNextCount[j] = DoNextCount[j + 1];     
                                  DoNextCond[j] = DoNextCond[j + 1];
                              }
                            j = j + 1;
                     
                        }
                    }
                      else {//we have full list
                        while(   j < CurrNumOrders - 1) {
                            if   (UnitType[j] == "Expo") { //if the next item is still an expo
                                  GasInBank[j] = GasInBank[j+1];
                                  MinInBank[j] = MinInBank[j+1];
                                  UnitName[j] = UnitName[j + 1];
                                  UnitType[j] = UnitType[j + 1];
                                  MakeTotal[j] = MakeTotal[j + 1];         
                                  MakeWhere[j] = MakeWhere[j + 1];         
                              }
                            else {
                                  UnitName[j] = UnitName[j + 1];
                                  UnitType[j] = UnitType[j + 1];
                                  MakeTotal[j] = MakeTotal[j + 1];         
                                  MakeWhere[j] = MakeWhere[j + 1];         
                                  BuildFlag[j] = BuildFlag[j + 1];     
                                  DoNextCount[j] = DoNextCount[j + 1];     
                                  DoNextCond[j] = DoNextCond[j + 1];
                              }
                            j = j + 1;
                          }
                        UnitName[j] = "";
                      }                   
                }
                else{//   number of expos not in yet.
                }
            }
              else { // we don't have res
            }
       
          }
       
    return "InProgress";
    }
    else {
     
    return "ListEmpty";
    }
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//Emergency   List Processor

string   EProcessOrder(int player){
    int i;
    int j;
    int   CurrNumOrders;
    int WantMore;

    i = 0;
    j = 0;
      CurrNumOrders = 0;
    WantMore = 0;
    // check if there's   something to process
    while ( (EUnitName[CurrNumOrders] != "")   && (CurrNumOrders < 20) ) {
        CurrNumOrders =   CurrNumOrders + 1;
    }
   
    if (CurrNumOrders != 0) {   //we have something
        //check if TotalUnit of the said unit is   less than what we specified
        if ( TotalUnit(EUnitName[0],   player) < EMakeTotal[0] ) {
            //we have less and we want   more...
            //get how much more we want:
              WantMore =  EMakeTotal[0] - TotalUnit(EUnitName[0], player);
              //check if it's a building or not...
            if ( EUnitType[0]   == "Unit") { //we have a unit, use train
                AITrain   (player, 10, EMakeWhere[0], EUnitName[0], WantMore);
            }
              else if (EUnitType[0] == "Struct") { //we have a structure,   use build
                AIBuild (player, 10, EMakeWhere[0],   EUnitName[0], WantMore, EBuildFlag[0]);
            }
              else {
                //do for tech here
            }
          }
        else if ( TotalUnit(EUnitName[0], player) >=   EMakeTotal[0] ) {
            //we have all we wanted, they're queued   or in progress, or better.
            //now we check if we can   discard this order
            if ( AITechCount(player, EUnitName[0],   EDoNextCond[0]) == EDoNextCount[0] ){
                //our limit   has arrived, and in the condition we specified
                //it's   time to take this out of the list, and move the rest up...
                  //get the maximum number of orders. you'll know it because it's   not ""
                CurrNumOrders = 0;
                while (   (EUnitName[CurrNumOrders] != "") && (CurrNumOrders < 20) ) {
                      CurrNumOrders = CurrNumOrders + 1;
                  }
                if ((CurrNumOrders != 20) &&   (CurrNumOrders > 0)) { //we dont have full list but not empty
                      while( j < CurrNumOrders ) {
                          EUnitName[j] = EUnitName[j + 1];
                          EUnitType[j] = EUnitType[j + 1];
                          EMakeTotal[j] = EMakeTotal[j + 1];         
                          EMakeWhere[j] = EMakeWhere[j + 1];         
                          EBuildFlag[j] = EBuildFlag[j + 1];     
                          EDoNextCount[j] = EDoNextCount[j + 1];     
                          EDoNextCond[j] = EDoNextCond[j + 1];
                        j = j +   1;
                    }
                }
                else   {//we have full list
                    while( j < CurrNumOrders   - 1) {
                        EUnitName[j] = EUnitName[j + 1];
                          EUnitType[j] = EUnitType[j + 1];
                          EMakeTotal[j] = EMakeTotal[j + 1];         
                          EMakeWhere[j] = EMakeWhere[j + 1];         
                          EBuildFlag[j] = EBuildFlag[j + 1];     
                          EDoNextCount[j] = EDoNextCount[j + 1];     
                          EDoNextCond[j] = EDoNextCond[j + 1];
                        j = j +   1;
                    }
                    EUnitName[j] = "";
                  }
            }
            else {
            }
          }
        else {
        }
       
    return   "InProgress";
    }
    else {
   
    return "ListEmpty";
      }
}


Inject Functions. Adds items to   list when called.

Quote
//   Main List Injector
//-------------------------------------------------------
string   InjectItem(string UName, string UType, int Total, int TownWhere, int   BFlag, int NextCount, int Cond, int player){
int CurrNumOrders=0;
int   i=0;

    // check if the list is full
    while (   (UnitName[CurrNumOrders] != "") && (CurrNumOrders < 20) ) {
          CurrNumOrders = CurrNumOrders + 1;
    }
   
    if   (CurrNumOrders == 20) {
   
    return "ListFull";
    }
      else {
        //add item at the last spot
        i =   CurrNumOrders;
        UnitName = UName;   
        UnitType =   UType;   
        MakeTotal = Total;       
        MakeWhere =   TownWhere;       
        BuildFlag = BFlag;
        DoNextCount =   NextCount;   
        DoNextCond = Cond;
       
    return   "Success";
    }
}

string InjectItemExpo(string UName,   string UType, int Total, int TownWhere, int BankMin, int BankGas, int   player){
int CurrNumOrders=0;
int i=0;

    // check if the   list is full
    while ( (UnitName[CurrNumOrders] != "") &&   (CurrNumOrders < 20) ) {
        CurrNumOrders = CurrNumOrders +   1;
    }
   
    if (CurrNumOrders == 20) {
   
      return "ListFull";
    }
    else {
        //add item at the   last spot
        i = CurrNumOrders;
        UnitName = UName;   
          UnitType = UType;   
        MakeTotal = Total;       
          MakeWhere = TownWhere;       
        MinInBank = BankMin;
          GasInBank = BankGas;
       
    return "Success";
    }
}


//   Emergency List Injector
//-------------------------------------------------------
string   EInjectItem(string UName, string UType, int Total, int TownWhere, int   BFlag, int NextCount, int Cond, int player){
int CurrNumOrders=0;
int   i=0;

    // check if the list is full
    while (   (EUnitName[CurrNumOrders] != "") && (CurrNumOrders < 20) ) {
          CurrNumOrders = CurrNumOrders + 1;
    }
   
    if   (CurrNumOrders == 20) {
   
    return "ListFull";
    }
      else {
        //add item at the last spot
        i =   CurrNumOrders;
        EUnitName = UName;   
        EUnitType =   UType;   
        EMakeTotal = Total;       
        EMakeWhere =   TownWhere;       
        EBuildFlag = BFlag;
          EDoNextCount = NextCount;   
        EDoNextCond = Cond;
       
      return "Success";
    }
}


Example   usage. BlockOneSubA -- Injects drones ovie and expo then drones.
Quote
string   BlockOneSubA(int player) {
int b;
b= (TotalUnit(c_ZU_Drone,   player) + 4);
if ( InjectItem(c_ZU_Drone,
                "Unit",   
                b,
                c_townOne,
                  c_nearCloseDropoff,
                b,
                  c_techCountInProgressOrBetter,
                player) != "Success")   { return "ListFull"; }

if ( InjectItem(c_ZU_Overlord,
                  "Unit",
                2,     
                  c_townOne,
                c_nearCloseDropoff,
                  2,
                c_techCountInProgressOrBetter,
                  player) != "Success") { return "ListFull"; }
               
if (   InjectItem(c_ZU_Drone,
                "Unit",
                  13,
                c_townOne,
                  c_nearCloseDropoff,
                13,
                  c_techCountInProgressOrBetter,
                player) != "Success")   {return "ListFull"; }               

if (   InjectItemExpo(c_ZB_Hatchery,
                "Expo",
                  1,
                0,
                250,
                  0,
                player) != "Success") {return "ListFull"; }
                 
if ( InjectItem(c_ZU_Drone,
                "Unit",   
                18,
                c_townOne,
                  c_nearCloseDropoff,
                18,
                  c_techCountInProgressOrBetter,
                player) != "Success")   {return "ListFull"; }           

               
else {
      return "Success";
}

}


Running   it under Zerg0.galaxy
Quote
    CBlockStatus =   ProcessOrder(player);
    if((AIGetTime() < 2) &&   (CBlockStatus != "InProgress")){
        CBlockStatus =   BlockOneSubA(player);
    }

    if( CBlockStatus ==   "ListEmpty"){

    }
   
    else {
    CBlockStatus =   ProcessOrder(player);
    UIDisplayMessage(PlayerGroupAll(), 1,   StringToText(CBlockStatus));
    }


CBlockStatus   is a string. It is used for checking the list status, and later moving   through lists, etc.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Astazha on March 15, 2010, 11:41:07 PM
Aeg1s I would be interested in seeing your code.


As I consider what kind of a decision making framework I would want I realize that the different races might actually benefit from different structures.


The kinds of things that inflence decision making won't change much - each AI needs to be able to scout out that island expansion, recognize the economic threat it represents, and respond with air units or some form of drop (or whatever)


However, each of the races is designed to have different trade offs.


Example 1:
Your base has been invaded and was nearly destroyed.  Your peon line is nearly gone.  Your military is nearly gone.  You destroyed the enemy forces but the next wave will be coming and you are at an income disadvantage but have a significant stockpile of minerals to spend to get back up to speed.


If you are Protoss or Terran then there is no reason why you couldn't build peons and a new military in parallel.  If you are Zerg, however, you have a judgment call to make.  Larva must be allocated, and your military and economic needs are now in direct tension with each other.


Example 2:
Things are going reasonably well and you have a surplus of minerals that has developed into a tidy little stockpile.  Your tech level is good for the stage the game is at and your military has the enemy reasonably well contained.  You largely have control of the map and are looking to press your advantage.


A Protoss or Terran player must consider whether to build additional "factories" to convert their surplus income into units, or whether it would be better to expand.  For a Zerg player, these are largely synonymous.


I personally play Zerg most of the time, so I'm more aware of that race's quirks, but undoubtedly each race has things that must be taken into account that the others do not deal with.


I also think, in the long run, that the good AI's will be for a specific match up - ZvT, ZvP, etc.  But hey, we can let it evolve to that.


Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Kernel64 on March 16, 2010, 06:01:15 AM
Those are brilliant thoughts, man. As for the decision making, I'm largely inclined to using personality. Personality in the sense that certain variables makes the AI be inclined to say make a bigger army, should it's stockpile be at the level you mentioned.

There are only these objectives the AI must choose from:
Military
Economy
Tech
Production

Military being the level of its military strength in comparison to its target.
Economy, number of workers, number of expansions.
Tech, level of technological capabilities, research, unit type it can produce.
Production, the potential it can wield a certain type of unit. Or in the case of the Zerg, larva, which includes queen, hatcheries.

I don't know if we can control the AI to position its units at a certain location on the map, and have it value that area higher. If so, map dominance/control could be one of what the AI would strive for.

As of now, what I'm thinking is how the AI would choose to pump military instead of econ, say, Triple-Rax+reactor marine pump from 5-7mins, then make a push.

So it's like, once the AI has chosen Military, and in that sense not in defense or response, but rather pump a certain military unit, it will automatically have values in its Aggression increased, but with variation in the amount that aggression calms down.

It will calm down, and retreat if it is losing, fast or sees the enemy has units its own is weak against. How to determine that, is just using a function that makes the AI evaluate what it sees and reduce everything to numbers and put itself in comparison by point value. Haven't coded this yet, but it can prove useful.

Anyway, that's how I would do it.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Astazha on March 16, 2010, 06:30:24 AM
I like all of that.  I might add scouting - if we're trying to make a truly non-cheating AI, and I am, then information is it's own priority.


Without getting into map specific logic, I don't think we can do map control in the "control the center" sense apart from just guessing at what might be a good location to place troops.  You could go for center of map, or center of your towns and toward the enemy a bit, or a more aggressive posture center of their towns and towards you a bit.  On some maps those guesses might be disastrous though.  I think this might be worth exploring, just from the standpoint of reducing unit travel time. 


The first thing I think the AI can do better as far as map control is to expand, expand, expand.  Particularly a Zerg AI.  Defenses could range from:


1) Undefended.  The AI is placing a Hatchery just to know if the player is there or not and prevent player expansion, and to provoke a defense response if they show up to try to expand.
2) Light to Moderate defenses. - Some Spines, maybe Spores depending.
3) Mobile defenses.  Multiple expansions are best covered by units that are mobile - either centrally placed, Air units, or perhaps a defensive wave chillin' in a Nydus network with exits already built (back by the mineral line) and protected by defense structures.
4) Heavy defenses.  Expansions that have become the main source of income might warrant the presence of a wave in addition to some of the above.


On a separate note, Wave management will be important to stop the player from abusing the AI's defensive instincts.  A mildly threatening harass should not cause his main attack wave to abort the invasion.


Have you had any luck with information gathering functions?  I'm working on this right now to try to create the mathematical reductions you described.  AIWaveEvalRatio provides a ratio of (friendly life)/(enemy life) which is some indication of how things are going but is very rough.  A better indication of how the battle is going is to watch how that number changes, but even then it's a far cry from knowing whether the enemy has zealots or stalkers, and how many of each.


Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Astazha on March 16, 2010, 07:54:02 AM
I have an idea that has survived initial testing.  I'm using the counterunit functions.  They don't cheat, and they have successfully told me how many zealots a player has when given the assumption that the player is protoss.  I think I can wrangle this to figure out everything we need once the scout has seen it.  We'll see... I haven't tried to make it work with buildings yet, which will be important.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Astazha on March 16, 2010, 08:00:45 AM
It does work on buildings... this is exciting.


::crawls into cave with computer::
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Kernel64 on March 16, 2010, 08:59:55 AM
Ooze-some!

I've updated the ProcessOrder thing in the previous post. It includes expansion. Meaning, you can inject an expansion into the list, and have the AI run it without interfering with other items in the list.

The issue with expansions previously is the conditions waited that a hatch be in progress for the next line to fire. This time, using AIExpand, as it returns the number of queued expansion orders, everything is fine.

edit: Post updated. Anyone can use it or modify it to make it better. The idea is the AI will have multiple lists and switch between them per direction.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Astazha on March 16, 2010, 09:35:12 AM
Ok, this is actually really straightforward.  I thought I was going to have to jump through some hoops, but it turns out to be extremely simple.


The two functions we are interested in are:
AICounterUnit()
AICounterUnits()


In Zerg.galaxy InitCounters() uses AICounterUnit() to set up relationships between enemy units and what they should be countered with.  For example:


AICounterUnit(player, c_PU_Zealot, 1.00, c_PU_Baneling);


This means that for every Zealot out there (that we've seen), we should build 1.00 Banelings.  It appears to be good at knowing the difference between a new unit and one that it has already seen, and it doesn't cheat.  We often don't get a real probe count on the initial scout, for example, because the default scout pathing doesn't get close enough to the mineral pile to actually see them all.


If you call AICounterUnits() like this:


howManyBanelingsShouldIBuild=AICounterUnits(player, 4, c_ZU_Baneling);


it will tell you how many Banelings should be built against player 3 (index 4).  As long as Zealots are the only thing Banelings counter, and the ratio is 1.00 then this is also the number of Zealots player 3 has.


It gets simpler.  We can't use any user-defined strings in these functions, but there is nothing preventing us from using the strings from other races.  Soooo...


We delete all the existing entries.  Then we make an entry for each unit and building, listing it as the counter to itself with a ratio of 1.00


Then when you call


countPBGateways=AICounterUnits(player, 4, c_PB_Gateway);


it will tell you how many Gateways player 3 has.  Wash Rinse Repeat for every unit in the game.  Once you get a read of what race you're dealing with you no longer have to check 2/3 of the units and we can begin to make match-specific decisions about the build order and general strategy.


I'll post actual code when I have something working, but if you want to mess with it now that's how it works.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Aeg1s on March 16, 2010, 09:40:57 AM
Quote from: Astazha on March 16, 2010, 09:35:12 AM
Ok, this is actually really straightforward.  I thought I was going to have to jump through some hoops, but it turns out to be extremely simple.


The two functions we are interested in are:
AICounterUnit()
AICounterUnits()

...

We delete all the existing entries.  Then we make an entry for each unit and building, listing it as the counter to itself with a ratio of 1.00


Then when you call


countPBGateways=AICounterUnits(player, 4, c_PB_Gateway);


it will tell you how many Gateways player 3 has.  Wash Rinse Repeat for every unit in the game.  Once you get a read of what race you're dealing with you no longer have to check 2/3 of the units and we can begin to make match-specific decisions about the build order and general strategy.


I'll post actual code when I have something working, but if you want to mess with it now that's how it works.


AIKnownUnitCount(player, 4, c_PB_Gateway); will return the number of units player knows that player 4 has; if you haven't enabled the cheating vision it will only return what it can/has seen.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Kernel64 on March 16, 2010, 10:27:33 AM
Cool.

Does calling AICounterUnit() actually make the player make the MakeWhat?
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Astazha on March 16, 2010, 10:38:05 AM
Quote from: Aeg1s on March 16, 2010, 09:40:57 AM


AIKnownUnitCount(player, 4, c_PB_Gateway); will return the number of units player knows that player 4 has; if you haven't enabled the cheating vision it will only return what it can/has seen.


It's possible that I used it wrong... I tried that and it didn't work.  Now that I'm talking about this I think I was running the player index from 0 to 3 back then instead of 2-5.  That would be way easier...


@Kernel64 no it doesn't make anything.  AICounterUnit just sets up relationships, and AICounterUnits reports numbers.


It may be useless if AIKnownUnitCount() works as Aeg1s just described, but here's the code for the new InitCounter()


Edit:  Aeg1s is right, AIKnownUnitCount() works fine if you have the right index.  I'll leave the code below in case someone wants to copy paste it and set up counters or something.  I'm a little skeptical about using this as it just returns one unit and sometimes the answer is combined arms.  Better than nothing for reactive code I suppose, but I'd rather read what they have and go from there than just feed a simple 1:1 counter into the production queue.


Note: The index for a player is the player# +1.  Player 2 is index 3, etc.  This Galaxy stuff seems to like 1 base indexes.  I'm not sure why player 1 isn't index 1, but whatever.


Quote//--------------------------------------------------------------------------------------------------
//  InitCounters()
//
//  Blank template for setting up unit counters and ratios.
//  Current version contains every unit and building listed in Requirements.galaxy whether used or not.
//  Functionality with aliases not tested.
//--------------------------------------------------------------------------------------------------
static void InitCounters (int player) {
//
    // Entries for Protoss Units
    AICounterUnit(player, c_PU_Archon,                 1.00, c_PU_Archon);
    AICounterUnit(player, c_PU_Carrier,                 1.00, c_PU_Carrier);
    AICounterUnit(player, c_PU_Colossus,                 1.00, c_PU_Colossus);
    AICounterUnit(player, c_PU_DarkTemplar,             1.00, c_PU_DarkTemplar);
    AICounterUnit(player, c_PU_HighTemplar,             1.00, c_PU_HighTemplar);
    AICounterUnit(player, c_PU_Immortal,                 1.00, c_PU_Immortal);
    AICounterUnit(player, c_PU_Mothership,              1.00, c_PU_Mothership);
    AICounterUnit(player, c_PU_Disruptor,                 1.00, c_PU_Disruptor);
    AICounterUnit(player, c_PU_Observer,                 1.00, c_PU_Observer);
    AICounterUnit(player, c_PU_Phoenix,                 1.00, c_PU_Phoenix);
    AICounterUnit(player, c_PU_Probe,                 1.00, c_PU_Probe);
    AICounterUnit(player, c_PU_Stalker,                 1.00, c_PU_Stalker);
    AICounterUnit(player, c_PU_VoidRay,                 1.00, c_PU_VoidRay);
    AICounterUnit(player, c_PU_WarpPrism,                 1.00, c_PU_WarpPrism);
    AICounterUnit(player, c_PU_WarpPrismPhasing,        1.00, c_PU_WarpPrismPhasing);
    AICounterUnit(player, c_PU_Zealot,                 1.00, c_PU_Zealot);




    //Entries for Protoss Buildings
    AICounterUnit(player, c_PB_Assimilator,             1.00, c_PB_Assimilator);
    AICounterUnit(player, c_PB_CyberneticsCore,         1.00, c_PB_CyberneticsCore);
    AICounterUnit(player, c_PB_DarkShrine,              1.00, c_PB_DarkShrine);
    AICounterUnit(player, c_PB_Obelisk,                 1.00, c_PB_Obelisk);
    AICounterUnit(player, c_PB_FleetBeacon,             1.00, c_PB_FleetBeacon);
    AICounterUnit(player, c_PB_Forge,                 1.00, c_PB_Forge);
    AICounterUnit(player, c_PB_Gateway,                 1.00, c_PB_Gateway);
    AICounterUnit(player, c_PB_Nexus,                 1.00, c_PB_Nexus);
    AICounterUnit(player, c_PB_PhotonCannon,            1.00, c_PB_PhotonCannon);
    AICounterUnit(player, c_PB_Pylon,                 1.00, c_PB_Pylon);
    AICounterUnit(player, c_PB_RoboticsBay,             1.00, c_PB_RoboticsBay);
    AICounterUnit(player, c_PB_RoboticsFacility,        1.00, c_PB_RoboticsFacility);
    AICounterUnit(player, c_PB_Stargate,                 1.00, c_PB_Stargate);
    AICounterUnit(player, c_PB_WarpGate,                 1.00, c_PB_WarpGate);
    AICounterUnit(player, c_PB_TemplarArchives,         1.00, c_PB_TemplarArchives);
    AICounterUnit(player, c_PB_TwilightCouncil,         1.00, c_PB_TwilightCouncil);




    //Entries for Terran Units
    AICounterUnit(player, c_TU_AutoTurret,              1.00, c_TU_AutoTurret);
    AICounterUnit(player, c_TU_Banshee,                 1.00, c_TU_Banshee);
    AICounterUnit(player, c_TU_Battlecruiser,           1.00, c_TU_Battlecruiser);
    AICounterUnit(player, c_TU_BattlecruiserClass,      1.00, c_TU_BattlecruiserClass);
    AICounterUnit(player, c_TU_BattlecruiserDefensive,  1.00, c_TU_BattlecruiserDefensive);
    AICounterUnit(player, c_TU_BattlecruiserMissile,    1.00, c_TU_BattlecruiserMissile);
    AICounterUnit(player, c_TU_BattlecruiserYamato,     1.00, c_TU_BattlecruiserYamato);
    AICounterUnit(player, c_TU_D8Charge,                 1.00, c_TU_D8Charge);
    AICounterUnit(player, c_TU_Ghost,                 1.00, c_TU_Ghost);
    AICounterUnit(player, c_TU_Hellion,                 1.00, c_TU_Hellion);
    AICounterUnit(player, c_TU_Marauder,                 1.00, c_TU_Marauder);
    AICounterUnit(player, c_TU_Marine,                 1.00, c_TU_Marine);
    AICounterUnit(player, c_TU_Medivac,                 1.00, c_TU_Medivac);
    AICounterUnit(player, c_TU_Raven,                 1.00, c_TU_Raven);
    AICounterUnit(player, c_TU_Reaper,                 1.00, c_TU_Reaper);
    AICounterUnit(player, c_TU_SCV,                 1.00, c_TU_SCV);
    AICounterUnit(player, c_TU_SiegeTank,                 1.00, c_TU_SiegeTank);
    AICounterUnit(player, c_TU_SiegeTankSieged ,        1.00, c_TU_SiegeTankSieged );
    AICounterUnit(player, c_TU_SiegeTank_Alias,         1.00, c_TU_SiegeTank_Alias);
    AICounterUnit(player, c_TU_Thor,                 1.00, c_TU_Thor);
    AICounterUnit(player, c_TU_Viking,                 1.00, c_TU_Viking);
    AICounterUnit(player, c_TU_VikingAir,                 1.00, c_TU_VikingAir);
    AICounterUnit(player, c_TU_VikingGnd,                 1.00, c_TU_VikingGnd);
    AICounterUnit(player, c_TU_Viking_Alias,            1.00, c_TU_Viking_Alias);


    //Entries for Terran Buildings
    AICounterUnit(player, c_TB_Armory,                 1.00, c_TB_Armory );
    AICounterUnit(player, c_TB_Barracks,                 1.00, c_TB_Barracks);
    AICounterUnit(player, c_TB_BarracksReactor,         1.00, c_TB_BarracksReactor);
    AICounterUnit(player, c_TB_BarracksTechLab,         1.00, c_TB_BarracksTechLab);
    AICounterUnit(player, c_TB_Bunker,                 1.00, c_TB_Bunker);
    AICounterUnit(player, c_TB_CommandCenter,           1.00, c_TB_CommandCenter);
    AICounterUnit(player, c_TB_CommandCenter_Alias,     1.00, c_TB_CommandCenter_Alias);
    AICounterUnit(player, c_TB_EngineeringBay,          1.00, c_TB_EngineeringBay);
    AICounterUnit(player, c_TB_Factory,                 1.00, c_TB_Factory);
    AICounterUnit(player, c_TB_FactoryReactor,          1.00, c_TB_FactoryReactor);
    AICounterUnit(player, c_TB_FactoryTechLab,          1.00, c_TB_FactoryTechLab);
    AICounterUnit(player, c_TB_FusionCore,              1.00, c_TB_FusionCore);
    AICounterUnit(player, c_TB_GenericTechLab,          1.00, c_TB_GenericTechLab);
    AICounterUnit(player, c_TB_GhostAcademy,            1.00, c_TB_GhostAcademy);
    AICounterUnit(player, c_TB_MercCompound,            1.00, c_TB_MercCompound);
    AICounterUnit(player, c_TB_MissileTurret,           1.00, c_TB_MissileTurret);
    AICounterUnit(player, c_TB_NuclearReactor,          1.00, c_TB_NuclearReactor);
    AICounterUnit(player, c_TB_OrbitalCommand,          1.00, c_TB_OrbitalCommand);
    AICounterUnit(player, c_TB_PlanetaryFortress,         1.00, c_TB_PlanetaryFortress );
    AICounterUnit(player, c_TB_Refinery,                 1.00, c_TB_Refinery);
    AICounterUnit(player, c_TB_SensorTower,             1.00, c_TB_SensorTower);
    AICounterUnit(player, c_TB_Starport,                 1.00, c_TB_Starport);
    AICounterUnit(player, c_TB_StarportReactor,         1.00, c_TB_StarportReactor);
    AICounterUnit(player, c_TB_StarportTechLab,         1.00, c_TB_StarportTechLab);
    AICounterUnit(player, c_TB_SupplyDepot,               1.00, c_TB_SupplyDepot );
    AICounterUnit(player, c_TB_SupplyDepot_Alias,       1.00, c_TB_SupplyDepot_Alias);




    //Entries for Zerg Units
    AICounterUnit(player, c_ZU_Baneling,                 1.00, c_ZU_Baneling);
    AICounterUnit(player, c_ZU_BanelingEgg,             1.00, c_ZU_BanelingEgg);
    AICounterUnit(player, c_ZU_Broodling,                 1.00, c_ZU_Broodling);
    AICounterUnit(player, c_ZU_Changeling,              1.00, c_ZU_Changeling);
    AICounterUnit(player, c_ZU_ChangelingZealot,        1.00, c_ZU_ChangelingZealot);
    AICounterUnit(player, c_ZU_ChangelingZergling,      1.00, c_ZU_ChangelingZergling);
    AICounterUnit(player, c_ZU_ChangelingZerglingWings, 1.00, c_ZU_ChangelingZerglingWings);
    AICounterUnit(player, c_ZU_ChangelingMarineShield,  1.00, c_ZU_ChangelingMarineShield);
    AICounterUnit(player, c_ZU_ChangelingMarine,        1.00, c_ZU_ChangelingMarine);
    AICounterUnit(player, c_ZU_Cocoon,                 1.00, c_ZU_Cocoon);
    AICounterUnit(player, c_ZU_Corruptor,                 1.00, c_ZU_Corruptor);
    AICounterUnit(player, c_ZU_CreepTumor,              1.00, c_ZU_CreepTumor);
    AICounterUnit(player, c_ZU_Drone,                 1.00, c_ZU_Drone);
    AICounterUnit(player, c_ZU_Hydralisk,                 1.00, c_ZU_Hydralisk);
    AICounterUnit(player, c_ZU_Infestor,                 1.00, c_ZU_Infestor);
    AICounterUnit(player, c_ZU_InfestedTerran,          1.00, c_ZU_InfestedTerran);
    AICounterUnit(player, c_ZU_InfestedTerranEgg,       1.00, c_ZU_InfestedTerranEgg);
    AICounterUnit(player, c_ZU_Larva,                 1.00, c_ZU_Larva);
    AICounterUnit(player, c_ZU_Lurker,                 1.00, c_ZU_Lurker);
    AICounterUnit(player, c_ZU_LurkerEgg,                 1.00, c_ZU_LurkerEgg);
    AICounterUnit(player, c_ZU_Mantaling,                 1.00, c_ZU_Mantaling);
    AICounterUnit(player, c_ZU_Mutalisk,                 1.00, c_ZU_Mutalisk);
    AICounterUnit(player, c_ZU_Overlord,                 1.00, c_ZU_Overlord);
    AICounterUnit(player, c_ZU_Overlord_Alias,          1.00, c_ZU_Overlord_Alias);
    AICounterUnit(player, c_ZU_Overseer,                 1.00, c_ZU_Overseer);
    AICounterUnit(player, c_ZU_Queen,                 1.00, c_ZU_Queen);
    AICounterUnit(player, c_ZU_Roach,                 1.00, c_ZU_Roach);
    AICounterUnit(player, c_ZU_BroodLord,                 1.00, c_ZU_BroodLord);
    AICounterUnit(player, c_ZU_BroodLord,                 1.00, c_ZU_BroodLord);
    AICounterUnit(player, c_ZU_Ultralisk,                 1.00, c_ZU_Ultralisk);
    AICounterUnit(player, c_ZU_Zergling,                 1.00, c_ZU_Zergling);




    //Entries for Zerg buildings
    AICounterUnit(player, c_ZB_BanelingNest,            1.00, c_ZB_BanelingNest);
    AICounterUnit(player, c_ZB_CreepTumor,              1.00, c_ZB_CreepTumor);
    AICounterUnit(player, c_ZB_EvolutionChamber,        1.00, c_ZB_EvolutionChamber);
    AICounterUnit(player, c_ZB_Extractor,                 1.00, c_ZB_Extractor);
    AICounterUnit(player, c_ZB_GreaterSpire,            1.00, c_ZB_GreaterSpire);
    AICounterUnit(player, c_ZB_Hatchery,                 1.00, c_ZB_Hatchery);
    AICounterUnit(player, c_ZB_Hatchery_Alias,          1.00, c_ZB_Hatchery_Alias);
    AICounterUnit(player, c_ZB_Hive,                 1.00, c_ZB_Hive);
    AICounterUnit(player, c_ZB_HydraliskDen,            1.00, c_ZB_HydraliskDen);
    AICounterUnit(player, c_ZB_HydraliskDen_Alias,      1.00, c_ZB_HydraliskDen_Alias);
    AICounterUnit(player, c_ZB_InfestationPit,          1.00, c_ZB_InfestationPit);
    AICounterUnit(player, c_ZB_Lair,                 1.00, c_ZB_Lair);
    AICounterUnit(player, c_ZB_Lair_Alias,              1.00, c_ZB_Lair_Alias);
    AICounterUnit(player, c_ZB_LurkerDen,                 1.00, c_ZB_LurkerDen);
    AICounterUnit(player, c_ZB_NydusNetwork,            1.00, c_ZB_NydusNetwork);
    AICounterUnit(player, c_ZB_NydusWorm,                 1.00, c_ZB_NydusWorm);
    AICounterUnit(player, c_ZB_RoachWarren,             1.00, c_ZB_RoachWarren);
    AICounterUnit(player, c_ZB_SpawningPool,            1.00, c_ZB_SpawningPool);
    AICounterUnit(player, c_ZB_SpineCrawler,            1.00, c_ZB_SpineCrawler);
    AICounterUnit(player, c_ZB_SpineCrawlerUp,          1.00, c_ZB_SpineCrawlerUp);
    AICounterUnit(player, c_ZB_Spire,                 1.00, c_ZB_Spire);
    AICounterUnit(player, c_ZB_Spire_Alias,               1.00, c_ZB_Spire_Alias);
    AICounterUnit(player, c_ZB_SporeCrawler,            1.00, c_ZB_SporeCrawler);
    AICounterUnit(player, c_ZB_SporeCrawlerUp,          1.00, c_ZB_SporeCrawlerUp);
    AICounterUnit(player, c_ZB_UltraliskCavern,         1.00, c_ZB_UltraliskCavern);


}
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Kernel64 on March 16, 2010, 12:21:47 PM
Done some testing. Here are the results.

I used the list thing that I posted to automatically queue in units depending on the number of enemy lings it has seen.

Quote
    if( AIGetTime() < 2) {
        AIResetCounterUnits (player);
    }
    CBlockStatus = ProcessOrder(player);
    if((AIGetTime() < 2) && (CBlockStatus != "InProgress")){
        CBlockStatus = BlockOne(player);
    }

        AICounterUnit(player, c_ZU_Zergling, 1.00, c_ZU_Zergling);
        test = AICounterUnits (player, (3 - player), c_ZU_Zergling);
        test = test / 100;
        SQU = IntToString(test);
        UIDisplayMessage(PlayerGroupAll(), 1, StringToText(SQU));
        if (test > 0) {
            if ( InjectItem(c_ZU_Zergling,
                        "Unit",
                        test,     
                        c_townOne,
                        c_nearCloseDropoff,
                        test,
                        c_techCountInProgressOrBetter,
                        player) == "Success") {
                       
                CBlockStatus = ProcessOrder(player);               
            }       
       
        }
   
    else {
    CBlockStatus = ProcessOrder(player);
    }

What I noticed was:
- the variable test was in hundreds. So divided it by 100 to yield 1 for 100.
- the processorder() has a bug. fixed it just now. (this is the custom thing a few posts up)
- counter unit functions returns 100 per 2 lings, but my processorder func since using AItechcount counts 1 ling as one. So AI makes half of the unit countering it should be doing. multiplying this either through the counter unit, from 1.00 to 2.00 will fix this for lings.

So far, I'm pretty excited of the potentials. I'm going to test structures later on.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Astazha on March 16, 2010, 02:13:40 PM
AICounterUnit() adds that value to the unit every time it runs, so you need to either put it in a function that only runs once, like the InitCounters() function or put it in a block of code that uses a static variable to make sure it only runs once.  Then you'll have the exact ratios you added.


An advantage of this approach is that you can run AICounterUnit() later with -1.00 and it will take the value out (if it was +1.00 to start with) and then put a different unit in.


I don't think that building a framework around that function is a good idea.  There are a lot of ways to counter a particular unit.  To counter Zealots, for example, we don't want to just build banelings.  It would be some combination of Zerglings and Banelings, or Zerglings and Roaches.  Maybe Roaches and Banelings.  The counter might be Mutas later in the game.


Besides, the question we'll be more likely to need to answer is what to do about a combination of Zealots and Stalkers given some approximate ratio that the enemy is using them in.  Does the enemy have detection that would prevent roach micro from stretching those units?  Will the battle take place near photon cannons?  Is AIGameTime() large enough that we should be worried about the sudden appearance of Void Rays?  Dark Templars?  Do we have a Greater Spire up?


Here's what I'm imagining:
1) We use the known units function to be aware of what we know the opponent has.
2) We record historical data making note of what units have been seen in the game at any time, how many there were of each at maximum, and at what time that maximum occurred.
3) We record another set of historical data making note of the last time a particular unit was actually spotted and how many there were then.  If this proves too difficult then we can at least make note of when the known unit count goes to 0.
4) Another data set is generated by our assumptions.  This will be tricky but it will give us the option to make educated guesses based on what has been seen and how long it has been.  For example:  After scouting the opponent's race as Protoss an Overlord scout was sent to replace the Drone scout, which we command to bug out after the 1st Zealot appears.  The Overlord has orders to leave when the Cybernetics Core is built, and when that occurred the Protoss player had only 1 Gateway up.  A Stargate started building as the Overlord was leaving.  We expect the player to produce a Void Ray in X minutes, and to have +1 Void ray every y minutes after that.  The threat is weighted by a probability.  If we had less information we might be uncertain whether the fast teching Protoss was going for Collossi, a DT drop with Warp Gates and Warp Prisms, Void Rays, or some other strat.
5) Based on the above we calculate for a given time (say, 3 minutes from now.) or maybe for multiple times what units we expect to face.
6) The cost in time and resources of producing a unit (and teching to it) is tallied.  Tech costs are counted once, unit costs are multiplied by the number of units needed for a counter.  Units that we already have are deducted from the cost at some point, but maybe not here.  Existing unit upgrades should be taken into account here.
7) This is getting complicated, isn't it?
8 ) Now we calculate the cost of countering each type of unit that the enemy has.  Each of our units is assigned a score for the number of enemy units it is being asked to counter multiplied by the cost of the enemy unit and by a factor that represent how dangerous those units are.  This is divided by the cost of making the counter. 
9) The 3 highest scoring units for each enemy unit are considered.  At this point units that have an unacceptable time cost are removed from the list entirely.
10) We begin by taking the #1 choice for every enemy unit and talling the cost of producing the total number required to counter everything that it is #1 for.  The total cost of countering everything with the #1 counters will be our start point.  This is where the cost of our existing army can be deducted, and the tech costs appropriately reduced.
11) From here the object is to reduce this list of #1 counters (which might be 7 types of our units) down to a list of 2 or 3 things that can counter the whole group most effectively. 
11a) Any unit that has already been reached in the tech tree stays.
11b) Now we make a list of units that are countering the smallest amount of stuff (either # of unit types or value of unit types).  We go to each enemy unit type that our candidate for elimination is countering and see what the 2nd and 3rd choices are.  If one of these is existing (already on the list to counter other stuff) then we substitute so that fewer tech costs are incurred and then recalculate to make sure that this is actually a cost improvement.
11c) Repeat step 11b until no more cost effective consolidations can be made.
11d) Now look at available tech for those units and see how much cost effective it is to pay for upgrades.  Tech that takes longer than the available time should not be considered.  Probably no more than the next level of any tech should be considered.
12) We now have a list of counter units, how many we need, what the cost will be, and a projection of how long they will take to make.  At this point the AI can run a check to see what percentage of that force could actually be produced, given our current income and resources. 
13) Numbers over 100% will represent a solid course of action.  Numbers in the 80-90% range will represent a skin of the teeth defense.  Below that, especially a lot below, should signify that things are desperate.  The Overmind level of AI may wish to get creative instead of trying to counter the entire enemy force.  It might be time to shift to a "Hidden Island" all-in tactic or something similar.


This is complicated as hell.  It will require a lot of arrays.  It will require a lot of data about counters.  It will be fallible in the sense that our counter-units may not be fighting what we wanted them to fight when the battle actually happens.  Overall though, I think it's a reasonable cost/benefit analysis.


This is my 1st brush at the matter, so I am of course open to suggestions.  In the meantime I think "this build counters that build" will be far easier to make and so will be much more effective in the short term.  We can just create favorite unit combinations and pick one of them based on what has been seen.


Also note that initially it isn't necessary to do all of these steps.  We can created a watered down version and grow it from there.  For example, the historical data is unnecessary to start with, we can just work with what we know in the present.  Predictive capabilities can be added later.  I do think that it will be important to quantify what we don't know - this is how I believe we will imitate a player's instincts.  When a player hasn't seen an enemy for 10 minutes, or when it's moving into late game and he hasn't scouted the Island expansions, he knows there is a problem.  He also has an approximate idea of how the opponent's economy is doing, based on how many bases he has and how many of them are old enough to be exhausted.  These are places where recording times can give us additional information.  Hell, the computer when scouting an expansion could count the workers and total the mineral piles.  Simple math to get info from that, but you have to write down when you took that data for it to truly help.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Astazha on March 16, 2010, 03:23:56 PM
Something the above doesn't consider is what the units are weak against.  It would be valuable to look for units that are countered by the fewest number of thing in the enemy group as well.


For starters I'd like to focus on responding with builds to what is seen.  Keep it simple to begin with and grow from there.  I'm still interested in discussing more complex possibilities though.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Kernel64 on March 16, 2010, 04:35:48 PM
So it's like making a chess engine of the sort. Making an AI that can generate it's own list of to-do's and choose between lists of what it can do at certain degree of effectiveness, is something.

I suggest a far simpler approach. An ai that doesn't problem solve, but still solve problems. It will become predictable overtime, in terms of how it responds, when, how much, etc. Eventually people will be able to trick it to doing something by triggering it to do just the thing, and smash it down, depending on how well the AI can shift between blocks of commands, and how fast it checks and rechecks if it should take another block.

This involves creating a database of the sort. These said blocks, or list of commands. These commands may be small, like, Train 6 lings more, tech to hydra, add more drones, add two more ovies.

It won't be as intuitive as what you mentioned, but it can serve, just as you said, to provide some level of responsiveness. It's not as close to an actual thinking engine, but more like a scenario randomizer that allows players to at least develop some skills until they face an actual human opponent.

I sure hope that idea of yours is doable. But if we can make an AI (or most accurately script) that does respond even if not at that level of precision you described, then I believe it would still be worth it.

Your insight is worth noting, and I think those capable should get a crack at it.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: hd on March 16, 2010, 09:20:34 PM
I think the both of you are over complicating the AI and this process. I'm not trying to be a nay-sayer but this is getting some what out of hand. :\
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Astazha on March 16, 2010, 10:54:22 PM
I'm just looking out at the horizon.  In the short term I have more modest goals, like making the AI expand and scout intelligently, and attack based on a probability of success instead of when a some total unit count is reached.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Kernel64 on March 17, 2010, 02:54:47 AM
Yeah, I know hd. Actually, what you said before always keeps me a bit grounded. For the sake of modesty, that is. And it's a good thing you said those words.

But yeah, as Astazha says, I love to think of the what ifs. But hey, if

"If (AITechCount( player, c_zu_Spawningpool, 3) != 1) works, maybe, just maybe, if we consider number of gateways and pylons as a single unit, we could do something like

HyperCounterUnit(data_struct_Gatewatys);

???
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Kernel64 on March 17, 2010, 10:14:29 PM
Here are some development notes that might be useful to anyone interested. The idea is to make a process for ai command execution and adding/modifying the OrderList -- the main list that the AI will process line by line.

Quote
Ultimate Goal:
        AI can decide the best course of action. But to do that, it must be able
        to filter through a collection of data.
       
        - it can get the numbers of what is. True, but it considers everything
        equal. How does it evaluate so that it will decide to make 4 drones in
        the beginning of a game?
            a) it is the most immediate thing it can buy.
            b) it is told to do so.
            c) it can see it will be needing the resource rate to make it happen.
           
        Option C is probably something very hard to pull of. There's just too
        much equation involved when you can simply feed it with information
        you know -- option B.
       
        Option A means that other decision factors must come into play whenever
        resources are high. Even so, a smart human will still spawn drones if
        it forsees a long match -- that it is going to need to gather resource
        to match the opponent's resource when the time comes what is in the bank
        has been depleted.
       
        note: AI can choose to go ECON, and choose DRONES if a function exists
        where the resource rate of its opponent is measured. It can then:
            a) WANT TO MATCH THAT RESOURCING RATE,
            b) BEAT IT by a degree,
           
        How I think:
            I chose to make all larva into drones because:
                a) there was no immediate threat, or
                b) the immediate threat is no longer a threat because
                    i) I have military to:
                        1.) Match it, or
                        2.) Hold it for enough time where the second
                            batch of larva and the production of units
                            is already possible.
            I did not think of:
                a) What if the opponent used that time to spawn units, and
                thereby pose a greater threat so that at the next turn,
                even if I produced all larva, I wouldn't be able to match it?
               
        AI Proposal:
            1) Check if opponent is producing ECON,
                a) yes:
                    i) Produce your own if you can
                    ii) Produce military and attack
                   
            note on ii: This would be very strong if the ratio after producing
                units is, Mine > Opponent
           
            therefore, we choose ECON only if we have equal military, we have
            no plans of attacking, we want econ.
           
            We WANT econ if:
                a) Enemy is going econ
                b) Military status quo is at a neutral level
                c) We do not wish to increase our rating Military wise
                d) We have free resource and larva,
               
            Two types of Econ building:
                a) Greed - we want more, because we can.
                b) Need - we have to, because we do not have.
               
            NEED
                - econ becomes the highest of priorities, even if we can
                do anything else;
                - we do not do this if military is not neutral, or minimal
               
                Military Status Quo reads enemy data of alive and in progress.
                Response to these must be immediate.
               
            GREED
                - everything else in the current status quo of demands have
                been met,
                - we have surplus larva, and minerals,
               
HOW TO DETERMINE THE STATUS QUO

    a) By feeding a list of what needs to be there at a certain phase
    b) By reading presently existing and changing data and responding:
   
    ECONOMIC STATUS QUO (B)
        1) We are greater, lesser, or equal to the enemy's economy
       
        How it works:
            1) READ ENEMY ECON STATUS
            2) CHECK IF WE NEED MILITARY
                a) No
                -Check if we are higher or lower or equal...
                    i) Lower
                        -check how many we can morph
                        -check how many we need
                        ()we can morph LESS THAN WE NEED TO:
                            -add them to queue
                        ()we can morph MORE THAN WE NEED TO:
                            -queue needed only
                        ()we can morph EXACTLY AS NEEDED:
                            -queue them
                   
                    ii) Higher (or Equal)
                        >> CHECK IF THERE IS A TECH NEEDED/WANTED...
                        yes:
                            *check if we have spare money after spending
                            on tech...
                                yes:
                                    -check how much we can spend on drones,
                                    -check how many larva are free
                                    -make accordingly (add to queue)
                        no:
                            {proceed to PRODUCTION check}
                           
                        >> CHECK IF THERE IS A PRODUCTION NEEDED/WANTED...
                        yes:
                            *check if we have spare money after spending
                            on Production
                                yes:
                                    -check how much we can spend on drones,
                                    -check how many larva are free
                                    -make accordingly
                        no:
                            *check if our money is good for expand
                                yes:
                                    -add expand 1 to queue
                   
                b) Yes
                    >> SATISFY MILITARY STATUS QUO
               

TECH-ING
    I want to tech because:
        a) I need to respond to a threat
            This is common. Human players choose a tech path based on what they want to produce
            in military. Upgrades are not considered tech.
           
        b) I can
        c) having this tech will allow me to counter threat better
        d) this tech is a prerequisite
       
    I tech even without a threat and way before a threat can be read because:
        a) I can
            -Everything else is satisfied
            -There is free resource
       
        b) It is a prerequisite to what I want/need now in Production queue
            -This is a call from other Directives. Directives such as Military
            and production can add a tech to the queue. But only if they
            absolutely need them.
           
    Why choose a baneling nest over roach warren and vice-versa?
        a) Enemy race factor
        b) Enemy forces factor
       
        I choose baneling nest if I'm looking to make banelings; R.Warren if
        roaches. Simple.
       
Issue: If the AI only builds tech if Threat data is already there, it will be
        too late when the threat arrives?
       
        Solution:
            We gather TECH data from the enemy and respond accordingly.
       
        How do you choose between two tech types available given enemy tech
        data without feeding the AI what's the best course?
       
            We can't. We have to have a list that the AI will use to evaluate
            what tech to respond with against a specific tech.
           
        We NEED Baneling nest tech if we see three gateways, and no Cybernetics
        core. (vs Toss)
        We NEED Roach Warren tech if we see two gateways, and no Cybernetics core..

Experience:

        (Vs. Toss with Early Immortal attack)
       
        When I saw gateway and cybernetics core, I was double minded between
        facing a stalker army/air/or immortals.
       
        When there was 3 zealots, I made lings. Harrassed, lost the lings, did
        not replenish, as they were not within range of attacking.
       
        I had to choose between mass lings/Muta/Muta:
            -Chose to expand early since I am capable of dealing with
            stalkers with lings, and there were no stalkers yet. (tech is ignored)
            -Chose to add gas/lair when RoboBay was in progess, and no
            stalkers were there. (lair was chosen as tech vs robobay. 2 gas was required by Lair)
           

    Therefore, we must have a list that the AI can use to counter tech choices of the enemy.
        ex.
            Tech to counter: Robotics Facility
            Tech Threat Count: 2
            Tech Counter: Lair, Spire

When do we Queue in a tech?

        a) When we need it for a Queen
        b) When we need it for a military response
        c) When it is a prereq to a desired tech
       

//==================================================================================================       
TECH MANAGER
        >>Check demand list which is queued by MILITARY, ECON, PRODUCTION, and self
        *do we have an item?
            yes:
                add this to the queue, and remove from our demand list
            no:
                (proceed)
        (since we have nothing demanded from other managers, let's see if we can slip in a tech)   
        (we always want to get to Hive. So we want prereqs done so we can get to hive)
        >>Check if we have Hive tech...
            no:
                >>Check if we have Lair tech...
                    no:
                        Check if we have Spawning Pool...
                            no:
                                add Spawning Pool to our demand list..
                                (proceed)
                            yes:
                                add Lair Tech to our demand list..
                                (proceed)
                    yes:
                        Check if we have Infestation Pit
                            no:
                                add Infestation Pit to our demand list...
                                (proceed)
                            yes:
                                add Hive tech to our demand list...
                                (proceed)
            yes:
                (proceed)
        (Let's see if we can slip in an Evo Chamber)
        >>Parse the Main Order List, and count the total cost...
        >>Get our current money and deduct the total cost of Order List items...
            *do we have spare money?
                yes:
                    >> Check if we can buy 1 evo chamber...
                        yes:
                            add evo chamber to our demand list.
                        no:
                            (proceed)
                no:
                    (proceed)
       
        (let's see if we can morph previos techs that was not called by Military, Prod, and Econ)
        >>Check if we have Hive tech...
            yes:
                >>Check what tech we don't have...
                    no Spire:
                        >>Check if we have spare money for this...
                            yes:
                                add to demand list.
                            no:
                                (proceed)
                   
                    no Hydra Den
                        >>Check if we have spare money for this...
                            yes:
                                add to demand list...
                            no:
                                (proceed)
                   
                    no GreaterSpire:
                        >>Check if we have spire...
                            yes:
                                >>Check if we have money for this...
                                    yes:
                                        add to demand list..
                                    no:
                                        (proceed)
                            no:
                                (proceed)
                   
                    no Nydus Network:
                        >>Check if we have money for this...
                            yes:
                                add to demand list...
                            no:
                                (proceed)
                   
                    (etc...)
            no (hive):
                >> Check if we have lair...
                    (do same yes and no here)
   
       
Note:    Our Military, Econ, and Production managers must always add what they need to the demand list,
        otherwise, the Tech Manager will choose a tech based on what it wants: TO GO HIVE and FULFILL
        EVERY MISSING TECH.
       
        the order of Fulfilling missing tech is from Hive down to Hatchery tech.
       
//======================================================================================================

Checking if we need Military...

    This function evaluates the Military Status Quo, and responds respectively.
   
MILITARY MANAGER

    what this does atm:
        1. Checks current enemy military force, and responds by adding unit orders to the OrderList.
        2. Updates the order list when we have what we want to produce in there.
        3. Demands tech if tech for Best Counter does not exists, also with secondary counter.
       
        -------------------------------------------------------------------------------
        ex.
            We see: Marauders, 10 units.
            Our Feed says:
                Best Counter Unit: Mutalisk
                Best Count: 40 percent
                Secondary Counter Unit: Hydralisk
                Best COunt: 100 percent
           
            We check if we have these units. If no:   
                We check if we can produce, if no:
                    We Demand Spire, because of Mutalisk
                    We check if we can produce secondary:
                        no:
                            We Demand Hydra Den because of Hydra
               
                our Tech Demand List becomes:
                    0 - Spire
                    1 - HydraDen
                    Tech List will process Tech demands like this:
                        Spire will be checked if it can be morphed, if not:
                            Pool will be checked, if not exists:
                                Lair is put first into the TechList.
                                Then Pool is put first into the techlist.
                            Tech list becomes:
                                0 - Pool
                                1 - Lair
                                2 - Spire
                                3 - HydraDen
        -------------------------------------------------------------------------------

    >> Gather current Enemy Military Data
        - list every enemy military unit by type and count, All in queue + progress + done. Most powerful
            comes first.
        - parse the list each line and: (repeat until the list is done)
            >>Get info on the best counter to this unit..
            >>Check if we have that unit include: ready, in queue, and in production...
           
            NO: Check if we can produce that unit..
                no:
                    >>Demand required tech, add to TechDemand list..
                    >>get info on the second best counter to that unit..
                    >>Check if we can produce that unit..
                        no:
                            >>Demand tech, add to TechDemand List..
                            >>Is this unit a Ground based unit?
                                yes:
                                    determine how many we can produce
                                    queue to our templist the default response unit to Ground Threats.
                                no:
                                    determine how many we can produce
                                    queue to out templist the default response unit to AIR threats.
                        yes:
                            determine how many we can produce
                            Queue that unit to our templist...
                yes:
                    determine how many we can produce..
                    Queue that unit to our templist...
           
            YES: Check if what we have in total is in the appropriate count...
                no:
                    add more..
                yes:   
                    (proceed)
        (continue parsing the list until done...)
           
    (we now have a templist)
    (we need to check the main OrderList, if we have these items listed)
    >> (Check OrderList if we have it there, update if necessary, add if not there)
        - Parse Orderlist by line
            - Parse ours each line until list is done..
                *Do we have it there?
                    yes:
                        *Is the count updated?
                            no:
                                change count to current count..
                            yes:
                                (proceed)
                                remove this line from templist
               
                    no:
                        add this line to the OrderList
                        remove this line from the templist
            (Continue until we've checked the entire templist)
        (continue until we've checked the entire OrderList)
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Astazha on March 17, 2010, 11:58:41 PM
I'm trying to get to approximately the same place with my Overmind project, just going to take it in small pieces.


Quotea) it is the most immediate thing it can buy.
            b) it is told to do so.
            c) it can see it will be needing the resource rate to make it happen.
I'm shooting mostly for C, with perhaps a tiny bit of B.  It's important to me to have an AI that makes decisions for itself.

I think the resource rate is very doable, and I'm working on that angle now.  We can initially just feed it an order or have it select from them based on map size and personality, but the problem just appears later in game if we don't think about it.

It requires 6 (about 5.5 actually) drones to produce an income that will finance Zerglings as fast as a Hatchery makes larva.

It requires 14 drones to do that if the Hatchery has a queen.  (These values are calculated, not from testing, so the AI can reach these conclusions without help.)

We want some amount of surplus for development unless (and this should be the unusual case) it is tactically worthwhile to spend everything on military at that time, but that cannot be sustained unless we're on the winning side of the exchanges.  Exactly how much surplus could be one of our personality variables.
So you can start with this kind of thing - if you told the AI you wanted to have double the required income it would conclude that an 11-Pool is the thing to do, which would be reasonable initially.  Once it evaluated that it could improve it's production capacity by building a queen, it would conclude that it wanted twice the economy of 14 Drones, which isn't possible with one town.  At this point, it must make do with one town until conditions are ripe for expansion.  Only 24 Drones would be useful so it would stop there, or perhaps even stop for a bit at the point of diminishing returns, 16, to press the military angle based on scout information about the enemy unit count and the recognition that 6 or 12 or whatever Zerglings isn't going to cut it anymore.


Once the military is happier it runs up to the max of 24.  Somewhere in there it starts to wish for Banelings to deal with this Zealot infestation and goes for gas.  At that point the AI needs another Hatch for increased production capacity and it needs an Expansion to make it's economy any better.  It will make a security evaluation and either Expand, Hidden Expand, or just build a 2nd Hatch at home to boost military production and reach a security level that will permit truly expanding.

I don't know that 2x will serve us throughout, but it can always be modified by other routines based on game conditions - most likely this would be data about enemy force sizes.

I think at the strategic level it's worth guessing what the enemy's economy is, but most of the time it will be exactly that.  Our scouts aren't going to get to see their mineral line once the game gets going, so we'll have to make assumptions based on the number of known expansions, etc.

I ran some testing today on economic rates.  These numbers are pretty close:


Quote1st 2 Drones on a mineral pile:  2/3 of one mineral per second (each)
3rd Drone on a mineral pile: 1/3 of one mineral per second
1st 2 Drones on gas: 2/3 of one gas per second (each)
3rd Drone on gas: 1/2 of one gas per second

Hatcheries produce 1/15 larva per second, and a queen hatch pair produces an additional 1/10 larva per second.  The Overmind thread has a link to a function that calculates larva & larval rates, and I'm working on the economic functions right now, to include marginal rates, % of possible economic capacity, payoff times, etc.

I'm also managing gas between certain min and max thresholds so that peons aren't wasted for gas when we're dying for minerals.  Right now it's half-ass, but I'll be wanting to make that sing us a tune before long.

I'm initially making nothing but Zealots and Banelings available to the AI, and asking to defeat Starcrack's full Protoss with that.  This will drive the framework and decision making infrastructure without bogging it down in the endless list of unit possibilities.  Use of subroutines to keep the decision making modular will allow us to increase the sophistication of a particular aspect without upending the whole framework.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Astazha on March 18, 2010, 12:03:52 AM
So for example the Military subroutines (Cerebral?) will produce the output:  I want Zerglings and Banelings in this ratio, with no more than X Banelings but as many Zerglings as I can get.


The Overmind will then balance that with everything else.  It doesn't matter if the initial way to reach the decision about unit composition is really poor or even random, because you can make it sophisticated later and the Overmind doesn't know the difference, it's just getting better input.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Kernel64 on March 19, 2010, 02:34:08 PM
Progress:

Have made a simply prototype of an engine that uses the orderlist which can be stacked on to or queued into with commands.

The AI's personality is currently based on randomization, and my plan is to use the modular blocks to add a list into the Order List. This Order list is a simple collection of arrays which are translated into AITrain/AIBuild/Expo.

It would be interesting to see if it's possible to parse from a text file. This way, we can maybe make an AI editor which people can use to define their custom personalities, order blocks, etc. and share them. We can then make a simple form that reads from other files and bunch them together. This way we'll have more scripts to play with.

The idea of the engine is that, it uses blocks to have a guideline for proactive behavior. Reactive modifies how those orders are executed, and sometimes skipped altogether, proceeding to the next block, when called for.

I'll post it up when a more stable one is available. Then anyone can use it for further development.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Astazha on March 19, 2010, 02:55:20 PM
Would love to see that.  I'm going AITrain for sure instead of the stock functions - that's one of my next big goals.  My intent is not really to create a queue but to have the AI decide every time what it should be building right now.  Unless conditions change, it should be heading for the same goal during every calculation cycle anyway, and if things have changed then changing directions is appropriate..


The reading from a text file thing: we can parse XML... the only thing is I'm not sure if you can reach XML that is outside of the MPQ.  Even if you couldn't though, a utility could be generated that would edit the AI variables without someone having to really get into code.


Another idea, and one that I'll be doing for testing at least:


I want to build the Overmind AI so that I can co-exist with another Zerg AI.  The idea is to have


1) A constant determining how many of the AI are allowed
2) a static global variables tracking how many are present already
3) code in the ZergInit that looks to see if an Overmind AI slot is available, if not this player gets the default.
4) a player indexed array will store which AI a player is using, and all of the appropriate functions will have to have if statements to shift between them.  This will mean that it will take a significant amount of surgery to install Overmind into a new AI version in this manner.


At the most basic level you would just replace the OpenGnd0 etc. level of code, but if you really wanted to see the full difference then the unit-behavior functions in TacticalZerg and the army behavior in MeleeAI etc. would have to have if statements as well.  This would allow you to see the difference that is made by things like the improved Queen routine we wrote.


It may not be worth the hassle for release, but for testing ZvZ this would be invaluable.  If you did release it like that, then the number of allowable AI's of each type could be one of the XML variables.  Or it could be random selection.  A 2v2 would be more interesting if a mix of completely different AI's was possible - though that would be a nightmare to maintain.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Kernel64 on March 19, 2010, 06:17:48 PM
Attached are the files.

Attached all these since I have no recollection of what exactly and which ones I've tampered. I'm sure though that one of these contained that thing where the engine jumps to the next state.

Zerg will remain within Opengrnd.

All the functions are inside the Zerg0.

It's not clean, but you should see what's happening.

I'm using the said OrderList (currently just arrays) as a list, where queuing or stacking happens.

Stacking on top is important to this process, since sometimes, I want the AI to prioritize a Train/Build/Expo, and I see that in the future, military will use this often.

You'll see in the Zerg0 how this happens, and how it shows that even if a current order in the list isn't finished, another one can be placed on top of it. etc.

Anyway, tell me what you think could be improved.
Title: Update
Post by: Kernel64 on March 25, 2010, 05:34:26 PM
System has been tested.

So far, reaction is faster, and more immediate. I may be able to provide the first version soon.
Title: Re: [For Scripting ZvP, PvZ] AI Strats (includes Triggering)
Post by: Kernel64 on March 31, 2010, 01:53:52 AM
Updated the opening post with the entire codes I'm working on right now.

It's all zerg atm, but everything can be copy-pasted for the other races. It needs to be fed with commands on what to do, when, etc., but the system is working.