[WIP] AI Library.. Read more..

SineCosine

I'm still looking for my Tangent
Reaction score
77
Well, I'm creating this Hero Survival map. (Was a Hero Defense, but is now a Hero Survival..)

And each level is supposed to be a different set of monsters, each with special abilities and a different strategy is required to defeat them.

The thing is, with special abilities, I'm going to need to develop an AI..
One that can tell the creeps when to cast a spell, how to group with each other, who is the leader, when to run, etc.

But..
There isn't any system like that.. (That I could find, maybe someone can find a system like that and save me from my misery..)
So, I decided to code a system like that myself..

Unfortunately, I have never done ANYTHING of the sort before..
And so, my codes are messy and I doubt everything is running smoothly.

This is all I have managed to do so far:
01) Create an AI for units to cast a Single Target Spell
02) Create an AI for units to group around a leader
03) When the 'leader' casts a Single Target Spell or attacks an enemy, all the other 'Followers' will attack the enemy

I'll upload a map with the System that's just started and I need comments =x
Remember, don't tell me, "It's horrible".
I'd rather read something specific like, "You can do it this way..."

Also, I can't figure out how to get the AI-Groups to do this..
01) Must attack lowest HP unit in area
02) But helping Leader is priority
03) If leader dies LOOK for another group to join

Things to note:
01) If any of the 2 felhounds survive.. Try tanking all the enemy creeps' damage, you'll see that as long as the felhound (Leader) attacks the hero the Orc Tamers (Follower) will ignore your felhound and attack you, instead. And that is supposed to happen.
02) There is a bug that, sometimes, 2 felhounds will 'Pounce' on the same unit. And that is not supposed to happen.

What's available:
01) [lJASS]function SpellInit takes integer UnitID, string STRING, real RANGE, integer BuffID, boolean CastOnEnemy, string BaseID, real TIME, real CoolDown returns nothing[/lJASS]
02)[lJASS] function AttachLeader takes unit Leader, integer NumberOfFollower, boolean SpecificUnitType, integer FollowerID, real RANGE returns nothing[/lJASS]

So..
Yea, looking for comments on how to improve what I already have and a bit of help on a few of the AI-Group functions.. =x

