Page 1 of 3 1 2 3 LastLast
Results 1 to 10 of 24

Thread: August 24 Bot Update

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Valve Developer
    Join Date
    Sep 2011
    Posts
    1,704

    August 24 Bot Update

    Bot Pathing:

    • Added Action_MoveDirectly to move to a location while bypassing the bot pathfinder (uses normal pathfinding).
    • Added GeneratePath( vStart, vEnd, tAvoidanceZones, funcCompletion ) to the global bot script API. It takes a start location, and end location, a table with an array of packed avoidance zones (3-vectors of x, y, and radius), and a function to call when the pathfind completes. The specified function should two parameters: the overall length of the path and a table containing waypoints along the path. If the pathfind fails, it will call the function with an empty waypoint table.
    • Added AddAvoidanceZone( vLocationAndRadius ) and RemoveAvoidanceZone( iZone ) to the global bot script API. AddAvoidanceZone adds a packed avoidance zone (3-vector of x, y, radius) that GeneratePath() will not path through. It returns an index to the newly-created avoidance zone. Avoidance zones can be removed by calling RemoveAvoidanceZone with that index.
    • Added Action_MovePath( tWaypoints ) to the bot script API. It takes the waypoint table returned from GeneratePath() and paths a bot along it.
    • Added the queue and push variants of the above Action_ functions.


    Bot State Dumping:

    Servers can now be run with a -botworldstatetosocket <port> parameter. This will cause the server to generate a protobuf containing the full bot-visible worldstate to that team, and send it as binary array (SerializeToArray) to the specified localhost socket. Each of these data blocks is prefaced by 32-bit integer containing size of the block.

    This is intended for doing offline or external analysis of running games. The protobuf it generates is attached to this post.
    Attached Files Attached Files

  2. #2
    Basic Member
    Join Date
    Dec 2016
    Posts
    732
    Awesome stuff!!!

  3. #3
    Basic Member
    Join Date
    Dec 2016
    Posts
    732
    Unrelated to Bot-API (most likely) but related to Custom Game Addon API ... the
    Say() function API seems broken.

    This API Link says the 3rd parameter is a boolean for team versus all speak. But... it seems to be broken.

    When set to "false" I see the message double printed - once to (Allies) and once to (All).
    When set to "true" the game out-right crashes.

    The conditions are:
    - I initially load as the first player, but the code over-writes my hero to the bot
    Code:
    GameRules:GetGameModeEntity():SetCustomGameForceHero("npc_dota_hero_nevermore")
    hero = Entities:FindByName (nil, "npc_dota_hero_nevermore")
    Say(hero, "Getting Latest Model..", false)
    Last edited by nostrademous; 08-25-2017 at 05:18 PM.

  4. #4
    Basic Member
    Join Date
    Mar 2012
    Posts
    2,018
    Quote Originally Posted by ChrisC View Post
    Bot Pathing:

    • Added Action_MoveDirectly to move to a location while bypassing the bot pathfinder (uses normal pathfinding).
    • Added GeneratePath( vStart, vEnd, tAvoidanceZones, funcCompletion ) to the global bot script API. It takes a start location, and end location, a table with an array of packed avoidance zones (3-vectors of x, y, and radius), and a function to call when the pathfind completes. The specified function should two parameters: the overall length of the path and a table containing waypoints along the path. If the pathfind fails, it will call the function with an empty waypoint table.
    • Added AddAvoidanceZone( vLocationAndRadius ) and RemoveAvoidanceZone( iZone ) to the global bot script API. AddAvoidanceZone adds a packed avoidance zone (3-vector of x, y, radius) that GeneratePath() will not path through. It returns an index to the newly-created avoidance zone. Avoidance zones can be removed by calling RemoveAvoidanceZone with that index.
    • Added Action_MovePath( tWaypoints ) to the bot script API. It takes the waypoint table returned from GeneratePath() and paths a bot along it.
    • Added the queue and push variants of the above Action_ functions.
    Welcome back Chris! WOW ! I asked for custom avoidance zones support and now I have it And I think Platinum asked for more pathfinding support

    So please confirm I understand this correctly:
    Code:
    local customAZ = nil;
    local npcBot = GetBot()
    
    -- custom wp
    local function MyPath ()
    	local myWaypoints = {Vector(1, 2}, Vector(1, 4)};
    	npcBot:Action_MovePath(myWaypoints);
    end
    	
    -- generated wp
    local function EventNewPath (nLength, tWayPoints)
        if (tWaypoints == nil) then 
            print("Path is not reachable");
        else
            npcBot:Action_MovePath(tWaypoints);
        end
    end
    
    -- avoid this zone in generated wps
    local function AvoidPath ()
    	customAZ = AddAvoidanceZone(Vector(1.5, 2.5, 500));
    end
    
    -- test wp generation
    local function GenerateNewBotPath ()
    	GeneratePath( Vector(999999999999, 999999999999}, Vector(999999999999, 999999999999), nil, EventNewPath); -- should fail
    	GeneratePath( Vector(1, 2}, Vector(1, 4), nil, EventNewPath);	-- will avoid Vector(1.5, 2.5) within 500 units
    	RemoveAvoidanceZone(customAZ);
    	GeneratePath( Vector(1, 2}, Vector(1, 4), {Vector(1.5, 2.25, 500), Vector(1.5, 2.5, 500)}, EventNewPath);
    end
    Is this the correct way to use them?

    Regarding #1, what is the difference between "normal" and "bot" pathfinding? Does MoveDirectly() behave like the player-controlled rightclick ? (meaning that the action completes EVEN IF the actual location is not reachable but it is ALMOST reachable) ATM bots will hang the queue trying to complete a move-to queued action if the target path is not reachable but it is almost reachable (meaning if a waypointed path would execute for a player controlled hero, it won't for bots because they expect to reach the actual coordinates - those were the results in some of my tests); this is where IsLocationReachable(vLocation) would be useful
    Regarding #3, will the custom AZ be returned by GetAvoidanceZones() ? If not, how do we iterate through them? Also, how long are the zones available for? I was thinking that maybe an expiration delay (0 meaning never, unless we call RemoveAvoidanceZone() manually) might help? For example areas where we know a sentry or observer was just planted. But if it complicates the code maybe it's not worth it. Not sure if it will be needed but is there a way to differentiate between them? If I understood how GetAvoidanceZones() works, it returns marked zones resulting from abilities (such as ice path, freezing field, black hole etc.) correct? And in addition it will return custom ones, right?
    Regarding #1 and #4, what is the action returned by those 2 commands? Is it still BOT_ACTION_TYPE_MOVE_TO?
    Last edited by The Nomad; 08-26-2017 at 01:08 AM.
    Explanations on the normal, high and very high brackets in replays: here, here & here
    Why maphacks won't work in D2: here

  5. #5
    Valve Developer
    Join Date
    Sep 2011
    Posts
    1,704
    Code-wise: I think you want EventNewPath as the fourth parameter in all your calls to GeneratePath, but yeah other than that it looks right. You also probably need to cache off the bot that you're generating a path for because when that pathing callback is called I don't believe GetBot() is accessible.

    Bot pathfinding uses a first-pass pathfind though the bot's pathing grid, using the weights from the built-in bot avoidance system -- it makes pathing through towers, etc more expensive. "Normal" is just a straight-up right-click.

    These avoidance zones (completely avoided by GeneratePath) are entirely independent of the default bot avoidance zones (which are used for weighting when bot pathfinding). Yes, it's kind of confusing. :-/

    The intention of the default-bot avoidance zones is that they're applied to all bots on a team, and have some information contained within them ("does physical damage", "does magical damage") etc, and different bots would react to them differently based on their details (tanky guy can ignore physical damage, if you have bkb active you can ignore the magic ones, etc). In practice, I don't think this actually works that well because they don't really capture the full breadth of the circumstances of avoidance for a given bot. I suspect that managing a moment-to-moment "absolutely do not path through this zone" list for each bot, along with a few generic "this is so bad that one one should go through it" zones works better for the specific details of what each bot wants to avoid. So that's the idea behind these new avoidance zones (and if I had the built-in ones to do over, I think that's how I'd write them). So I guess my recommendation is to punt on the old avoidance stuff entirely.

    There's no way to iterate through them currently other than you building a table of them as you add them. If it would be useful, I could add an iterator.

    Currently they last forever, but yes I could definitely add an expiration duration that you could supply when you added one. I could definitely see that being useful.

    Oh, yes, there's a new action for them, it's...um...BOT_ACTION_TYPE_MOVE_TO_DIRECT I think? I can check on Monday, but it's definitely added to the API.

  6. #6
    Basic Member
    Join Date
    Mar 2012
    Posts
    2,018
    Quote Originally Posted by ChrisC View Post
    Code-wise: I think you want EventNewPath as the fourth parameter in all your calls to GeneratePath, but yeah other than that it looks right. You also probably need to cache off the bot that you're generating a path for because when that pathing callback is called I don't believe GetBot() is accessible.
    oops, copy-paste error as I was writing it before bed (corrected in case others want it as a reference). As you prolly guessed, it was just notepad scribbling without an actual test for a PoC. And you're right, the callback context wouldn't have the handle, so I corrected the PoC with the classic npcBot to make more sense

    Quote Originally Posted by ChrisC View Post
    Bot pathfinding uses a first-pass pathfind though the bot's pathing grid, using the weights from the built-in bot avoidance system -- it makes pathing through towers, etc more expensive. "Normal" is just a straight-up right-click.
    Aha, so, that sounds like for certain modes or states where fighting would not be an issue (in certain situations - like "afk" jungling) it could reduce overhead and improve performance

    Quote Originally Posted by ChrisC View Post
    These avoidance zones (completely avoided by GeneratePath) are entirely independent of the default bot avoidance zones (which are used for weighting when bot pathfinding). Yes, it's kind of confusing. :-/

    There's no way to iterate through them currently other than you building a table of them as you add them. If it would be useful, I could add an iterator.
    Thought so

    Quote Originally Posted by ChrisC View Post
    Currently they last forever, but yes I could definitely add an expiration duration that you could supply when you added one. I could definitely see that being useful.
    TYVM. The idea is, you keep adding events every few patches and this makes us improve code by deleting IF checks that are performed every frame. I had the damage event done in Think(), before you added it so it is a huge help. While we could manage expiration with frame-by-frame checks as well, an expiration param would deffinitely be faster and better so thank you for considering it

    Quote Originally Posted by ChrisC View Post
    Oh, yes, there's a new action for them, it's...um...BOT_ACTION_TYPE_MOVE_TO_DIRECT I think? I can check on Monday, but it's definitely added to the API.
    No hurry moddota will prolly scan the _G table for it and publish it. It was just a fyi matter mostly
    Explanations on the normal, high and very high brackets in replays: here, here & here
    Why maphacks won't work in D2: here

  7. #7
    Basic Member
    Join Date
    Dec 2016
    Posts
    732
    Quote Originally Posted by ChrisC View Post
    Currently they last forever, but yes I could definitely add an expiration duration that you could supply when you added one. I could definitely see that being useful.
    Can I suggest that instead we are allowed to pass a function pointer to an assertion function (aka evaluation function) - that takes 1 argument (of my choosing) - or an arbitrary number of arguments really... not sure it matters? What I mean is, a function of my choosing that is evaluated to a true or false return value.

    That way I could do exactly what you suggest and make a simple expiration check function for some avoidance zones based on game time.
    Code:
    function TimeBasedAssertion(fTime)
        return GameTime() >= fTime
    end
    However I could also make Avoidance Zones based on other variables. For example: an enemy tower avoidance zone on the assertion that enemy tower health is >= 1.
    Code:
    function HealthBasedAssertion(hTower)
        return hTower:GetHealth() >= 1
    end
    Also, more interesting, it would also allow me to be more creative and make conditional avoidance zone (mark the Dire Safelane Jungle a huge avoidance zone if I don't see the midlaner or offlaner and have no vision via wards in their jungle) - the assertion function could be something like:
    (pseudocode)
    Code:
    function avoidEnemySafelaneJungleAssertion(fTime)
        return midlaneEnemyNotVisible(fTime) and offlaneEnemyNotVisible(fTime)
    end
    With this last example it might be helpful if the avoidance zone never really gets "deleted" but rather is considered or not considered based on the assertion evaluation.

    Quote Originally Posted by ChrisC View Post
    There's no way to iterate through them currently other than you building a table of them as you add them. If it would be useful, I could add an iterator.
    Of course that would be useful, although considering the above it might not be necessary.
    Last edited by nostrademous; 08-26-2017 at 06:59 AM.

  8. #8
    Basic Member
    Join Date
    Mar 2012
    Posts
    2,018
    Quote Originally Posted by nostrademous View Post
    Can I suggest that instead we are allowed to pass a function pointer to an assertion function (aka evaluation function) - that takes 1 argument (of my choosing) - or an arbitrary number of arguments really... not sure it matters? What I mean is, a function of my choosing that is evaluated to a true or false return value.
    So you want something similar to this pseudo-context?
    Code:
    local TowerCustomAZ = nil;
    local TimerCustomAZ = nil;
    
    local function EvalTempAZTower (tParams)
        return (tParams.tower~= nil and tParams.tower:GetHealth() > 0)
    end
    
    local function EvalTempAZTime (tParams)
        return (tParams.LastSeenTime > 5);
    end
    
    local function AddTempAZ ()
        local unit = GetTower(GetOpposingTeam(), 1);
        local location = unit:GetLocation();
        location.Z = 1000; -- tower range + 100
    
       TowerCustomAZ = AddAvoidanceZone(location, function () EvalTempAZTower ( { ["tower"] = unit} ) );
    
        unit = GameTime() + GetLastRoamTargetLastSeenTime() + lastSeenThreshold;
        location = GetSomeEnemyLastLocation();
        location.Z = 500;
       TimerCustomAZ = AddAvoidanceZone(location, function () EvalTempAZTime ( { ["Unit"] = unit, ["LastSeenTime"] = GameTime() } ) );
    end
    ... and if the eval functions return false (for example), then the engine automatically calls the AZ removal. Right?

    If so, that'd mean that the engine would have to remember the table you passed as param for each AZ callback. But I can definitely see some potential in such a design
    I mostly wanted it for wards but your idea can expand it even further.
    Last edited by The Nomad; 08-26-2017 at 07:39 AM.
    Explanations on the normal, high and very high brackets in replays: here, here & here
    Why maphacks won't work in D2: here

  9. #9
    Basic Member
    Join Date
    Dec 2016
    Posts
    732
    Quote Originally Posted by The Nomad View Post
    ... and if the eval functions return false (for example), then the engine automatically calls the AZ removal. Right?
    Sure.

    Quote Originally Posted by The Nomad View Post
    If so, that'd mean that the engine would have to remember the table you passed as param for each AZ callback.
    Not necessarily. It really depends where the evaluation happens. Currently no timers exist so the remove of any AZ would have to be done by player code. What I suggest is the same with the addition that a termination assertion is embodied with the AZ so that in our bot code we don't have to iterate all the AZ as we have a reference to the termination function which was established when we created the AZ in the first place. This would allow re-use of typical AZ termination assertions (like timers) if it made a new copy of the function when associating with an AZ (rather then just passing it by reference).

    The iteration over all the AZ's and their associated termination assertions could just be another pass complete as part of the player code time (most likely prior to each bot's Think() execution).

    The table we pass would only need to be remembered if the termination assertion evaluation is done on the server code, and not as part of player code.

  10. #10
    Basic Member
    Join Date
    Dec 2016
    Posts
    180
    Bot pathing is awesome. previously i create 2 classes( of course they are not actually class ) for creating and moving along the path. but this update may make coding easier.

    Many thanks to Chris for Bot State Dumping but 2 question comes to my mind( may be silly ):

    1- Is this table ( CMsgBotWorldState ) global for bot_generic? what i mean can we take all its parameters at any time?
    2- Is there anyway to add our own parameters to this table, for example i want to track variables that i used in my last hit function for specific hero( like antimage )?

    Hope we see some more updates about decision making

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •