namespace SpriteCompiler.AI
{
using System.Collections.Generic;
using System.Linq;
using Queue;
///
/// An abstract description of a state-space search. Specific algorthims are determined by
/// how the nodes are expanded, evaluated and enqueued.
///
/// The description of the AI problem is delegated to the ISearchProblem interface.
///
///
///
///
///
public abstract class AbstractSearchStrategy : ISearchStrategy
where T : ISearchNode
where C : ICost
{
// Conceptually the expander is responsible for two things:
//
// 1. Creating a node's successors via the Successor Function
// and wrapping them in SearchNodes (computing path costs
// and heuristics, as well).
//
// 2. Creating a search node from a state. This lets us
// decouple the search algorithm from the state expansion
private INodeExpander expander;
public AbstractSearchStrategy(INodeExpander expander)
{
this.expander = expander;
}
public INodeExpander Expander { get { return expander; } set { expander = value; } }
///
/// Helper method to walk a solution node to the root and then reverse the list
///
///
///
public IEnumerable Solution(T node)
{
var sequence = new List();
for (var curr = node; curr != null; curr = curr.Parent)
{
sequence.Add(curr);
}
sequence.Reverse();
return sequence;
}
///
///
///
///
/// Must be initialized -- usually that means being empty
///
///
public virtual IEnumerable Search(ISearchProblem problem, IQueue fringe, S initialState)
{
// Add the initial state to the fringe
fringe.Enqueue(Expander.CreateNode(initialState));
// Search until success, or the search space is exhausted
while (!fringe.Empty)
{
var node = fringe.Remove();
if (problem.IsGoal(node.State))
{
return Solution(node);
}
AddNodes(fringe, node, problem);
}
return Enumerable.Empty();
}
///
/// When it's time to actually expand a node and add the new states to the fringe, different
/// algorithms can make different choices
///
///
///
///
protected abstract void AddNodes(IQueue fringe, T node, ISearchProblem problem);
}
}