diff --git a/SpriteCompiler/AI/AbstractAISearch.cs b/SpriteCompiler/AI/AbstractAISearch.cs index 522f3ea..5fa8661 100644 --- a/SpriteCompiler/AI/AbstractAISearch.cs +++ b/SpriteCompiler/AI/AbstractAISearch.cs @@ -25,7 +25,7 @@ this.expander = expander; } - public INodeExpander Expander { get { return expander; } } + public INodeExpander Expander { get { return expander; } set { expander = value; } } public IEnumerable Solution(T node) { diff --git a/SpriteCompiler/AI/CostNodeLimiter.cs b/SpriteCompiler/AI/CostNodeLimiter.cs new file mode 100644 index 0000000..e24fc20 --- /dev/null +++ b/SpriteCompiler/AI/CostNodeLimiter.cs @@ -0,0 +1,24 @@ +namespace SpriteCompiler.AI +{ + /// + /// Class that taken a search node and detemines whether or not to terminate the + /// search at the node. This is different than a goal test and is used in the + /// contect of depth-limited searches. + /// + public class CostNodeLimiter : INodeLimiter + where T : ISearchNode + where C : IPathCost, new() + { + private readonly C maxCost; + + public CostNodeLimiter(C maxCost) + { + this.maxCost = maxCost; + } + + public bool Cutoff(T node) + { + return node.PathCost.CompareTo(maxCost) >= 0; + } + } +} diff --git a/SpriteCompiler/AI/DepthFirstSearch.cs b/SpriteCompiler/AI/DepthFirstSearch.cs new file mode 100644 index 0000000..42c4db2 --- /dev/null +++ b/SpriteCompiler/AI/DepthFirstSearch.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SpriteCompiler.AI +{ + public class DepthFirstSearch : ISearch + where T : ISearchNode + where C : IPathCost, new() + { + protected readonly AbstractAISearch search; + protected readonly IQueue fringe = new Lifo(); + + public DepthFirstSearch(AbstractAISearch search) + { + this.search = search; + } + + public virtual IEnumerable Search(ISearchProblem problem, S initialState) + { + fringe.Clear(); + return search.Search(problem, fringe, initialState); + } + + public virtual IEnumerable ExtendSearch(ISearchProblem problem) + { + return search.ExtendSearch(problem, fringe); + } + } +} diff --git a/SpriteCompiler/AI/DepthLimitedNodeExpander.cs b/SpriteCompiler/AI/DepthLimitedNodeExpander.cs new file mode 100644 index 0000000..5f538f7 --- /dev/null +++ b/SpriteCompiler/AI/DepthLimitedNodeExpander.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SpriteCompiler.AI +{ + public class DepthLimitedNodeExpander : NodeExpanderDelegator + where T : ISearchNode + where C : IPathCost, new() + { + private readonly INodeLimiter limit; + private bool cutoffOccured = false; + + public DepthLimitedNodeExpander(INodeExpander expander, INodeLimiter limit) + : base(expander) + { + this.limit = limit; + } + + public bool CutoffOccured { get { return cutoffOccured; } } + + public override IEnumerable Expand(ISearchProblem problem, T node) + { + if (limit.Cutoff(node)) + { + cutoffOccured = true; + return Enumerable.Empty(); + } + + return base.Expand(problem, node); + } + } +} diff --git a/SpriteCompiler/AI/DepthLimitedSearch.cs b/SpriteCompiler/AI/DepthLimitedSearch.cs new file mode 100644 index 0000000..de3587d --- /dev/null +++ b/SpriteCompiler/AI/DepthLimitedSearch.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SpriteCompiler.AI +{ + /// + /// Depth-first search with a cutoff + /// + public class DepthLimitedSearch : DepthFirstSearch + where T : ISearchNode + where C : IPathCost, new() + { + protected readonly INodeLimiter limit; + + public DepthLimitedSearch(AbstractAISearch search, INodeLimiter limit) + : base(search) + { + this.limit = limit; + } + + public override IEnumerable Search(ISearchProblem problem, S initialState) + { + // Save the old node expander + var oldExpander = search.Expander; + + // Wrap the expander with a depth-limied expanded in order to + // terminate the search + var expander = new DepthLimitedNodeExpander(oldExpander, limit); + search.Expander = expander; + + // Run the search + var solution = base.Search(problem, initialState); + + // Restore the old expander + search.Expander = oldExpander; + + // Check to see we failed and if the reason for failing was not reaching the cutoff depth. + if (search.IsFailure(solution) && expander.CutoffOccured) + { + return null; + } + + return solution; + + } + + public override IEnumerable ExtendSearch(ISearchProblem problem) + { + return base.ExtendSearch(problem); + } + + public bool IsCutoff(IEnumerable result) + { + return result == null; + } + } +} diff --git a/SpriteCompiler/AI/INodeLimiter.cs b/SpriteCompiler/AI/INodeLimiter.cs new file mode 100644 index 0000000..f836816 --- /dev/null +++ b/SpriteCompiler/AI/INodeLimiter.cs @@ -0,0 +1,9 @@ +namespace SpriteCompiler.AI +{ + public interface INodeLimiter + where T : ISearchNode + where C : IPathCost, new() + { + bool Cutoff(T node); + } +} diff --git a/SpriteCompiler/AI/ISearchNode.cs b/SpriteCompiler/AI/ISearchNode.cs index 41435f0..ed6844e 100644 --- a/SpriteCompiler/AI/ISearchNode.cs +++ b/SpriteCompiler/AI/ISearchNode.cs @@ -12,15 +12,15 @@ public interface ISearchNode : ISearchNode where C : IPathCost { A Action { get; set; } - C PathCost { get; } - C StepCost { get; set; } - int Depth { get; } S State { get; } T Parent { get; } } public interface ISearchNode { + C PathCost { get; } + C StepCost { get; set; } + int Depth { get; } C EstCost { get; } } } diff --git a/SpriteCompiler/AI/IterativeDeepeningAStarSearch.cs b/SpriteCompiler/AI/IterativeDeepeningAStarSearch.cs new file mode 100644 index 0000000..37c75c2 --- /dev/null +++ b/SpriteCompiler/AI/IterativeDeepeningAStarSearch.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 IterativeDeepeningAStarSearch : ISearch + where T : ISearchNode + where C : IPathCost, new() + { + protected readonly AbstractAISearch search; + protected readonly C limit; + + public IterativeDeepeningAStarSearch(AbstractAISearch search, C limit) + { + this.search = search; + this.limit = limit; + } + + public IEnumerable Search(ISearchProblem problem, S initialState) + { + C bound = new C(); + while (bound.CompareTo(limit) < 0) + { + var dls = new DepthLimitedSearch(search, new CostNodeLimiter(bound)); + var result = dls.Search(problem, initialState); + + if (!dls.IsCutoff(result)) + { + return result; + } + } + + // An empty list signals failure + return Enumerable.Empty(); + } + + public IEnumerable ExtendSearch(ISearchProblem problem) + { + throw new NotImplementedException(); + } + } +} diff --git a/SpriteCompiler/AI/Lifo.cs b/SpriteCompiler/AI/Lifo.cs new file mode 100644 index 0000000..9e2282c --- /dev/null +++ b/SpriteCompiler/AI/Lifo.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SpriteCompiler.AI +{ + public class Lifo : IQueue + { + private readonly Stack stack = new Stack(); + + public void Clear() + { + stack.Clear(); + } + + public bool Empty + { + get { return stack.Count == 0; } + } + + public T Remove() + { + return stack.Pop(); + } + + public void Enqueue(T item) + { + stack.Push(item); + } + + public void AddRange(IEnumerable items) + { + foreach (var item in items) + { + stack.Push(item); + } + } + } +} diff --git a/SpriteCompiler/AI/NodeExpanderDelegator.cs b/SpriteCompiler/AI/NodeExpanderDelegator.cs new file mode 100644 index 0000000..576fabe --- /dev/null +++ b/SpriteCompiler/AI/NodeExpanderDelegator.cs @@ -0,0 +1,26 @@ +namespace SpriteCompiler.AI +{ + using System.Collections.Generic; + + public class NodeExpanderDelegator : INodeExpander + where T : ISearchNode + where C : IPathCost + { + private readonly INodeExpander expander; + + public NodeExpanderDelegator(INodeExpander expander) + { + this.expander = expander; + } + + public virtual IEnumerable Expand(ISearchProblem problem, T node) + { + return expander.Expand(problem, node); + } + + public virtual T CreateNode(T parent, S state) + { + return expander.CreateNode(parent, state); + } + } +} diff --git a/SpriteCompiler/SpriteCompiler.csproj b/SpriteCompiler/SpriteCompiler.csproj index 4a37303..3b50ec8 100644 --- a/SpriteCompiler/SpriteCompiler.csproj +++ b/SpriteCompiler/SpriteCompiler.csproj @@ -58,9 +58,14 @@ + + + + + @@ -70,6 +75,9 @@ + + +