Idea for AI unit detection...

Started by hd, March 05, 2010, 08:20:17 PM

Previous topic - Next topic

hd

So, I'm trying to make my AI identify enemy units/buildings when they become visible to my AI's units/buildings.

I'm thinking of adding it to the wave think

void AIWaveThinkZerg (int player, wave w, int type) {
    // hd: seeing if we can pick up opposing unit activity
    unitgroup found;
    unitgroup enemyGroup;
    int enemyCount;
    aifilter filter;
   
    AIWaveThinkDefault(player, w, type);
   
    for (int i = 0; i < AIWaveUnitCount(w); i++) {
        found = AIFindUnits(player, null, UnitGetPosition([cycle through wave units here]), [sight range of unit here], c_noMaxCount);
        // filter = AIFilter(player);
        //AISetFilterAlliance(filter, c_playerGroupEnemy);
        enemyGroup = UnitGroupFilterThreat(found, [wave unit], null, 0);
        enemyCount = UnitGroupCount(enemyGroup, c_unitCountAlive);
       
        if (enemyCount <= 0) { return; }
        else {
            // found something, add found enemy unit/building to an array of enemy units
            // set a priority system based off count of enemy units found / type of units
        }
    }
}


Now, obviously the above code won't work as is. I don't know how to cycle through each unit in a wave or to obtain the field of vision of a unit... but if we could get this solved, it would be really easy to make the AI very human like, make scouting mean something and setup reactive game play from the AI

Kernel64

#1
I thought about reusing the filtering done within the Think functions. The get would be in groups, and each unit type is then counted and appropriate actions are taken.

I'm at the very conceptual stages though, and haven't examined all the functions yet.

If I do stumble upon something, I will post them here.

edit:

this lines for example:

Quote
//    findResults = AIFindUnits(0, c_NU_Minerals, UnitGetPosition(aiUnit), 8, c_noMaxCount);
//    if (UnitGroupCount(findResults, c_unitCountAll) == 0) {
//        findResults = AIFindUnits(0, c_NU_HighYieldMinerals, UnitGetPosition(aiUnit), 8, c_noMaxCount);
//        if (UnitGroupCount(findResults, c_unitCountAll) == 0) {
//            OrderSetTargetPoint(ord, UnitGetPosition(aiUnit));
//            AICast(aiUnit, ord, c_noMarker, c_castHold);
//            return;
//        }
//    }
//
//    OrderSetTargetUnit(ord, UnitGroupUnit(findResults, 1));
//    AICast(aiUnit, ord, c_noMarker, c_castHold);
//}

Somehow, it counts the number of high yield meeaneraaallz (female voiced). So, through this, each unit will have to be listed, checked, etc. The code will be hell though.

I'm certain you can find better means to do the same with fewer lines, by cycling through each race specific unit type in your array.

I'm shooting in the dark, as always. I'm hoping you find it.

hd

I'm a few steps closer to being able to test this...

I need to know how to get the sight radius of a unit. I was thinking it would have something to do with UnitGetPropertyInt but I don't know what flag to use to get it.

void AIWaveThinkZerg (int player, wave w, int type) {
    // hd: seeing if we can pick up opposing unit activity
    unitgroup found;
    unitgroup enemyGroup;
    unitgroup waveGroup = AIWaveGetUnits(w);
    int enemyCount;
    aifilter filter;
   
    AIWaveThinkDefault(player, w, type);
   
    for (int i = 0; i < UnitGroupCount(waveGroup); i++) {
        found = AIFindUnits(player, null, UnitGetPosition(UnitGroupUnit(waveGroup, i)), [sight range of unit here], c_noMaxCount);
        // filter = AIFilter(player);
        //AISetFilterAlliance(filter, c_playerGroupEnemy);
        enemyGroup = UnitGroupFilterThreat(found, [wave unit], null, 0);
        enemyCount = UnitGroupCount(enemyGroup, c_unitCountAlive);
       
        if (enemyCount <= 0) { return; }
        else {
            // found something, add found enemy unit/building to an array of enemy units
            // set a priority system based off count of enemy units found / type of units
        }
    }
}

Kernel64

#3
EDIT:

this function seems to get the Overseer's range:

