Add Iterative Deepening search infrastructure

This commit is contained in:
Lucas Scharenbroich 2016-12-08 21:59:21 -06:00
parent 780f94050e
commit 2d25c5c295
11 changed files with 284 additions and 4 deletions

View File

@ -25,7 +25,7 @@
this.expander = expander;
}
public INodeExpander<A, S, T, C> Expander { get { return expander; } }
public INodeExpander<A, S, T, C> Expander { get { return expander; } set { expander = value; } }
public IEnumerable<T> Solution(T node)
{

View File

@ -0,0 +1,24 @@
namespace SpriteCompiler.AI
{
/// <summary>
/// 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.
/// </summary>
public class CostNodeLimiter<T, C> : INodeLimiter<T, C>
where T : ISearchNode<C>
where C : IPathCost<C>, new()
{
private readonly C maxCost;
public CostNodeLimiter(C maxCost)
{
this.maxCost = maxCost;
}
public bool Cutoff(T node)
{
return node.PathCost.CompareTo(maxCost) >= 0;
}
}
}

View File

@ -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<A, S, T, C> : ISearch<A, S, T, C>
where T : ISearchNode<A, S, T, C>
where C : IPathCost<C>, new()
{
protected readonly AbstractAISearch<A, S, T, C> search;
protected readonly IQueue<T> fringe = new Lifo<T>();
public DepthFirstSearch(AbstractAISearch<A, S, T, C> search)
{
this.search = search;
}
public virtual IEnumerable<T> Search(ISearchProblem<A, S, C> problem, S initialState)
{
fringe.Clear();
return search.Search(problem, fringe, initialState);
}
public virtual IEnumerable<T> ExtendSearch(ISearchProblem<A, S, C> problem)
{
return search.ExtendSearch(problem, fringe);
}
}
}

View File

@ -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<A, S, T, C> : NodeExpanderDelegator<A, S, T, C>
where T : ISearchNode<A, S, T, C>
where C : IPathCost<C>, new()
{
private readonly INodeLimiter<T, C> limit;
private bool cutoffOccured = false;
public DepthLimitedNodeExpander(INodeExpander<A, S, T, C> expander, INodeLimiter<T, C> limit)
: base(expander)
{
this.limit = limit;
}
public bool CutoffOccured { get { return cutoffOccured; } }
public override IEnumerable<T> Expand(ISearchProblem<A, S, C> problem, T node)
{
if (limit.Cutoff(node))
{
cutoffOccured = true;
return Enumerable.Empty<T>();
}
return base.Expand(problem, node);
}
}
}

View File

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SpriteCompiler.AI
{
/// <summary>
/// Depth-first search with a cutoff
/// </summary>
public class DepthLimitedSearch<A, S, T, C> : DepthFirstSearch<A, S, T, C>
where T : ISearchNode<A, S, T, C>
where C : IPathCost<C>, new()
{
protected readonly INodeLimiter<T, C> limit;
public DepthLimitedSearch(AbstractAISearch<A, S, T, C> search, INodeLimiter<T, C> limit)
: base(search)
{
this.limit = limit;
}
public override IEnumerable<T> Search(ISearchProblem<A, S, C> 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<A, S, T, C>(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<T> ExtendSearch(ISearchProblem<A, S, C> problem)
{
return base.ExtendSearch(problem);
}
public bool IsCutoff(IEnumerable<T> result)
{
return result == null;
}
}
}

View File

@ -0,0 +1,9 @@
namespace SpriteCompiler.AI
{
public interface INodeLimiter<T, C>
where T : ISearchNode<C>
where C : IPathCost<C>, new()
{
bool Cutoff(T node);
}
}

View File

@ -12,15 +12,15 @@
public interface ISearchNode<A, S, T, C> : ISearchNode<C> where C : IPathCost<C>
{
A Action { get; set; }
C PathCost { get; }
C StepCost { get; set; }
int Depth { get; }
S State { get; }
T Parent { get; }
}
public interface ISearchNode<C>
{
C PathCost { get; }
C StepCost { get; set; }
int Depth { get; }
C EstCost { get; }
}
}

View File

@ -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<A, S, T, C> : ISearch<A, S, T, C>
where T : ISearchNode<A, S, T, C>
where C : IPathCost<C>, new()
{
protected readonly AbstractAISearch<A, S, T, C> search;
protected readonly C limit;
public IterativeDeepeningAStarSearch(AbstractAISearch<A, S, T, C> search, C limit)
{
this.search = search;
this.limit = limit;
}
public IEnumerable<T> Search(ISearchProblem<A, S, C> problem, S initialState)
{
C bound = new C();
while (bound.CompareTo(limit) < 0)
{
var dls = new DepthLimitedSearch<A, S, T, C>(search, new CostNodeLimiter<T, C>(bound));
var result = dls.Search(problem, initialState);
if (!dls.IsCutoff(result))
{
return result;
}
}
// An empty list signals failure
return Enumerable.Empty<T>();
}
public IEnumerable<T> ExtendSearch(ISearchProblem<A, S, C> problem)
{
throw new NotImplementedException();
}
}
}

41
SpriteCompiler/AI/Lifo.cs Normal file
View File

@ -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<T> : IQueue<T>
{
private readonly Stack<T> stack = new Stack<T>();
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<T> items)
{
foreach (var item in items)
{
stack.Push(item);
}
}
}
}

View File

@ -0,0 +1,26 @@
namespace SpriteCompiler.AI
{
using System.Collections.Generic;
public class NodeExpanderDelegator<A, S, T, C> : INodeExpander<A, S, T, C>
where T : ISearchNode<A, S, T, C>
where C : IPathCost<C>
{
private readonly INodeExpander<A, S, T, C> expander;
public NodeExpanderDelegator(INodeExpander<A, S, T, C> expander)
{
this.expander = expander;
}
public virtual IEnumerable<T> Expand(ISearchProblem<A, S, C> problem, T node)
{
return expander.Expand(problem, node);
}
public virtual T CreateNode(T parent, S state)
{
return expander.CreateNode(parent, state);
}
}
}

View File

@ -58,9 +58,14 @@
<Compile Include="AI\AStarComparer.cs" />
<Compile Include="AI\AStarSearch.cs" />
<Compile Include="AI\BestFirstSearch.cs" />
<Compile Include="AI\CostNodeLimiter.cs" />
<Compile Include="AI\DepthFirstSearch.cs" />
<Compile Include="AI\DepthLimitedNodeExpander.cs" />
<Compile Include="AI\DepthLimitedSearch.cs" />
<Compile Include="AI\HeuristicSearchNode.cs" />
<Compile Include="AI\IHeuristicFunction.cs" />
<Compile Include="AI\InformedNodeExpander.cs" />
<Compile Include="AI\INodeLimiter.cs" />
<Compile Include="AI\ISuccessorFunction.cs" />
<Compile Include="AI\IGoalTest.cs" />
<Compile Include="AI\INodeExpander.cs" />
@ -70,6 +75,9 @@
<Compile Include="AI\ISearchNode.cs" />
<Compile Include="AI\ISearchProblem.cs" />
<Compile Include="AI\IStepCostFunction.cs" />
<Compile Include="AI\IterativeDeepeningAStarSearch.cs" />
<Compile Include="AI\Lifo.cs" />
<Compile Include="AI\NodeExpanderDelegator.cs" />
<Compile Include="AI\SearchProblem.cs" />
<Compile Include="AI\GraphSearch.cs" />
<Compile Include="AI\TreeSearch.cs" />