diff --git a/SpriteCompiler/AI/AStarSearch.cs b/SpriteCompiler/AI/AStarSearch.cs index 981bf4e..97ebabd 100644 --- a/SpriteCompiler/AI/AStarSearch.cs +++ b/SpriteCompiler/AI/AStarSearch.cs @@ -5,7 +5,7 @@ where C : IPathCost { public AStarSearch(AbstractAISearch search) - : base(search, new AStarComparator()) + : base(search) // , new AStarComparator()) { } diff --git a/SpriteCompiler/AI/AbstractAISearch.cs b/SpriteCompiler/AI/AbstractAISearch.cs index e6e8534..3476114 100644 --- a/SpriteCompiler/AI/AbstractAISearch.cs +++ b/SpriteCompiler/AI/AbstractAISearch.cs @@ -6,7 +6,7 @@ public abstract class AbstractAISearch where T : ISearchNode - where C : IComparable + where C : IPathCost { // Conceptually the expander is responsible for two things: // @@ -68,7 +68,7 @@ return !solution.Any(); } - public IEnumerable Search(ISearchProblem problem, IQueue fringe, S initialState) + public virtual IEnumerable Search(ISearchProblem problem, IQueue fringe, S initialState) { fringe.Enqueue(expander.CreateNode(default(T), initialState)); return ExtendSearch(problem, fringe); diff --git a/SpriteCompiler/AI/BestFirstSearch.cs b/SpriteCompiler/AI/BestFirstSearch.cs index a647340..93e9073 100644 --- a/SpriteCompiler/AI/BestFirstSearch.cs +++ b/SpriteCompiler/AI/BestFirstSearch.cs @@ -1,5 +1,6 @@ namespace SpriteCompiler.AI { + using Adapters; using System.Collections.Generic; public class BestFirstSearch : ISearch @@ -15,10 +16,10 @@ this.fringe = fringe; } - public BestFirstSearch(AbstractAISearch search, IComparer comparator) + public BestFirstSearch(AbstractAISearch search) { this.search = search; - // this.fringe = new PriorityQueue(INITIAL_CAPACITY, comparator); + this.fringe = new QueueAdapter(); } public IEnumerable Search(ISearchProblem problem, S initialState) diff --git a/SpriteCompiler/AI/GraphSearch.cs b/SpriteCompiler/AI/GraphSearch.cs new file mode 100644 index 0000000..39d9594 --- /dev/null +++ b/SpriteCompiler/AI/GraphSearch.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; + +namespace SpriteCompiler.AI +{ + public class GraphSearch : AbstractAISearch + where T : ISearchNode + where C : IPathCost + { + private readonly ISet closed = new HashSet(); + + public GraphSearch(INodeExpander expander) + : base(expander) + { + } + + /// + /// Generic graph search. See page 83 in Russell and Norvig. This only works in informed + /// search if the heuristic is admissible.However, if a heuristic is not admissible and + /// you still want to use, that means you should know enough to extend this class or write + /// your own Search class. + /// + public override IEnumerable Search(ISearchProblem problem, IQueue fringe, S initialState) + { + closed.Clear(); + return base.Search(problem, fringe, initialState); + } + + protected override void AddNodes(IQueue fringe, T node, ISearchProblem problem) + { + if (!closed.Contains(node.State)) + { + closed.Add(node.State); + fringe.AddRange(Expand(problem, node)); + } + } + } +} diff --git a/SpriteCompiler/AI/IGoalTest.cs b/SpriteCompiler/AI/IGoalTest.cs new file mode 100644 index 0000000..beed721 --- /dev/null +++ b/SpriteCompiler/AI/IGoalTest.cs @@ -0,0 +1,7 @@ +namespace SpriteCompiler.AI +{ + public interface IGoalTest + { + bool IsGoal(S state); + } +} diff --git a/SpriteCompiler/AI/IHeuristicFunction.cs b/SpriteCompiler/AI/IHeuristicFunction.cs new file mode 100644 index 0000000..afcf070 --- /dev/null +++ b/SpriteCompiler/AI/IHeuristicFunction.cs @@ -0,0 +1,9 @@ +namespace SpriteCompiler.AI +{ + using System; + + public interface IHeuristicFunction where C : IPathCost + { + C Eval(S state); + } +} diff --git a/SpriteCompiler/AI/INodeExpander.cs b/SpriteCompiler/AI/INodeExpander.cs index 6415d62..6809603 100644 --- a/SpriteCompiler/AI/INodeExpander.cs +++ b/SpriteCompiler/AI/INodeExpander.cs @@ -11,7 +11,7 @@ /// public interface INodeExpander where T : ISearchNode - where C : IComparable + where C : IPathCost { IEnumerable Expand(ISearchProblem problem, T node); T CreateNode(T parent, S state); diff --git a/SpriteCompiler/AI/IQueue.cs b/SpriteCompiler/AI/IQueue.cs index 1f1d4fd..52572ff 100644 --- a/SpriteCompiler/AI/IQueue.cs +++ b/SpriteCompiler/AI/IQueue.cs @@ -1,12 +1,15 @@ namespace SpriteCompiler.AI { using System; + using System.Collections.Generic; public interface IQueue { void Clear(); bool Empty { get; } T Remove(); + void Enqueue(T item); + void AddRange(IEnumerable items); } } diff --git a/SpriteCompiler/AI/ISearchNode.cs b/SpriteCompiler/AI/ISearchNode.cs index e0b18e4..8cb500a 100644 --- a/SpriteCompiler/AI/ISearchNode.cs +++ b/SpriteCompiler/AI/ISearchNode.cs @@ -9,13 +9,17 @@ /// State of the search /// Type of the parent /// Cost type - public interface ISearchNode where C : IComparable + public interface ISearchNode : ISearchNode where C : IPathCost { A Action { get; set; } C StepCost { get; set; } - C PathCost { get; } int Depth { get; } S State { get; } T Parent { get; } } + + public interface ISearchNode + { + C PathCost { get; } + } } diff --git a/SpriteCompiler/AI/IStepCostFunction.cs b/SpriteCompiler/AI/IStepCostFunction.cs new file mode 100644 index 0000000..5efaccf --- /dev/null +++ b/SpriteCompiler/AI/IStepCostFunction.cs @@ -0,0 +1,9 @@ +namespace SpriteCompiler.AI +{ + using System; + + public interface IStepCostFunction where C : IPathCost + { + C StepCost(S fromState, A action, S toState); + } +} diff --git a/SpriteCompiler/AI/ISuccessorFunction.cs b/SpriteCompiler/AI/ISuccessorFunction.cs new file mode 100644 index 0000000..876c501 --- /dev/null +++ b/SpriteCompiler/AI/ISuccessorFunction.cs @@ -0,0 +1,9 @@ +namespace SpriteCompiler.AI +{ + using System.Collections.Generic; + + public interface ISuccessorFunction + { + IDictionary Successors(S state); + } +} diff --git a/SpriteCompiler/AI/InformedNodeExpander.cs b/SpriteCompiler/AI/InformedNodeExpander.cs new file mode 100644 index 0000000..0c8b0ab --- /dev/null +++ b/SpriteCompiler/AI/InformedNodeExpander.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; + +namespace SpriteCompiler.AI +{ + public abstract class InformedNodeExpander : INodeExpander + where T : HeuristicSearchNode + where C : IPathCost + { + public abstract T CreateNode(T parent, S state); + + public IEnumerable Expand(ISearchProblem problem, T node) + { + foreach (var successor in problem.Successors(node.State)) + { + var action = successor.Key; + var state = successor.Value; + var next = CreateNode(node, state); + + next.Action = action; + next.StepCost = problem.StepCost(node.State, action, state); + next.Heuristic = problem.Heuristic(state); + + yield return next; + } + } + } +} diff --git a/SpriteCompiler/AI/SearchProblem.cs b/SpriteCompiler/AI/SearchProblem.cs new file mode 100644 index 0000000..d6941fb --- /dev/null +++ b/SpriteCompiler/AI/SearchProblem.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SpriteCompiler.AI +{ + public class SearchProblem : ISearchProblem + where C : IPathCost + { + private readonly IGoalTest goalTest; + private readonly IStepCostFunction stepCost; + private readonly ISuccessorFunction successorFn; + private readonly IHeuristicFunction heuristicFn; + + public SearchProblem(IGoalTest goalTest, IStepCostFunction stepCost, ISuccessorFunction successor, IHeuristicFunction heuristicFn) + { + this.goalTest = goalTest; + this.stepCost = stepCost; + this.successorFn = successor; + this.heuristicFn = heuristicFn; + } + + public IDictionary Successors(S state) + { + return successorFn.Successors(state); + } + + public bool IsGoal(S state) + { + return goalTest.IsGoal(state); + } + + public C StepCost(S fromState, A action, S toState) + { + return stepCost.StepCost(fromState, action, toState); + } + + public C Heuristic(S state) + { + return heuristicFn.Eval(state); + } + } +} diff --git a/SpriteCompiler/AI/TreeSearch.cs b/SpriteCompiler/AI/TreeSearch.cs new file mode 100644 index 0000000..a28483d --- /dev/null +++ b/SpriteCompiler/AI/TreeSearch.cs @@ -0,0 +1,22 @@ +using System; + +namespace SpriteCompiler.AI +{ + public class TreeSearch : AbstractAISearch + where T : ISearchNode + where C : IPathCost + { + public TreeSearch(INodeExpander expander) + : base(expander) + { + } + + /// + /// Generic tree search. See page 72 in Russell and Norvig + /// + protected override void AddNodes(IQueue fringe, T node, ISearchProblem problem) + { + fringe.AddRange(Expand(problem, node)); + } + } +}