   Levelling my AI knowledge - Planning AI
  Knowing how to make an AI that can plan seems like a good tool for the toolbox; hence, I've made a simple "Gem Thief" computer game. Literally, that is; the computer plays the game. The goal of the thief is to steal the gem; my goal is to make the AI as robust as possible - Far more robust than is needed for the complexity of the game.The simple reason is so I can learn to make an AI that can scan its environment, comprehend it and develop goals and plans to accomplish those goals.However, AI is one of my weak points, so hence I'm posting snapshots here for comments, criticisms and suggestions. The AI should be generic, with as few special cases as possible. It should also be an active and reactive planner.Current state of the obstacles:Map: None. A blank, walled map.Gem: It has a 1/15 chance of randomly teleporting.The AI:    class ThiefAI    {   public ThiefAI(Map map)   {  this.data.hasGem = false;  this.needOpportunityTable = new Dictionary<NeedOpportunity, double>();  this.goals = new Dictionary<Goal, double>();  this.goalWeights = new Dictionary<Goal, double>();  this.goalWeights.Add(Goal.GetGem, 1.0);  this.plan = new List<PlanSegment>();  this.data.position.X = GeneralData.Random.Next(1, map.SizeX - 1);  this.data.position.Y = GeneralData.Random.Next(1, map.SizeY - 1);  this.symbol = new ConsoleSymbol('@');   }   protected Data data;   protected ConsoleSymbol symbol;   protected Dictionary<NeedOpportunity, Double> needOpportunityTable;   protected Dictionary<Goal, Double> goals;   protected Dictionary<Goal, Double> goalWeights;   protected List<PlanSegment> plan;   protected struct Data   {  public Data(Boolean hasGem, Point position)  { this.hasGem = hasGem; this.position = position;  }  public Boolean hasGem;  public Point position;  public Data Copy()  { return new Data(this.hasGem, this.position);  }   }   public void Update(Map map)   {  if (!data.hasGem)  { Boolean planValid = CheckPlanValid(map); if (!planValid) {GetNeedsAndOpportunities();GetGoals();MakePlan(map); } if (planValid)ExecutePlan(map);  }   }   protected Boolean CheckPlanValid(Map map)   {  Boolean planValid = plan.Count > 0;  Data tempData = this.data.Copy();  plan.Reverse();  foreach (PlanSegment planSegment in plan)  { if (planSegment.Type == PlanSegmentType.Goto) {if (planSegment.Data.GetType().GetHashCode() == typeof(Point).GetHashCode()){    if (!map.Passable[planSegment.Data.X, planSegment.Data.Y])    {   planValid = false;   break;    }    tempData.position = planSegment.Data;}else if (planSegment.Data.GetType().GetHashCode() == typeof(List<GraphNodeMap2D>).GetHashCode()){    List<GraphNodeMap2D> path = planSegment.Data;    foreach (GraphNodeMap2D graphNode in path)    {   if (!map.Passable[graphNode.Position.X, graphNode.Position.Y])   {  planValid = false;  break;   }    }    if (planSegment.Data.Count > 0)   tempData.position = path[0].Data;} } else if (planSegment.Type == PlanSegmentType.Take) {if (planSegment.Data.GetType().GetHashCode() == typeof(ItemType).GetHashCode()){    if (planSegment.Data == ItemType.Gem)    {   if (tempData.position != map.GemLocation)   {  planValid = false;  break;   }    }} }  }  plan.Reverse();  return planValid;   }   protected void ExecutePlan(Map map)   {  PlanSegment planSegment = null;  if (plan.Count > 0)  { planSegment = plan[plan.Count - 1];  }  if (planSegment != null && !planSegment.Complete)  { if (planSegment.Type == PlanSegmentType.Goto) {PlanSegmentGoto(map, planSegment); } else if (planSegment.Type == PlanSegmentType.Take) {PlanSegmentTake(map, planSegment); }  }  else plan.Clear();   }   protected void PlanSegmentGoto(Map map, PlanSegment planSegment)   {  if (planSegment.Data.GetType().GetHashCode() == typeof(Point).GetHashCode())  { Point to = planSegment.Data; if (data.position != to) {GraphMap2D graphMap = new GraphMap2D(map.Passable);graphMap.SetStart(data.position);graphMap.SetGoal(to);List<GraphNode> path;if (AStar.Pathfind(graph: graphMap, path: out path)){    /* while (path[path.Count - 1].Data == position)    {   path.RemoveAt(path.Count - 1);    }    position = path[path.Count - 1].Data; */    planSegment.Data = path.Cast<GraphNodeMap2D>().ToList();} } else {planSegment.Complete = true; }  }  else if (planSegment.Data.GetType().GetHashCode() == typeof(List<GraphNodeMap2D>).GetHashCode())  { List<GraphNodeMap2D> path = planSegment.Data; if (path.Count > 0) {while (path.Count > 0    && path[path.Count - 1].Data == data.position){    path.RemoveAt(path.Count - 1);}if (path.Count > 0)    data.position = path[path.Count - 1].Data; } elseplanSegment.Complete = true;  }   }   protected void PlanSegmentTake(Map map, PlanSegment planSegment)   {  if (planSegment.Data == ItemType.Gem)  { if (data.position == map.GemLocation) {this.data.hasGem = true; }  }   }   protected void MakePlan(Map map)   {  KeyValuePair<Goal, Double> myGoal = goals.OrderBy(a => { return a.Value; }).ElementAt(0);  if (myGoal.Key == Goal.GetGem)  { Boolean knowWhereGemIs = true; if (knowWhereGemIs) {plan.Clear();plan.Add(new PlanSegment(PlanSegmentType.Goto, map.GemLocation));plan.Add(new PlanSegment(PlanSegmentType.Take, ItemType.Gem));plan.Reverse(); }  }   }   protected void GetNeedsAndOpportunities()   {  needOpportunityTable.Clear();  needOpportunityTable.Add(NeedOpportunity.GetGem, 1.0);   }   protected void GetGoals()   {  goals.Clear();  foreach (NeedOpportunity needOp in needOpportunityTable.Keys)  { if (needOp == NeedOpportunity.GetGem)goals[Goal.GetGem] = needOpportunityTable[needOp] * goalWeights[Goal.GetGem];  }   }   public void Draw()   {  Display.Console.CursorLeft = data.position.X;  Display.Console.CursorTop = data.position.Y;  Display.Console.Write(symbol);  Display.Console.Update();   }    }
  Did some work making it more generic; instead of a hardcoded "gemLocation", it now searches for an ItemType of "Gem" in the map's IEntity list.I'm thinking of making PlanSegment graph nodes to A* a plan to accomplish the chosen goal, but I'm unsure how.    class ThiefAI    {   public ThiefAI(Map map)   {  this.data.hasGem = false;  this.needOpportunityTable = new Dictionary<NeedOpportunity, double>();  this.goals = new Dictionary<Goal, double>();  this.goalWeights = new Dictionary<Goal, double>();  this.goalWeights.Add(Goal.GetGem, 1.0);  this.plan = new List<PlanSegment>();  this.data.position.X = GeneralData.Random.Next(1, map.SizeX - 1);  this.data.position.Y = GeneralData.Random.Next(1, map.SizeY - 1);  this.symbol = new ConsoleSymbol('@');   }   protected Data data;   protected ConsoleSymbol symbol;   protected Dictionary<NeedOpportunity, Double> needOpportunityTable;   protected Dictionary<Goal, Double> goals;   protected Dictionary<Goal, Double> goalWeights;   protected List<PlanSegment> plan;   protected struct Data   {  public Data(Boolean hasGem, Point position)  { this.hasGem = hasGem; this.position = position;  }  public Boolean hasGem;  public Point position;  public Data Copy()  { return new Data(this.hasGem, this.position);  }   }   public void Update(Map map)   {  if (!data.hasGem)  { Boolean planValid = CheckPlanValid(map); if (!planValid) {GetNeedsAndOpportunities();GetGoals();MakePlan(map); } if (planValid)ExecutePlan(map);  }   }   protected Boolean CheckPlanValid(Map map)   {  Boolean planValid = plan.Count > 0;  Data tempData = this.data.Copy();  plan.Reverse();  foreach (PlanSegment planSegment in plan)  { if (planSegment.Type == PlanSegmentType.Goto) {if (planSegment.Data.GetType().GetHashCode() == typeof(Point).GetHashCode()){    if (!map.Passable[planSegment.Data.X, planSegment.Data.Y])    {   planValid = false;   break;    }    tempData.position = planSegment.Data;}else if (planSegment.Data.GetType().GetHashCode() == typeof(List<GraphNodeMap2D>).GetHashCode()){    List<GraphNodeMap2D> path = planSegment.Data;    foreach (GraphNodeMap2D graphNode in path)    {   if (!map.Passable[graphNode.Position.X, graphNode.Position.Y])   {  planValid = false;  break;   }    }    if (planSegment.Data.Count > 0)   tempData.position = path[0].Data;} } else if (planSegment.Type == PlanSegmentType.Take) {if (planSegment.Data.GetType().GetHashCode() == typeof(ItemType).GetHashCode()){    if (planSegment.Data == ItemType.Gem)    {   IEntity entity = map.Entities.Find(a =>   {  return a.MetaType.GetHashCode() == typeof(ItemType).GetHashCode() && a.Type == ItemType.Gem && a.Position == tempData.position;   }   );   if (entity == null)   {  planValid = false;  break;   }    }} }  }  plan.Reverse();  return planValid;   }   protected void ExecutePlan(Map map)   {  PlanSegment planSegment = null;  if (plan.Count > 0)  { planSegment = plan[plan.Count - 1];  }  if (planSegment != null && !planSegment.Complete)  { if (planSegment.Type == PlanSegmentType.Goto) {PlanSegmentGoto(map, planSegment); } else if (planSegment.Type == PlanSegmentType.Take) {PlanSegmentTake(map, planSegment); }  }  else plan.Clear();   }   protected void PlanSegmentGoto(Map map, PlanSegment planSegment)   {  if (planSegment.Data.GetType().GetHashCode() == typeof(Point).GetHashCode())  { Point to = planSegment.Data; if (data.position != to) {GraphMap2D graphMap = new GraphMap2D(map.Passable);graphMap.SetStart(data.position);graphMap.SetGoal(to);List<GraphNode> path;if (AStar.Pathfind(graph: graphMap, path: out path)){    planSegment.Data = path.Cast<GraphNodeMap2D>().ToList();} } else {planSegment.Complete = true; }  }  else if (planSegment.Data.GetType().GetHashCode() == typeof(List<GraphNodeMap2D>).GetHashCode())  { List<GraphNodeMap2D> path = planSegment.Data; if (path.Count > 0) {while (path.Count > 0    && path[path.Count - 1].Data == data.position){    path.RemoveAt(path.Count - 1);}if (path.Count > 0)    data.position = path[path.Count - 1].Data; } elseplanSegment.Complete = true;  }   }   protected void PlanSegmentTake(Map map, PlanSegment planSegment)   {  if (planSegment.Data == ItemType.Gem)  { if (map.Entities.Any(a => { return a.Type == ItemType.Gem && a.Position == data.position; })) {this.data.hasGem = true; }  }   }   protected void MakePlan(Map map)   {  KeyValuePair<Goal, Double> myGoal = goals.OrderBy(a => { return a.Value; }).ElementAt(0);  if (myGoal.Key == Goal.GetGem)  { Boolean knowWhereGemIs = true; if (knowWhereGemIs) {plan.Clear();plan.Add(new PlanSegment(PlanSegmentType.Goto, map.Entities.Find(a =>{    return a.MetaType.GetHashCode() == typeof(ItemType).GetHashCode()   && a.Type == ItemType.Gem;}   ).Position));plan.Add(new PlanSegment(PlanSegmentType.Take, ItemType.Gem));plan.Reverse(); }  }   }   protected void GetNeedsAndOpportunities()   {  needOpportunityTable.Clear();  needOpportunityTable.Add(NeedOpportunity.GetGem, 1.0);   }   protected void GetGoals()   {  goals.Clear();  foreach (NeedOpportunity needOp in needOpportunityTable.Keys)  { if (needOp == NeedOpportunity.GetGem)goals[Goal.GetGem] = needOpportunityTable[needOp] * goalWeights[Goal.GetGem];  }   }   public void Draw()   {  Display.Console.CursorLeft = data.position.X;  Display.Console.CursorTop = data.position.Y;  Display.Console.Write(symbol);   }    }