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

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

Previous topic - Next topic

Kernel64

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

Kernel64

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

Astazha

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.



Kernel64

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.

Astazha

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.



Astazha

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.

Astazha

It does work on buildings... this is exciting.


::crawls into cave with computer::

Kernel64

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

Astazha

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.

Aeg1s

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.

Kernel64

Cool.

Does calling AICounterUnit() actually make the player make the MakeWhat?

Astazha

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


}

Kernel64

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.

Astazha

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.

Astazha

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.