Quote
fixed AIRangeOverseer (int player, unit aiUnit) {   
    return AIUnitFixed(player, c_ZU_Overseer, c_fieldSightDawn) + 1;

    //return MaxF(MaxF(AIAbilityFixed(player, c_AB_FungalGrowth, c_fieldRange0),
    //    AIAbilityFixed(player, c_AB_AcidSpores, c_fieldRange0)),
    //    AIAbilityFixed(player, c_AB_Changeling, c_fieldRange0)) + 1;
}

c_fieldSightDawn is something.

Quote
fixed AIUnitFixed (int player, string entry, string field) {
    return StringToFixed(AIUnitStr(player, entry, field));
}

AIUnitFixed() seems to return data from somewhere.

then we have this catalog:

Quote
//--------------------------------------------------------------------------------------------------
//  CATALOG ACCESSOR CONSTANTS
//--------------------------------------------------------------------------------------------------

const string c_fieldAmount          = "Amount";
const string c_fieldAreaRadius0     = "AreaArray[0].Radius";
const string c_fieldAreaRadius1     = "AreaArray[1].Radius";
const string c_fieldAreaRadius2     = "AreaArray[2].Radius";
const string c_fieldAreaFraction0   = "AreaArray[0].Fraction";
const string c_fieldAreaFraction1   = "AreaArray[1].Fraction";
const string c_fieldAreaFraction2   = "AreaArray[2].Fraction";
const string c_fieldAttrStructure   = "AttributeBonus[Structure]";
const string c_fieldAttrArmored     = "AttributeBonus[Armored]";
const string c_fieldAttrPsionic     = "AttributeBonus[Psionic]";
const string c_fieldAttrLight       = "AttributeBonus[Light]";
const string c_fieldDrainFactor     = "DrainVitalCostFactor";
const string c_fieldEffectChange0   = "VitalArray[0].Change";
const string c_fieldEffectChange1   = "VitalArray[1].Change";
const string c_fieldEffectChange2   = "VitalArray[2].Change";
const string c_fieldEnergyCost      = "Cost[0].Vital[Energy]";
const string c_fieldEnergyMax       = "EnergyMax";
const string c_fieldFilters         = "SearchFilters";
const string c_fieldMinRange        = "MinimumRange";
const string c_fieldModification0   = "Modification.VitalRegenArray[Energy]";
const string c_fieldPeriodCount     = "PeriodCount";
const string c_fieldRadius          = "Radius";
const string c_fieldRadiusBonus0    = "AreaArray[0].RadiusBonus";
const string c_fieldRange0          = "Range[0]";
const string c_fieldRange           = "Range";
const string c_fieldSightDawn       = "Sight";
const string c_fieldTargetFilters   = "TargetFilters";
const string c_fieldTargetFiltersAB = "TargetFilters[0]";
const string c_fieldTargetFilters0  = "ValidatorArray[0].value.value";

And we find "const string c_fieldSightDawn       = "Sight";"

Thus, it could possibly mean that since:

"AIUnitStr(player, entry, field)" used as:  "return StringToFixed(AIUnitStr(player, entry, field));"

where field is c_fieldSightDawn       = "Sight";

We can get a unit's sight range through something like this:


Fixed MyUnitRange = StringToFixed(AIUnitStr(player, [wave_unit(i)], "Sight"));

Where wave_unit(i) is a unit among an array of units taken from a group of units.

Thus, our function will be:

Quote
void AIWaveThinkZerg (int player, wave w, int type) {
    // hd: seeing if we can pick up opposing unit activity
    unitgroup found;
    unitgroup enemyGroup;
    unitgroup waveGroup = AIWaveGetUnits(w);
    int enemyCount;
    aifilter filter;
   
    AIWaveThinkDefault(player, w, type);
   
    for (int i = 0; i < UnitGroupCount(waveGroup); i++) {
        found = AIFindUnits(player, null, UnitGetPosition(UnitGroupUnit(waveGroup, i)),
                                                           ( StringToFixed( AIUnitStr(player, UnitGroupUnit(waveGroup, i), "Sight")  ) ),
                                                           c_noMaxCount);
        // filter = AIFilter(player);
        //AISetFilterAlliance(filter, c_playerGroupEnemy);
        enemyGroup = UnitGroupFilterThreat(found, [wave unit], null, 0);
        enemyCount = UnitGroupCount(enemyGroup, c_unitCountAlive);
       
        if (enemyCount <= 0) { return; }
        else {
            // found something, add found enemy unit/building to an array of enemy units
            // set a priority system based off count of enemy units found / type of units
        }
    }
}

