1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Join other hivers in a friendly concept-art contest. The contestants have to create a genie coming out of its container. We wish you the best of luck!
    Dismiss Notice
  3. The Melee Mapping Contest #4: 2v2 - Results are out! Step by to congratulate the winners!
    Dismiss Notice
  4. We're hosting the 15th Mini-Mapping Contest with YouTuber Abelhawk! The contestants are to create a custom map that uses the hidden content within Warcraft 3 or is inspired by any of the many secrets within the game.
    Dismiss Notice
  5. The 20th iteration of the Terraining Contest is upon us! Join and create exquisite Water Structures for it.
    Dismiss Notice
  6. Check out the Staff job openings thread.
    Dismiss Notice

Hero AI v4.3.4

Submitted by watermelon_1234
This bundle is marked as approved. It works and satisfies the submission rules.
Description
This system provides a simple computer AI for arena-type maps. Basic actions for the AI include learning skills, picking up items, buying items, and running away. The default AI can be customized further by the user for specific heroes.

There are also four optional add-ons intended to make coding the AI easier.
One is the priority library that allows the AI to choose targets based on more factors.
The second is the threat library, which allows the AI to perceive threat. This will factor in the AI's decision to run away.
The third is the event response library, intended to allow the AI to react to specific events.
The last is an independent library from Hero AI and allows the AI to have a handicap or advantage based on its difficulty.


Requires JNGP and the most recent JassHelper.

The code is quite long and better seen in the test-map with the example AIs.

Changelog
v4.3.4
  • Removed FireCode which has been graveyarded. Magtheridon96 is still credited for the logic of evaluating a boolexpr.
  • Removed GetHeroAIIndex, which should have been pretty useless
  • Clarified parts of the documentation
v4.3.3
  • Added a curItem method operator to retrieve the item the hero is trying to buy.
v4.3.2
  • The methods canBuyItem and buyItem are now accessible.
v4.3.1
  • Now properly accounts for neutral units with a NEUTRAL_PLAYER constant.
  • Optionally supports IsUnitChanneling
v4.3.0
  • GroupUtils is now optional.
  • weightPriority is now a method instead of being static.
  • Added a weightThreat method to HeroAIThreat.
  • Removed some dead code.
v4.2.0
  • Custom AI are now able to define a shop unit for the AI to go to by setting .shop or calling .canShop().
  • The AI state variable is now readonly.
  • SELL_ITEM_RANGE is now public.
v4.1.0
  • Learnset can now account for morphing heroes.
  • Learnset now supports multiple skills per level.
v4.0.0
  • Separated learnset and itemset-related configuration from HeroAI into their own triggers for organization. They are still part of the core system.
  • Added AIPlayerDifficultySettings. This library is independent of HeroAI and allows the user to handicap or give an advantages to computer players based on their difficulty setting.
  • The system now requires GetClosestWidget.
  • Added a new function, RegisterHeroAISkill, to allow the user to define the learnset for a hero in its AI struct
  • Changes IsSafeUnit to take the hero's owner instead.
  • SetupItemTypes was renamed to SetupDefaultItemBuild.
  • Buying items now supports lumber.
  • Itemset syntax has been changed. The system now requires registration of items to find out its gold and lumber cost. Items can also consider shop ids so that the AI will run to them.
  • Itemsets function a bit differently. They can hold up to MAX_ITEMSET_SIZE items and will be used to tell how the AI should upgrade its hero's items.
  • Heroes will refund items when they try to improve their item build.
  • HeroAIStruct now has a utility method operator to refer to the amount of lumber the owner has. Both this and gold can now be directly set to affect the owner's resouces.
  • runAway has been deprecated in favor of a state member. This will be used to tell what kind of condition the hero is in.
v3.1.0
  • Changed RegisterHeroAI so that it won't be inlined.
  • Changed behavior of the AI to attack enemies or assist allies if the hero is at the safe spot and safeActions is not defined.
  • Now allows the user to customize the threat threshold factor for running away. THRESHOLD_RUN_FACTOR is used for the default and is based on percent life.
  • Minor code improvements
  • [Test-Map] Updated FireCode
v3.0.0
  • Allowed threat and priority to support specific unit configuration
  • AI can now consider threat to run away
  • Includes a THRESHOLD_RUN_FACTOR in HeroAIThreat so that heroes won't easily run away
  • Allowed AI to find a place to run to instead of always running to static coordinates
  • Added an on acquire event to HeroAIEventResponse
  • Fixed USE_ON_ATTACKED
  • Renamed moveAround to move
  • Renamed FOUNTAIN_RNG to SAFETY_RANGE
  • Renamed needHeal to runAway
  • Improved Learnset to fully reflect that only one instance is created
  • General code improvements