Full-Code (So Far..)
JASS:
library AIL
    
    globals
        private constant integer Chk = 'A010'
    endglobals
    
    ////This Is Where My GroupAI is..
    private struct GroupAI
        private static GroupAI data = 0
        static GroupAI ATK = 0
        
        unit Leader
        integer NumberOfFollower
        boolean SpecificUnitType
        integer FollowerID
        
        integer ticks
        
        unit array Followers[1000]
        real LeaderHP
        real array FollowerHP[1000]
        integer FollowerAmount
        
        boolean LeaderAtk
        integer MTick
        
        static method AttackForLeader takes unit Targ returns nothing
            local GroupAI g = GroupAI.ATK
            local integer i = 1
            
            set g.LeaderAtk = true
            
            loop
            exitwhen i > g.FollowerAmount
                call IssueTargetOrder(g.Followers<i>, &quot;attack&quot;, Targ)
            set i = i + 1
            endloop
            
        endmethod
        
        private method periodic takes nothing returns nothing
            local GroupAI g = this
            local integer i = 1
            local integer OtherI = 1
            
            if g.ticks &lt; g.MTick then
            
                loop
                exitwhen i &gt; g.FollowerAmount
                    
                    if GetWidgetLife(g.Followers<i>) &lt;= 1 then
                        
                        set g.Followers<i> = null
                        
                        loop
                        exitwhen OtherI &gt; g.FollowerAmount
                        
                            set g.Followers<i> = g.Followers[i+1]
                        
                        set OtherI = OtherI + 1
                        endloop
                        
                        set g.FollowerAmount = g.FollowerAmount - 1
                        
                    endif
                    
                set i = i + 1
                endloop
            
            set g.ticks = g.ticks + 1
            else
                
                if g.ticks == (32*10) + 1 then
                    set g.LeaderAtk = false
                    set g.MTick = 33
                endif
                
                loop
                exitwhen i &gt; g.FollowerAmount
                
                    if g.LeaderAtk == false then
                        call IssueTargetOrder(g.Followers<i>, &quot;move&quot;, g.Leader)
                    endif
                    
                set i = i + 1
                endloop
                
            set g.ticks = 1
            endif
            
            if g.LeaderAtk == true then
                set g.MTick = (32*10) + 1
            endif
            
            if GetWidgetLife(g.Leader) &lt;= 1 then
                call g.stopPeriodic()
                call g.destroy()
            endif
        endmethod
        
        implement T32x
        
        static method GetFollower takes nothing returns boolean
            local GroupAI g = GroupAI.data
            local unit u = GetFilterUnit()
            local boolean b = false
            local integer i = 1
            
            loop
            exitwhen i &gt; g.FollowerAmount
                if IsUnit(u, g.Followers<i>) == true then
                    set b = true
                endif
            set i = i + 1
            endloop
            
            if  b == false and g.FollowerAmount != g.NumberOfFollower then 
            
                if g.SpecificUnitType == true then
            
                    if GetUnitTypeId(u) == g.FollowerID and GetWidgetLife(u) &gt; 0 and IsUnitEnemy(u, GetOwningPlayer(g.Leader)) == false then
                        set g.FollowerAmount = g.FollowerAmount + 1
                        set g.FollowerHP[g.FollowerAmount] = GetWidgetLife(u)
                        set g.Followers[g.FollowerAmount] = u
                    endif
            
                else
            
                    if GetWidgetLife(u) &gt; 0 and IsUnitEnemy(u, GetOwningPlayer(g.Leader)) == false then
                        set g.FollowerAmount = g.FollowerAmount + 1
                        set g.FollowerHP[g.FollowerAmount] = GetWidgetLife(u)
                        set g.Followers[g.FollowerAmount] = u
                    endif
            
                endif
            
            endif
            
            set u = null
            return false
            
        endmethod
        
        static method Start takes unit Leader, integer NumberOfFollower, boolean SpecificUnitType, integer FollowerID, real RANGE returns nothing
            local GroupAI g = GroupAI.create()
            local integer i = 0
            
            set g.Leader = Leader
            set g.NumberOfFollower = NumberOfFollower
            set g.SpecificUnitType = SpecificUnitType
            set g.FollowerID = FollowerID
            
            set g.ticks = 1
            
            set g.LeaderHP = GetWidgetLife(g.Leader)
            set g.FollowerAmount = 0
            set g.LeaderAtk = false
            set g.MTick = 33
            
            set GroupAI.data = g
            call GroupEnumUnitsInRange(GROUP, GetUnitX(g.Leader), GetUnitY(g.Leader), RANGE, function GroupAI.GetFollower)
            
            if g.FollowerAmount == 0 then
                call g.destroy()
            else
                call g.startPeriodic()
                call SetUnitUserData(g.Leader, g)
            endif
            
        endmethod
        
        method onDestroy takes nothing returns nothing
            local integer i = 1
            
            set .Leader = null
            
            loop
            exitwhen i &gt; .FollowerAmount
            
                set .Followers<i> = null
            
            set i = i + 1
            endloop
        endmethod
        
    endstruct
    
    function AttachLeader takes unit Leader, integer NumberOfFollower, boolean SpecificUnitType, integer FollowerID, real RANGE returns nothing
        
        call GroupAI.Start(Leader, NumberOfFollower, SpecificUnitType, FollowerID, RANGE)
        
    endfunction
    
    //This Detects When A Leader Is Attacking..
    private function CLAAct takes nothing returns nothing
        local GroupAI g = GetUnitUserData(GetAttacker())
        local integer i = 1
            
        set g.LeaderAtk = true
            
        loop
        exitwhen i &gt; g.FollowerAmount
            call IssueTargetOrder(g.Followers<i>, &quot;attack&quot;, GetTriggerUnit())
            call IssueTargetOrder(g.Leader, &quot;attack&quot;, GetTriggerUnit())
        set i = i + 1
        endloop
    endfunction
    
    private function CLACond takes nothing returns boolean
        local GroupAI g = GetUnitUserData(GetAttacker())
        
        return g.Leader != null
    
    endfunction
    
    private function CheckLeaderAtk takes nothing returns nothing
        local trigger t = CreateTrigger()
        local integer i = 0
        
        loop
        exitwhen i == 12
            call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_ATTACKED, null)
        set i = i + 1
        endloop
        
        call TriggerAddCondition(t, Condition( function CLACond ))
        call TriggerAddAction(t, function CLAAct)
        
    endfunction
    
    //This Is Where My SpellAI is..
    
    private struct PC
        unit caster
        integer ticks
        integer MTick
        
        private method periodic takes nothing returns nothing
            local PC pc = this
            
            if pc.ticks &lt; pc.MTick then
                set pc.ticks = pc.ticks + 1
            else
                call pc.stopPeriodic()
                call pc.destroy()
            endif
        endmethod
        
        implement T32x
        
        static method PCAct takes unit u, real time returns nothing
            local PC pc = PC.allocate()
            
            set pc.caster = u
            set pc.ticks = 1
            set pc.MTick = R2I(time / 0.03125)
            
            call UnitRemoveAbility(pc.caster, Chk)
            
            call pc.startPeriodic()
        endmethod
        
        method onDestroy takes nothing returns nothing
            call UnitAddAbility(.caster, Chk)
            set .caster = null
        endmethod
        
    endstruct
    
    private struct UnitSpell
        private static UnitSpell CastData = 0
        private static UnitSpell TargData = 0
        
        integer UnitID
        real RANGE
        integer BuffID
        boolean CastOnEnemy
        string BaseID
        real CoolDown
        
        group UnitsCasting = CreateGroup()
        group Targets = CreateGroup()
        
        integer Ticks
        integer MAXTicks
        
        unit CurrentUnit
        
        static method TARGGET takes nothing returns boolean
            local UnitSpell u = UnitSpell.TargData
            local GroupAI g = GetUnitUserData(u.CurrentUnit)
            local unit Targ = GetFilterUnit()
            
            if u.BuffID != 0 then
                if IsUnitEnemy(Targ, GetOwningPlayer(u.CurrentUnit)) == u.CastOnEnemy and GetUnitAbilityLevel(Targ, u.BuffID) == 0 and IsUnitInGroup(Targ, u.Targets) == false and GetWidgetLife(Targ) &gt; 0 then
                    call GroupAddUnit(u.Targets, Targ)
                    call IssueTargetOrder(u.CurrentUnit, u.BaseID, Targ)
                    call PC.PCAct(u.CurrentUnit, u.CoolDown)
                    
                    if IsUnit(u.CurrentUnit, g.Leader) == true then
                        set GroupAI.ATK = g
                        call GroupAI.AttackForLeader(Targ)
                    endif
                    
                endif
            else
                if IsUnitEnemy(Targ, GetOwningPlayer(u.CurrentUnit)) == u.CastOnEnemy and IsUnitInGroup(Targ, u.Targets) == false and GetWidgetLife(Targ) &gt; 0 then
                    call GroupAddUnit(u.Targets, Targ)
                    call IssueTargetOrder(u.CurrentUnit, u.BaseID, Targ)
                    call PC.PCAct(u.CurrentUnit, u.CoolDown)
                    
                    if IsUnit(u.CurrentUnit, g.Leader) == true then
                        set GroupAI.ATK = g
                        call GroupAI.AttackForLeader(Targ)
                    endif
                    
                endif
                
            endif
            
            set Targ = null
            return false
        endmethod
        
        static method CASTGET takes nothing returns boolean
            local UnitSpell u = UnitSpell.CastData
            local unit Cast = GetFilterUnit()
            
            if GetUnitTypeId(Cast) == u.UnitID and GetUnitAbilityLevel(Cast, Chk) &gt; 0 and IsUnitInGroup(Cast, u.UnitsCasting) == false and GetWidgetLife(Cast) &gt; 0 then
                call GroupAddUnit(u.UnitsCasting, Cast)
                set u.CurrentUnit = Cast
                set UnitSpell.TargData = u
                call GroupEnumUnitsInRange(GROUP2, GetUnitX(Cast), GetUnitY(Cast), u.RANGE, function UnitSpell.TARGGET)
            endif
            
            set Cast = null
            return false
        endmethod
        
        private method periodic takes nothing returns nothing
            local UnitSpell u = this
            
            if u.Ticks == 1 then
                
                call GroupClear(u.UnitsCasting)
                call GroupClear(u.Targets)
                set u.CurrentUnit = null
                
            set u.Ticks = u.Ticks + 1
            elseif u.Ticks &lt; u.MAXTicks then
            
                set u.Ticks = u.Ticks + 1
                
            else
                
                set UnitSpell.CastData = u
                call GroupEnumUnitsInRange(GROUP, 0, 0, 99999999.0, function UnitSpell.CASTGET)
                
            set u.Ticks = 1
            endif
        endmethod
        
        implement T32x
        
        static method Start takes integer UnitID, real RANGE, integer BuffID, boolean CastOnEnemy, string BaseID, real TIME, real CoolDown returns nothing
            local UnitSpell u = UnitSpell.allocate()
            
            set u.UnitID = UnitID
            set u.RANGE = RANGE
            set u.BuffID = BuffID
            set u.CastOnEnemy = CastOnEnemy
            set u.BaseID = BaseID
            set u.MAXTicks = R2I(TIME / 0.03125) + 1
            set u.Ticks = 1
            set u.CoolDown = CoolDown
            
            call u.startPeriodic()
            
        endmethod
    
    endstruct

    function SpellInit takes integer UnitID, string STRING, real RANGE, integer BuffID, boolean CastOnEnemy, string BaseID, real TIME, real CoolDown returns nothing
        
        if STRING == &quot;UNIT_SPELL_AI&quot; then
            call UnitSpell.Start(UnitID, RANGE, BuffID, CastOnEnemy, BaseID, TIME, CoolDown)
        endif
        
    endfunction
    
    
    
