[For Scripting ZvP, PvZ] AI Strats (includes Triggering)

Started by Kernel64, March 08, 2010, 04:12:02 PM

Previous topic - Next topic

Kernel64

Moving the code to Mapster.

Kernel64

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)

Kernel64

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()

hd

I don't get why you have half the functions you do...

Kernel64

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.

hd

...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.

Astazha

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.

Kernel64

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.

hd

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.

Astazha

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.

hd

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.

Astazha

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.

Kernel64

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.

Kernel64

#13
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.

Aeg1s

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
I posted some of them in 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.