Sunday, January 4, 2015

Artificial Intelligence - Weighted Heuristics

One of the goals of my game is to have enemy AI that's robust enough to drive interesting and challenging battles, and also defined by external data. Ideally, enemy behavior would simply be defined by a few enumerated types, and from there, the state of the world (items, HP, positioning) and the enemy type (mage, warrior, priest) would lead to good gameplay.

It's going to be a while before I can say I'm at that point, but for now I've come up with an approach I'm calling weighted heuristics.


Watch live video from daydalus9 on Twitch

Each AI Actor has a collection of Actions, which have a weight and cost.

public Dictionary<AIActionType, int> actionWeightDictionary { get; set; } //static for life of character
public List<AIAction> AIActionList { get; set; } //updates every turn

    public enum AIActionType
    {
        Attack,
        RangedAttack,
        Heal,
        Buff,
        Nuke,
        Flee
    }

    public class AIAction
    {
        public AIActionType actionType { get; set; }
        public List battleActionList { get; set; }
        public int cost { get; set; }
        public int weight { get; set; }
    }


The default weights are defined by the enemy type, and range from 0-100. They can be thought of as a percentage that the action would be chosen.

A warrior would have something like:

case EnemyType.Warrior:
  dict.Add(AIActionType.Attack, 80);
  dict.Add(AIActionType.Heal, 10);
  dict.Add(AIActionType.Flee, 5);


while a priest would have something like:

case EnemyType.Priest:
  dict.Add(AIActionType.Attack, 10);
  dict.Add(AIActionType.Buff, 50);
  dict.Add(AIActionType.Heal, 75);
  dict.Add(AIActionType.Nuke, 50);
  dict.Add(AIActionType.Flee, 25);


The weights can also be altered each round. Healing self can be a function of current hitpoints:

int healWeight = 100 - ((character.hp / character.totalHP) * 100);


The cost of each action is driven by the actual battle actions available.

Ex: A warrior trying to attack someone 5 spaces away would cost 5 + weapon AP.

Once you have a collection of Actions, with weights and costs, you can sort them based on this formula:

AIActionList.Sort((x1, x2) => (x1.cost * (100 - x1.weight)).CompareTo(x2.cost * (100 - x2.weight)));


Once you have your sorted list, you can return the first action and get the optimum BattleAction the AI should take. If any Action Points are remaining, you can recalculate the list and get the next available Battle Action.

Right now I've built out the logic for attacking, ranged attacking (which will seek out the nearest LOS to fire the ranged weapon), and healing self. There's still a good bit more code to write, but the framework is there for the enemies to fight somewhat intelligently.

No comments:

Post a Comment