endlibrary
</i></i></i></i></i></i></i></i>
 

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
Reaction score
216
Hmm, a good AI is not easily achieved. You struct is basically failed. You should use linked list instead of array. Array in struct will cripple it's availability, while linked list does not do so and provides dynamic size of variables, this can increase the usability without hesitating the problem of wasting index.

You can refer to my AI in Panda Wars Primal@Members' Project section. (But my AI is not completely coded but it is working well and I don't know where is yami-joey currently :p )
 

SineCosine

I'm still looking for my Tangent
Reaction score
77
@Sharp
Thanks for the recommendation =)
I'll be sure to ask you if I ever have questions

@kingking
I expected someone to say something like that =x
I have a few questions..

What's a linked list?
How does an array in a struct cripple its availability? o.0
(Does it have something to do with the 8190-ish instance limit?)

And I'll make sure to take a look at the AI in your map =)
(I have to go out now, I over-slept, f**k)
 

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
Reaction score
216
(Does it have something to do with the 8190-ish instance limit?)
Yes. :) Linked list.. hmm it is had to understand even you read about it. Anyway, Click Me!

Feel free to ask if you have any question more. :)
 

SineCosine

I'm still looking for my Tangent
Reaction score
77
I read it.
Or at least I tried.. =/

I got a headache >.<
I'm no programmer! xD

