Home Forums Development Experimenting with AI modifications

Viewing 6 posts - 1 through 6 (of 6 total)
  • Author
    Posts
  • #5665
    Bas Testerink
    Participant

    My current hobby is to try out different AI techniques to improve the autonomous behavior of creatures. In this thread I’ll keep a public log/discussion of how it’s going.

    As an initial modification I implemented a ‘Charge’ command which sends the members of your team charging in a given direction (e.g. I can use it to send a pet raven in the fog of war to see if it comes back). I figured that MinionController was the right class to add the team command functionalities. When I give a command, then it is translated to a task for the minion. However, minion tasks are only executed if getTeamMemberMove() returns no move, unless it’s a priority task (see Collective::getMove). Therefore, I added a setPriorityTaskFor function to TaskMap that inserts the task in the priority list and added a corresponding function for Collective.

    Question 1: Is using priority tasks this way a good way or is there a better way to do this?
    Question 2: TaskMap seems to never remove priority tasks. Isn’t this a potential memory issue?

    On a second note I’ve started working on a mod friendly approach for specifying AI. The idea is that you will be able to change easy to understand specifications into efficient easy to execute decision trees. The repository can be found here. An example specification looks like this:

    Heal is an action.
    Shoot is an action.
    Move is an action.
    Hit is an action.
    You can heal if you have a potion or you have a healing spell.
    You can shoot if you have a bow and the enemy is in sight.
    You can hit if you are standing next to the enemy and the enemy is in sight.
    You are wounded if you are critically wounded or you are lightly wounded.
    You are not wounded if you are not critically wounded and you are not lightly wounded.
    You should not attack if you are critically wounded and you can heal.
    If you can heal then you should.
    If you are not wounded then you should not heal.
    If you can shoot then you should.
    If you should not attack then you should not shoot.
    If you are standing next to the enemy then you should not shoot.
    If the enemy is in sight and you are not standing next to the enemy then you should move.
    If you should not attack then you should not move.
    If you can hit then you should.
    If you should not attack then you should not hit.

    Which is converted to:

    you are critically wounded?
    |-y->you have a healing spell?
    |    |-y->[heal]
    |    L-n->you have a potion?
    |         |-y->[heal]
    |         L-n->the enemy is in sight?
    |              |-y->you are standing next to the enemy?
    |              |    |-y->[hit]
    |              |    L-n->you have a bow?
    |              |         |-y->[move,shoot]
    |              |         L-n->[move]
    |              L-n->noMove
    L-n->the enemy is in sight?
         |-y->you are standing next to the enemy?
         |    |-y->you are lightly wounded?
         |    |    |-y->you have a healing spell?
         |    |    |    |-y->[heal,hit]
         |    |    |    L-n->you have a potion?
         |    |    |         |-y->[heal,hit]
         |    |    |         L-n->[hit]
         |    |    L-n->[hit]
         |    L-n->you are lightly wounded?
         |         |-y->you have a healing spell?
         |         |    |-y->you have a bow?
         |         |    |    |-y->[heal,move,shoot]
         |         |    |    L-n->[heal,move]
         |         |    L-n->you have a potion?
         |         |         |-y->you have a bow?
         |         |         |    |-y->[heal,move,shoot]
         |         |         |    L-n->[heal,move]
         |         |         L-n->you have a bow?
         |         |              |-y->[move,shoot]
         |         |              L-n->[move]
         |         L-n->you have a bow?
         |              |-y->[move,shoot]
         |              L-n->[move]
         L-n->you are lightly wounded?
              |-y->you have a healing spell?
              |    |-y->[heal]
              |    L-n->you have a potion?
              |         |-y->[heal]
              |         L-n->noMove
              L-n->noMove
    #6060
    Keeperman
    Participant

    Hi,

    Did you get anywhere with this? Would you like any help with it?

    #6062
    Bas Testerink
    Participant

    Thanks for asking! Yes, I’m still working on the tactical A.I., but have taken a bit of a different development approach. The KeeperRL code is still under development and C++ isn’t my most comfortable language, so I’m currently developing a testing environment for ideas in Java. This way I can focus more productively on setting up experiments to try out ideas and then, if something works, I can port it to C++/KeeperRL.

    My A.I. approach relies heavily on high-level observations for which I’m still in the phase of determining which ones are useful. I’ve been going through some research papers and books (e.g. A.I. programming wisdom) on tactical A.I. for turn-based games. Most sources say “make an influence map” and “search the game tree smartly”. I do not want to build game trees due to the computational cost of such algorithms. I’m not trying to make the best tactical A.I., but a fun tactical A.I. However, influence maps is definitely something I’m looking at.

    The hard part with influence maps is determining how to go from different maps to a plan to execute. If you have any ideas/experience with this then I’d love to have a talk about it : )

    On a side note regarding formations:

    If you’re interested we could collaborate on making the simple formation system that you mentioned earlier where:

    1. You order units to stay.
    2. You position yourself.
    3. You hit a button “hold formation” that keeps the units at the relative position.
    4. You can hit a button to release the formation.
    5. You can hit a button to turn the formation 45 degrees clock/counterwise.

    #6064

    Influence maps (also known as Dijkstra maps in roguelike world) assign values to all positions on the map, and a creature simply moves to an adjacent position with the highest value. In other words, it follows the gradient. The map could represent the distance to a valuable item, and the creature would go fetch it (the values would have to be negative obviously).

    The great thing about them is that they can be combined by summing up the values. Say you have a strong enemy that you would prefer to run away from, but somewhere close to it there is a valuable item.

    The current AI in KeeperRL would first weigh between fleeing from the enemy and fetching the item, and then it would run either straight for the item a straight away from the enemy.

    With influence maps, you’d have one map that repels you from the enemy, and another that wants you to fetch the item. When you combine them and have the creature follow the highest values, it could potentially find a path around the enemy that leads to the item. So it’s an outcome that you can’t get if you consider the actions individually.

    Influence maps have a pretty high computation cost, which pays off if you have a lot of units with the same movement types. You compute the maps once, and have all the units follow them (a unit can use a combination of a subset of the maps that are of interest). Computing the decision of a single unit is very cheap.

    The problem is that in KeeperRL you can have a team where one creature can fly, another is fire resistant and can walk through fire, etc. So there would be a lot of maps to compute if you want to make all the decisions that way. In addition, the units move sequentially, so after single move a map can get invalidated.

    I do use influence maps for fleeing AI though, thank to which you can see enemies smartly run past you if you try to corner them.

    #6066
    Bas Testerink
    Participant

    … the units move sequentially, so after single move a map can get invalidated.

    Yes this was one of the difficulties that I encountered. Another is that as in the case below the repelling influence of Y on X can be low when Y’s shortest path to X (given flying, etc) is long. If the wall among them would be broken down, then the influence would be large. Given that there are many dungeon-maze situations in KeeperRL, we would have to use much potential path planning, which does not make things easier.

    |--------|
     X |Y    |
    |  |--|  |
    |  |--|  |
    |        |
    |--------|

    When I’m working on it I often feel like perhaps it’s better to first focus on some more simple mechanisms such as flocking behaviors and relative formations and hope for cool emergent effects.

    • This reply was modified 7 years, 2 months ago by Bas Testerink.
    #6069
    Keeperman
    Participant

    Drop me a mail at keepermanj@Gmail.com

Viewing 6 posts - 1 through 6 (of 6 total)
  • You must be logged in to reply to this topic.