Now this:
Quote
int ZealotCount;

ZealotCount = UnitGroupCount(AIFilterPathable(AIFindUnits(enemyPlayer, c_PU_Zealot,
                                                  UnitGetPosition(UnitGroupUnit(waveGroup, i)),
                                                  StringToFixed( AIUnitStr(player, UnitGroupUnit(waveGroup, i),
                                                  enemyCount),
                                                  UnitGetPosition(UnitGroupUnit(waveGroup, i))),
                                                  c_unitCountAlive)

will get the number of Zealots found within range of waveGroup, among the number of enemy units found.

What I also don't know how is getting the AI to determine the human player's int value.

Heinermann

Are you looking for this?
http://galaxywiki.com/wiki/AIKnownUnitCount
http://galaxywiki.com/wiki/AIEvalLargestEnemy

You can also detect the AI's own units by making the target player the same as the AI player.

hd

#5
Okay, so I took a different approach just to see if I could get the AI's units (not the enemies).

After some tinkering, I created a function that returns a unitgroup that consists of all of the AI's forces.

Now, this would be used only once to get the starting units (buildings included) for the AI. Then you could create a recursive function to check each unit and add any additional AI forces to the check list and then check to see if any of the AI's units see anything.

unitgroup zergGetAllUnits(int player) {
    unitgroup pUnits = null;
    int unitCount;
    unit pUnit;
    string unitType;
    int droneCount;
    unit startUnit;
   
    startUnit = AIGrabUnit(player, c_ZU_Drone, c_prioNone, null);
   
    pUnits = AIFindUnits(player, null, UnitGetPosition(startUnit), 1000, c_noMaxCount);
    unitCount = UnitGroupCount(pUnits, c_unitCountAll);
    droneCount = 0;
   
    TriggerDebugOutput(1, StringToText("Unit count for Player " + IntToString(player) + ": " + IntToString(unitCount)), true);
   
    while (unitCount > 0) {
        pUnit = UnitGroupUnit(pUnits, unitCount);
        unitType = UnitGetType(pUnit);
         
        if (unitType == c_ZU_Drone) {
             droneCount = droneCount + 1;
        }
       
        unitCount = unitCount - 1;
    }
   
    TriggerDebugOutput(1, StringToText(IntToString(droneCount) + " drones"), true);
   
    return pUnits;
}


One thing I've learned from this is you cannot use a null point for AIFindUnits because it will return 0. I also found that there is a maximum range you can use (1 million crashed the game). If you specify null on the unittype, it will return all units found, period.

The difficult part is getting a unit to start with (hence AIGrabUnit). AIGrabUnit allows you to specify a null point (grabbing the first one of the specified type it finds). However, I don't think you can specify a null unittype for AIGrabUnit because then it won't know what to grab... on the contrary, it could just grab the first unit it finds period.

Now, if we think back to the wave think function, I believe all units end up in a wave from the very beginning of a game. If that's true, we can just cycle through each wave, grabbing each unit until eventually, we find something.

hd

Quote from: Kernel64 on March 06, 2010, 09:45:43 AMWhat I also don't know how is getting the AI to determine the human player's int value.

int hdGetOpponent (int player) {
    unitgroup enemyGroup;
    int enemyPlayer = -1;
    enemyGroup = UnitGroupAlliance(player, c_unitAllianceEnemy, null, null, c_noMaxCount);
    enemyPlayer = AIGetPlayerGroup(enemyGroup);
   
    return enemyPlayer;
}


I haven't tested it for more than 2 players, but it works flawlessly 1v1.

Kernel64

Thanks, Heinermann!

This is brilliant,Hd! So, with the wavethink function, we can use the evalLargestenemy to get the enemyPlayer.

The WaveThink tries to simulate a LoS awareness for the AI. It doesn't remember what he had before, and constantly needs to see units in order to decide, if I'm not mistaken?

Have you tested the WaveThink yet?

I'm trying to discover how the functions involving waves and their strengths and ratios, for the purpose of having the AI evaluate itself, with regards to what it has vs. the enemy atm.

Waves in retreat, in the base, attacking, divert, etc.

hd

Quote from: Kernel64 on March 06, 2010, 01:32:00 PM
Thanks, Heinermann!

This is brilliant,Hd! So, with the wavethink function, we can use the evalLargestenemy to get the enemyPlayer.

The WaveThink tries to simulate a LoS awareness for the AI. It doesn't remember what he had before, and constantly needs to see units in order to decide, if I'm not mistaken?

Have you tested the WaveThink yet?

I'm trying to discover how the functions involving waves and their strengths and ratios, for the purpose of having the AI evaluate itself, with regards to what it has vs. the enemy atm.

Waves in retreat, in the base, attacking, divert, etc.

Well, theoretically we can keep track of what it sees and make decisions based on what it thinks the enemy has... what would be very useful is figuring out how to subtract an unit from the known enemy units when an unit is killed.

Kernel64

What if we make the script omniscient first, then add in the simulation later? It will check as the script rolls and gets updated of what's on the board all the time.

It should then do what it considers the best set of actions based on some rating.

I'll post the full concept of this later, after some rest.

hd

Quote from: Kernel64 on March 06, 2010, 04:05:29 PM
What if we make the script omniscient first, then add in the simulation later? It will check as the script rolls and gets updated of what's on the board all the time.

It should then do what it considers the best set of actions based on some rating.

I'll post the full concept of this later, after some rest.

It would probably be best to have it check known vs all, updating what it knows and comparing it to all units of the enemy. then every 30-45 seconds it wipes what it knows and starts over or when a manual call comes in to wipe what it knows. this would allow for strategy changes.

As for knowing when to retreat and what not, that's already in the game. it has a weight value of its units vs the opposing units. if the enemy units out weigh the ai's units, it retreats. of course, you can turn this feature off i believe.

Aeg1s

AIKnownUnitCount(player, enemyPlayer, unitClass); will return the number of enemy units and if you haven't disabled normal vision via: AISetDifficulty(player, c_diffNormalVision, false); then the AI can only see what's in its normal vision range (ie. no cheating and seeing everything on the map).

Kernel64

Quote from: Aeg1s on March 06, 2010, 08:14:47 PM
AIKnownUnitCount(player, enemyPlayer, unitClass); will return the number of enemy units and if you haven't disabled normal vision via: AISetDifficulty(player, c_diffNormalVision, false); then the AI can only see what's in its normal vision range (ie. no cheating and seeing everything on the map).

Does this mean that each time we call AIKnownUnitCount, where seeing everything is off, the AI will only return the count of what it sees?

So, say, the previous second, the ai saw 6 lings. And it retreats. Now, while in the base and no scouts, the AIKnownUnitCount will return 0? This is correct right?

Aeg1s

Quote from: Kernel64 on March 07, 2010, 02:49:34 AM
Quote from: Aeg1s on March 06, 2010, 08:14:47 PM
AIKnownUnitCount(player, enemyPlayer, unitClass); will return the number of enemy units and if you haven't disabled normal vision via: AISetDifficulty(player, c_diffNormalVision, false); then the AI can only see what's in its normal vision range (ie. no cheating and seeing everything on the map).

Does this mean that each time we call AIKnownUnitCount, where seeing everything is off, the AI will only return the count of what it sees?

So, say, the previous second, the ai saw 6 lings. And it retreats. Now, while in the base and no scouts, the AIKnownUnitCount will return 0? This is correct right?


Actually no, it will return 6; it still knows the opponent has 6 lings. It ends up tracking those lings somehow; I'm not sure if it tracks each one individually or just knows there are at least 6 lings (ie. if it kills 6 lings later it will no longer know how many the opponent has). Some testing would be needed to determine this.

hd

Well, I'm sure each unit has a unique identifier in the unit variable has. Thus, it would go through each one checking to see if it's seen it.

How it knows when it's killed one I'm not sure. In 1 vs 1, that would be simple... since a unit will most likely only die when it's within the AI's field of vision. Against multiple opponents though, I'm not sure how it keeps track of what has died, if it even keeps track of it at all.