Erm..
Is there, like, an easier way to put it?
Like, in Jass.. *Feels guilty*
 

SineCosine

I'm still looking for my Tangent
Reaction score
77
Hmm..
This thing uses a camp creep system ._.
The monsters in my game have no camping place,

They just sort of, like, go all-out and wanna' kill the players' heroes xD
And, it doesn't really specify what we need to get the system running..


It recommends IDDS, but never says it is a necessity =/
And the example DamageDetect trigger has no 'Event' to trigger it..

Trigger:
  • DamageDetect
    • Events
    • Conditions
    • Actions
      • -------- A simple damage detection function used to add threat in amount of damage dealt --------
      • Custom script: call ZTS_ModifyThreat(GetEventDamageSource(), GetTriggerUnit(), GetEventDamage(), true)


So..
How does that run without something to 'trigger' it? o.0


[EDIT]
If I set the:

JASS:
    private constant real OrderReturnRange = 4000 //The range the unit&#039;s target can be away from the original camping position, before being ordered to return.
    private constant real ReturnRange = 1500 //The range the unit can move away from the original camping position, before being ordered to return.


Values above to something like, 99999999 ..
Will it be enough to ensure that they never 'return' to a camp spot? o.0
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
I'm currently writing an advanced AI for my Forest CTF map, and I've found that one large periodic loop is not a very easy way to program the AI.

Instead, I've taken a different approach, and that is basing all AI instructions off a reactionary, event-based system. Any time a unit performs an action (such as an attack, a unit takes damage, a unit dies, etc), the AI's instructions are changed/given. Depending on the nature of the instruction, I may then end up using a periodic loop, but I try to avoid it if possible.

This makes it very easy to evaluate things, as I simply set up a bunch of functions that order the unit to perform a different instruction. For example, if a unit is outnumbered, I can order the units to flee to a safer position, or die protecting the flag carrier.

An AI is a very advanced system, so there's only so much that can be covered in a simple post like this. Basically, I am just recommending you split up the system into many, many event-driven functions. You'll save yourself a lot of time. :)
 

SineCosine

I'm still looking for my Tangent
Reaction score
77
Thanks for the tip.

I'll make sure I do that, but for now, I think using Zwiebelchen's Threat System will suffice.
I will code other bits if I find his system insufficient (Looks pretty damn good at the moment, just a few little annoying bugs here and there =/)