v2.0.0
  • Switched to a module interface
  • AI can no longer be destroyed
  • Corrected priority library
  • Added an optional add-on, HeroAIEventResponses
  • Uses FireCode instead of the function interface
  • isChanneling now has a text macro to provide easier configuration
  • Restructured some code
  • Renamed MAX_ITEM_INVENTORY to MAX_INVENTORY_SIZE
v1.0.0
Released


Please give credit if you use this system in your map.

Credits:
  • Magtheridon96 for IsUnitChanneling and RegisterPlayerUnitEvent. His feedback was also valuable in improving the system.
  • Vexorian for TimerUtils
  • Rising_Dusk for GroupUtils
  • Bribe for NewTable
  • Anitarf for PruneGroup, FitnessFunc, and SpellEvent (used only in test-map)
  • Spinnaker for Get Closest Widget
  • jim7777 for good feedback and suggestions
  • DarnYak for EotA which inspired the Assassin AI in the test-map

Any feedback or suggestions to improve this system would be greatly appreciated.

Keywords:
computer, AI, artificial, intelligence, arena, hero
Contents

HeroAI (Map)

Reviews
Moderator
16th Jan 2012 Bribe: This is a great template/building block and I'm thinking to recommend it. GroupFunctionality is a mostly terrible resource. Half of it was already done in Blizzard.j, the GroupRemoveUnitTimed thing is pretty good though. For...
  1. 16th Jan 2012
    Bribe: This is a great template/building block and I'm thinking to recommend it.

    GroupFunctionality is a mostly terrible resource. Half of it was already done in Blizzard.j, the GroupRemoveUnitTimed thing is pretty good though. For the other stuff you typically want to use FirstOfGroup loops instead.

    Your GetAngleDifference function gets the smallest angle between two angles? I wonder if it is more efficient than this:

    Code (vJASS):
            set ang = RAbsBJ(ang - ang2)
            if ang > 180 then
                set ang = R2I(ang) / 360 * 360 + 360 - ang
            endif


    I don't see a problem using a local text tag instead of "bj_lastCreatedTextTag", because, keep in mind that JASS is an interpreted language, longer names take longer to process, and that the optimizer will not compress bj_ global names.

    You end up with a lot of duplicates of code by using Anitarf's functions interfaces. While I am not aware of any alternatives to fitness functions, http://www.hiveworkshop.com/forums/jass-resources-412/system-spell-struct-204774/ by Nestharus poops all over SpellEvent by Anitarf.

    I'm considering approving this and since I could not find glitches I will approve it with an initial rating of 3/5.
     
  2. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,008
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    Great system!

    Code (vJASS):
        private interface AIInterface
            method loopActions      takes nothing returns nothing defaults nothing  // Determines the periodic behavior of the hero. Defined by default.
            method assistAlly       takes nothing returns boolean defaults false      // Actions to take when supporting allies. Needs to return true if a supportive action was taken. Has higher priority than attacking enemies
            method assaultEnemy     takes nothing returns nothing defaults nothing  // Actions to take when attacking enemies. Defined by default.
            method runActions       takes nothing returns boolean defaults false    // Actions to take when running to (RUN_X, RUN_Y). Needs to return true if an action was taken.
            method safeActions      takes nothing returns nothing defaults nothing  // Actions to take while at (RUN_X, RUN_Y)
        endinterface


    struct HeroAI extends AIInterface


    Code (vJASS):
            group units
            group allies
            group enemies


    ZOMG

    You don't have to use an interface, module interfaces are cooler :p (Cleaner, faster, and they don't create shitcode for teh lulz)

    Since you don't need the interface, you can make that struct extend an array.

    Also, instead of a group, you can use a linked list of units (LinkedListModule by Dirac and UnitIndexer so you don't only store 32-bytes of data instead of storing ~3072-bytes.) (By this, I mean you should store the Unit Ids.)

    Or, LinkedListModule and UnitIndexer because dynamic groups are too old school now :p

    Code (vJASS):
            method operator isChanneling takes nothing returns boolean
                local integer o = GetUnitCurrentOrder(.hero)
                return o == 852664 or /* Healing spray
                    */
    o == 852183 or /* Starfall
                    */
    o == 852593 or /* Stampede
                    */
    o == 852488 or /* Flamestrike
                    */
    o == 852089 or /* Blizzard
                    */
    o == 852238 // Rain of Fire
            endmethod


    This method should be in a textmacro somewhere at the top of the script so that it's easier for the user to configure it.

    private function interface RegisterHeroAIFunc takes unit h returns nothing  


    This duplicates a lot of code :/
    You could make the user pass code arguments to the functions instead and if you need to store them, convert them to boolexprs with the function
    Filter(code)

    It's a lot cooler ^_^



    There are many more things that could be improved, but I'm really tired at the moment.
    I want to give this a 5/5, but I can't do that yet ;)
     
  3. baassee

    baassee

    Joined:
    Nov 14, 2008
    Messages:
    3,220
    Resources:
    17
    Spells:
    14
    Tutorials:
    3
    Resources:
    17
    Great system! Really just wanna make a square sized map with thousands of spells and add this to it.
     
  4. watermelon_1234

    watermelon_1234

    Joined:
    Nov 18, 2007
    Messages:
    1,066
    Resources:
    10
    Spells:
    9
    JASS:
    1
    Resources:
    10
    Thanks for the feedback! ^_^

    @Magtheridon96
    If I used a module interface instead, would the user still be able to call on the AI's actions from a hero without knowing what kind of AI the hero has?

    I'll look into using linked lists but it looks like it might complicate things on the user's end.

    For the function interface: If I switched to using boolexprs, I would need a trigger to evaluate it and a global variable to store the unit, right?
     
    Last edited: Dec 25, 2011
  5. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,008
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    For the unit, yes.
    For the trigger, nah, you can add "FireCode" as a requirement (Jass section)
    But, I wouldn't recommend doing that just yet cause I just did a ninja-update and found a major bug in it :eek:
    I'll tell you when it's safe to use it :p

    edit
    FireCode has been updated.

    Now that I think about it, a LinkedList wouldn't make much of a difference.
    It would just add tons of shitcode to your library.
    Use those groups, it's ok.
     
    Last edited: Dec 25, 2011
  6. Bugers

    Bugers

    Joined:
    Dec 28, 2010
    Messages:
    20
    Resources:
    1
    Maps:
    1
    Resources:
    1
  7. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,008
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    This AI is more advanced.
    You can use that other AI for neutral creeps if you want because I think it gets the job done.
    Implement both of them :>
     
  8. RakEnRoL

    RakEnRoL

    Joined:
    Sep 2, 2011
    Messages:
    316
    Resources:
    0
    Resources:
    0
    I like this. However, I lack jass knowledge, how can I automatically implement this to my map?
     
  9. watermelon_1234

    watermelon_1234

    Joined:
    Nov 18, 2007
    Messages:
    1,066
    Resources:
    10
    Spells:
    9
    JASS:
    1
    Resources:
    10
    First, you need to make sure that you have implemented all the required libraries. So basically all the triggers only under the "Needed Libraries" needs to be copied into your map.
    Then copy the trigger named HeroAI into your map.
    Now, to run the AI for a hero, you need some custom script.
    • Custom script: call RunHeroAI(YourUnit)


    Right now, I'm still updating the system so I'd advise you to wait before implementing this.
     
  10. RakEnRoL

    RakEnRoL

    Joined:
    Sep 2, 2011
    Messages:
    316
    Resources:
    0
    Resources:
    0
    Alrighty. Great work on this. I'm really planning on using this after it is finished. Do also make a documentation for noobs (like me) on how to uhm use it properly. Since, I really lack in jass in anyway.
     
  11. watermelon_1234

    watermelon_1234

    Joined:
    Nov 18, 2007
    Messages:
    1,066
    Resources:
    10
    Spells:
    9
    JASS:
    1
    Resources:
    10
    Thanks! ^_^ I'll make sure to do that.

    EDIT: I just found an embarrassing bug. I'm going to update this tomorrow.
     
    Last edited: Dec 26, 2011
  12. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    After you fix the stuff up like the interface, I'll do a full review of this line by line for you : ). This is a library that I was thinking about making for a while and one that appears to be quite good ;p, so it's one that I look forward to reviewing ; ).
     
  13. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,008
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    Code (vJASS):
        struct Learnset    
            private TableArray info
           
            method operator [] takes integer key returns Table
                return info[key - 1]
            endmethod
           
            static method create takes nothing returns thistype
                local thistype this = thistype.allocate()
                set .info = TableArray[MAX_SKILL_LVL]
                return this
            endmethod
        endstruct


    Well, you aren't destroying any of the instances, so the optimal solution here would be this:

    Code (vJASS):

        struct LearnSet extends array
            // This integer does all the allocation for you ;)
            private static integer stack = 1
            // 3-D Array, FUCK YEAH!
            private static TableArray array info
           
            method operator [] takes integer key returns Table
                return info[this][key - 1]
            endmethod
           
            static method create takes nothing returns thistype
                local thistype this = stack
                set stack = stack + 1
                set info[this] = TableArray[MAX_SKILL_LVL]
                return this
            endmethod
        endstruct


    This way, you're producing much less compiled code, making the script more light-weight :)
    I broke compatibility though :p
    I want it to follow Jass convention ;)

    And take your time on fixing those Interfaces (I know how hard it is to manage a module interface) :p

    edit
    And when you update the code, could you paste it outside of hidden tags?
    The font size is horrible :(

    edit
    Another optimization:

    Code (vJASS):
        struct ItemSet extends array
            private static integer stack = 0
            private static integer array items
            private static integer array costs
            private static integer array count = 0
           
            method itemId takes integer index returns integer
                return items[this * MAX_ITEM_INVENTORY + index]
            endmethod
           
            method cost takes integer index returns integer
                return costs[this * MAX_ITEM_INVENTORY + index]
            endmethod

            method operator total takes nothing returns integer
                return count[this]
            endmethod
           
            static method create takes nothing returns thistype
                set stack = stack + 1
                return stack
            endmethod
           
            method addItemId takes integer id, integer c returns nothing
                debug if count[this] < 6 then
                    set items[this * MAX_ITEM_INVENTORY + count[this]] = id
                    set costs[this * MAX_ITEM_INVENTORY + count[this]] = c
                    set count[this] = count[this] + 1
                debug else
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[ItemSet]Error: Max Items Capacity Reached. Aborting.")
                debug endif
            endmethod
        endstruct


    That should do it.
    It looks better, but I broke compatibility :p
    It's ok since this resource is still new ;)
     
    Last edited: Dec 26, 2011
  14. watermelon_1234

    watermelon_1234

    Joined:
    Nov 18, 2007
    Messages:
    1,066
    Resources:
    10
    Spells:
    9
    JASS:
    1
    Resources:
    10
    I think I'm going to have to raise the version number up to 2.0.0 already. XD

    Thanks again for the feedback! I really appreciate it. ^_^
     
  15. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,008
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    Nah, you can keep it as 1.0.0 since it's still new :p
    My ModeManager system is already up to 6.0.0.0 =p
    I broke backwards compatibility with long intervals of time xD

    I usually wait 3 days after the first upload before I go to 2.0.0.0 when breaking backwards compatibility (I don't want to fall in the same trap I did while developing ModeManager)
     
  16. Lautanen

    Lautanen

    Joined:
    Dec 8, 2009
    Messages:
    94
    Resources:
    1
    Spells:
    1
    Resources:
    1
    Is my jasshelper out-dated?
    Screenshot
    [​IMG]


    Edit: If it is, could someone give me a link for a newer one? I really need something like this :(
     
  17. watermelon_1234

    watermelon_1234

    Joined:
    Nov 18, 2007
    Messages:
    1,066
    Resources:
    10
    Spells:
    9
    JASS:
    1
    Resources:
    10
    The map was compiled with 0.A.2.B Jasshelper.

    Don't use this system yet, I'm still in the process of updating it. >o<
     
  18. Lautanen

    Lautanen

    Joined:
    Dec 8, 2009
    Messages:
    94
    Resources:
    1
    Spells:
    1
    Resources:
    1
    I redownloaded the map, after that I were able to test it. Its really great system. Thank you for the link.
     
  19. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    public constant real        DEFAULT_PERIOD      = 1.7   // The period in which the hero AI will do actions. A very low period can cause strain.


    While this makes sense in general coding, it makes more sense to use events for wc3.


    public constant real        MOVE_DIST           = 1000. // The random amount of distance the hero will move

    This should probably be different depending on the unit

    public constant integer     MAX_INVENTORY_SIZE  = 6     // The number of items the hero can hold

    This is dependent upon unit and possible custom inventories

    public constant real        FOUNTAIN_RNG          = 500.  // The range the hero should be in of the fountain.

    Should probably be changed to aura and it depends on the aura >.>. This value shouldn't exist... it should go until the unit has the aura on them.

    private constant integer    MAX_SKILL_LVL         = 10     // The level at which the hero learns all of its skills

    This is just plain silly as it can be dynamically calculated

    return .percentLife <= .35 or (.percentLife <= .55 and .mana / GetUnitState(.hero, UNIT_STATE_MAX_MANA) <= .3) or (.maxLife < 700 and .life <= 250.)

    Naive approach. This should depend on the number of enemy units, the number of ally units, the state of the ally units vs the enemy units, and depend on if there is a healer in range or not, in which case the AI should use the healer to heal the hero or w/e.

    return .percentLife >= .85 and .mana / GetUnitState(.hero, UNIT_STATE_MAX_MANA) >= .65

    Same issue. The return to points should be any point under play control that provides a means of healing.

    Code (vJASS):

                return o == 852664 or /* Healing spray
                     */
    o == 852183 or /* Starfall
                     */
    o == 852593 or /* Stampede
                     */
    o == 852488 or /* Flamestrike
                     */
    o == 852089 or /* Blizzard
                     */
    o == 852238 // Rain of Fire
     

    Very bad approach. You can easily detect whether a unit is currently channeling or not. Make a seperate resource with an IsUnitChanneling function.

    Code (vJASS):

        private function SetupItemTypes takes nothing returns nothing
            call DefaultItemBuild.addItemId('gcel', 100)
            call DefaultItemBuild.addItemId('bspd', 150)
            call DefaultItemBuild.addItemId('rlif', 200)        
            call DefaultItemBuild.addItemId('prvt', 350)
            call DefaultItemBuild.addItemId('rwiz', 400)
            call DefaultItemBuild.addItemId('pmna', 500)
        endfunction
     

    This seems stupid. It should be dependent on the hero and the state of the player and team.

    Code (vJASS):

        private function SetupLearnset takes nothing returns nothing
            // Syntax:
            // set LearnsetInfo[LEVEL OF HERO][HERO UNIT-TYPE ID] = SKILL ID
           
            // Paladin
            set LearnsetInfo[1]['Hpal'] = 'AHhb' // Holy Light
            set LearnsetInfo[3]['Hpal'] = 'AHhb'
            set LearnsetInfo[5]['Hpal'] = 'AHhb'
            set LearnsetInfo[2]['Hpal'] = 'AHds' // Divine Shield
            set LearnsetInfo[4]['Hpal'] = 'AHds'
            set LearnsetInfo[7]['Hpal'] = 'AHds'
            set LearnsetInfo[8]['Hpal'] = 'AHad' // Devotion Aura
            set LearnsetInfo[9]['Hpal'] = 'AHad'
            set LearnsetInfo[10]['Hpal'] = 'AHad'
            set LearnsetInfo[6]['Hpal'] = 'AHre' // Resurrection
           
            // Blood Mage
            set LearnsetInfo[1]['Hblm'] = 'AHfs' // Flame Strike
            set LearnsetInfo[3]['Hblm'] = 'AHfs'
            set LearnsetInfo[5]['Hblm'] = 'AHfs'
           
            // Assassin
            set LearnsetInfo[1]['E000'] = 'A001' // Backstab
            set LearnsetInfo[3]['E000'] = 'A001'
            set LearnsetInfo[5]['E000'] = 'A001'
            set LearnsetInfo[7]['E000'] = 'A001'
        endfunction
     

    This should depend on the current state of the game >.>. Obviously, if you are fighting undead heroes, you are going to want to go for Holy Light before anything else. If you have a large army and are fighting regular units, devotion aura. If you are fighting heroes regularly Divine Shield.

    struct Itemset

    This needs to provide a way to interface with a custom inventory

    and (IsUnitAlly(u, temp.owner) or IsUnitEnemy(u, temp.owner))

    That == true..

    private method buyItems takes nothing returns nothing

    Hero should buy items straight from the shop, so the set player state thing is silly as is the unit add item by id..

    method defaultLoopActions takes nothing returns nothing

    Instead of this, a goal needs to be defined for the hero. From there, the AI can follow actions leading to that goal.

    method update takes nothing returns nothing

    This is rather silly as it should just be tied in with events...

    Threats should depend on hero weaknesses and strengths... if a hero is weak vs a specific unit, then obviously that hero should go for that unit first.

    USE_ON_ATTACKED

    Should use acquisition range rather than on attack




    Overall, I wouldn't recommend anyone to use this resource quite yet. It requires a lot of work and a lot of rewriting. Try again ^)^. Also keep in mind that AI actions are largely dependent on the unit and the game that the unit is in as well as the state of the game. This makes general AI pretty much impossible to do, which is why I personally haven't done one. You could sort of get away with unique AI scripts for specific units, but you'd still have to customize them to a specific game (perhaps like a core goal script that effects all of the units).

    All in all, I think AI has 3 parts: core AI script that all AI uses, unit specific script, and team specific scripts. You sort of have the core AI, although it isn't that great, as well as minimal support for unit specific (again not that great). This is why I said that you should probably just rewrite the whole thing ; ).