I still want to know how to use a linked list =(
 

Sharp

New Member
Reaction score
5
Sorry if this is a bit off-topic, but maybe there's other people wanting to learn Artificial Intelligence Coding.

Well as a exercise you could try to code an AI for a basic combate situation, i would suggest one like this:

Dryad(AI Coded) vs. Footman(default game AI - attack enemies and thats only)

The dryad has 500 range and slow poison, so she's faster than the Footman, so an ideal combate strategy for AI would be hit 'n run, to avoid taking damage. Try to do this, see what difficulties you have...should be simple.
 

SineCosine

I'm still looking for my Tangent
Reaction score
77
Start out small, grow up big.
Me likes that idea, lol

I'll try it xD
I don't know how to contact BMP ._.
 

tooltiperror

Super Moderator
Reaction score
231
I'm actually going to work on the Dryad thing. It sounds like a fun challenge, and I already have the idea of formulating the triggers and moving from location to location in my head....

This will be fun :D
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • The Helper The Helper:
    The bots will show up as users online in the forum software but they do not show up in my stats tracking. I am sure there are bots in the stats but the way alot of the bots treat the site do not show up on the stats
  • Varine Varine:
    I want to build a filtration system for my 3d printer, and that shit is so much more complicated than I thought it would be
  • Varine Varine:
    Apparently ABS emits styrene particulates which can be like .2 micrometers, which idk if the VOC detectors I have can even catch that
  • Varine Varine:
    Anyway I need to get some of those sensors and two air pressure sensors installed before an after the filters, which I need to figure out how to calculate the necessary pressure for and I have yet to find anything that tells me how to actually do that, just the cfm ratings
  • Varine Varine:
    And then I have to set up an arduino board to read those sensors, which I also don't know very much about but I have a whole bunch of crash course things for that
  • Varine Varine:
    These sensors are also a lot more than I thought they would be. Like 5 to 10 each, idk why but I assumed they would be like 2 dollars
  • Varine Varine:
    Another issue I'm learning is that a lot of the air quality sensors don't work at very high ambient temperatures. I'm planning on heating this enclosure to like 60C or so, and that's the upper limit of their functionality
  • Varine Varine:
    Although I don't know if I need to actually actively heat it or just let the plate and hotend bring the ambient temp to whatever it will, but even then I need to figure out an exfiltration for hot air. I think I kind of know what to do but it's still fucking confusing
  • The Helper The Helper:
    Maybe you could find some of that information from AC tech - like how they detect freon and such
  • Varine Varine:
    That's mostly what I've been looking at
  • Varine Varine:
    I don't think I'm dealing with quite the same pressures though, at the very least its a significantly smaller system. For the time being I'm just going to put together a quick scrubby box though and hope it works good enough to not make my house toxic
  • Varine Varine:
    I mean I don't use this enough to pose any significant danger I don't think, but I would still rather not be throwing styrene all over the air
  • The Helper The Helper:
    New dessert added to recipes Southern Pecan Praline Cake https://www.thehelper.net/threads/recipe-southern-pecan-praline-cake.193555/
  • The Helper The Helper:
    Another bot invasion 493 members online most of them bots that do not show up on stats
  • Varine Varine:
    I'm looking at a solid 378 guests, but 3 members. Of which two are me and VSNES. The third is unlisted, which makes me think its a ghost.
    +1
  • The Helper The Helper:
    Some members choose invisibility mode
    +1
  • The Helper The Helper:
    I bitch about Xenforo sometimes but it really is full featured you just have to really know what you are doing to get the most out of it.
  • The Helper The Helper:
    It is just not easy to fix styles and customize but it definitely can be done
  • The Helper The Helper:
    I do know this - xenforo dropped the ball by not keeping the vbulletin reputation comments as a feature. The loss of the Reputation comments data when we switched to Xenforo really was the death knell for the site when it came to all the users that left. I know I missed it so much and I got way less interested in the site when that feature was gone and I run the site.
  • Blackveiled Blackveiled:
    People love rep, lol
    +1
  • The Helper The Helper:
    The recipe today is Sloppy Joe Casserole - one of my faves LOL https://www.thehelper.net/threads/sloppy-joe-casserole-with-manwich.193585/
  • The Helper The Helper:
    Decided to put up a healthier type recipe to mix it up - Honey Garlic Shrimp Stir-Fry https://www.thehelper.net/threads/recipe-honey-garlic-shrimp-stir-fry.193595/
  • The Helper The Helper:
    Here is another comfort food favorite - Million Dollar Casserole - https://www.thehelper.net/threads/recipe-million-dollar-casserole.193614/

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top