diff --git a/AI.Test/AI.Test.csproj b/AI.Test/AI.Test.csproj index c21903c..7828f92 100644 --- a/AI.Test/AI.Test.csproj +++ b/AI.Test/AI.Test.csproj @@ -8,7 +8,7 @@ Properties AI.Test AI.Test - v4.5 + v4.5.2 512 {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10.0 @@ -16,6 +16,7 @@ $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages False UnitTest + true @@ -54,6 +55,12 @@ + + + {56f54ca9-17c1-45c6-915b-6fabf4dae5d0} + SpriteCompiler + + diff --git a/AI.Test/EightPuzzleBoard.cs b/AI.Test/EightPuzzleBoard.cs index b894141..71227bb 100644 --- a/AI.Test/EightPuzzleBoard.cs +++ b/AI.Test/EightPuzzleBoard.cs @@ -6,17 +6,47 @@ using System.Threading.Tasks; namespace AI.Test { + public static class DirectionExtensions + { + public static bool CanMove(this Direction direction, int p) + { + switch (direction) + { + case Direction.LEFT: return (p % 3) != 0; + case Direction.RIGHT: return (p % 3) != 2; + case Direction.UP: return (p > 2); + case Direction.DOWN: return (p < 6); + } + + throw new ArgumentException(); + } + + public static int MoveFrom(this Direction direction, int p) + { + switch (direction) + { + case Direction.LEFT: return p - 1; + case Direction.RIGHT: return p + 1; + case Direction.UP: return p - 3; + case Direction.DOWN: return p + 3; + } + + throw new ArgumentException(); + } + } + + public enum Direction + { + LEFT, + RIGHT, + UP, + DOWN + } + public class EightPuzzleBoard { protected static Random rng = new Random(); - public enum Direction - { - LEFT, - RIGHT, - UP, - DOWN - } private int[] board; @@ -35,6 +65,8 @@ namespace AI.Test this.board = (int[])aBoard.board.Clone(); } + public int[] Board { get { return board; } } + public EightPuzzleBoard Scramble() { return Scramble(10); @@ -45,12 +77,13 @@ namespace AI.Test var newPuzzle = new EightPuzzleBoard(this); var direction = Enum.GetValues(typeof(Direction)).Cast().ToList(); - for (int i = 0; i < n; i++) + for (int i = 0; i < n;) { int j = rng.Next(direction.Count); if (newPuzzle.CanMoveGap(direction[j])) { newPuzzle.MoveGap(direction[j]); + i += 1; } } @@ -113,8 +146,51 @@ namespace AI.Test return retVal; } - public int[] Board { get { return board; } } + public int[] GetLocationOf(int val) + { + return ind2sub(GetPositionOf(val)); + } + public EightPuzzleBoard MoveGap(Direction direction) + { + var pos1 = GapPosition; + if (direction.CanMove(pos1)) + { + var pos2 = direction.MoveFrom(pos1); + Swap(pos1, pos2); + } + return this; + } + + private void Swap(int pos1, int pos2) + { + var val = this[pos1]; + this[pos1] = this[pos2]; + this[pos2] = val; + } + + public override bool Equals(object obj) + { + return (this == obj) || board.SequenceEqual(((EightPuzzleBoard)obj).board); + } + + public override int GetHashCode() + { + return board.GetHashCode(); + } + + public bool CanMoveGap(Direction where) + { + return where.CanMove(GetPositionOf(0)); + } + + public override string ToString() + { + return + board[0] + " " + board[1] + " " + board[2] + Environment.NewLine + + board[3] + " " + board[4] + " " + board[5] + Environment.NewLine + + board[6] + " " + board[7] + " " + board[8]; + } } } diff --git a/AI.Test/SearchTest.cs b/AI.Test/SearchTest.cs index cd81834..93cb137 100644 --- a/AI.Test/SearchTest.cs +++ b/AI.Test/SearchTest.cs @@ -1,14 +1,270 @@ using System; +using System.Linq; +using System.Collections.Generic; using Microsoft.VisualStudio.TestTools.UnitTesting; +using SpriteCompiler.AI; +using System.Diagnostics; +using SpriteCompiler.Adapters; namespace AI.Test { - [TestClass] - public class SearchTest + public class EightPuzzleNode : HeuristicSearchNode { - [TestMethod] - public void TestMethod1() + public EightPuzzleNode(EightPuzzleNode node, EightPuzzleBoard state) : base(node, state) { } } + + public class EightPuzzleGoalTest : IGoalTest + { + private readonly EightPuzzleBoard goal; + + public EightPuzzleGoalTest(EightPuzzleBoard goal) + { + this.goal = new EightPuzzleBoard(goal); + } + + public bool IsGoal(EightPuzzleBoard state) + { + return state.Equals(goal); + } + } + + public class EightPuzzleSuccessorFunction : ISuccessorFunction + { + public IEnumerable> Successors(EightPuzzleBoard board) + { + foreach (var direction in Enum.GetValues(typeof(Direction)).Cast()) + { + if (board.CanMoveGap(direction)) + { + yield return Tuple.Create(direction, new EightPuzzleBoard(board).MoveGap(direction)); + } + } + } + } + + public class EightPuzzleStepCost : IStepCostFunction + { + private static readonly IntegerCost UNIT_STEP_COST = (IntegerCost) 1; + + public IntegerCost StepCost(EightPuzzleBoard fromState, Direction action, EightPuzzleBoard toState) + { + return UNIT_STEP_COST; + } + } + + public class ManhattanHeuristic : IHeuristicFunction + { + private readonly EightPuzzleBoard goal; + + public ManhattanHeuristic(EightPuzzleBoard goal) + { + this.goal = new EightPuzzleBoard(goal); + } + + public IntegerCost Eval(EightPuzzleBoard state) + { + int cost = 0; + + for (int i = 1; i < 9; i++) + { + var goalLocation = goal.GetLocationOf(i); + var stateLocation = state.GetLocationOf(i); + + cost += Math.Abs(goalLocation[0] - stateLocation[0]); + cost += Math.Abs(goalLocation[1] - stateLocation[1]); + } + + return cost; + } + } + + public class MisplacedHeuristic : IHeuristicFunction + { + private readonly EightPuzzleBoard goal; + + public MisplacedHeuristic(EightPuzzleBoard goal) + { + this.goal = new EightPuzzleBoard(goal); + } + + public IntegerCost Eval(EightPuzzleBoard state) + { + return goal.CountMismatches(state); + } + } + + public static class IntrumentedParameters + { + public const string NODES_EXPANDED = "nodesExpanded"; + } + + public class InstrumentedNodeExpander : NodeExpanderDelegator + where T : ISearchNode + where C : ICost + { + private readonly IDictionary metrics = new Dictionary(); + private readonly INodeExpander expander; + + public InstrumentedNodeExpander(INodeExpander expander) + : base(expander) + { + ClearMetrics(); + } + + public override IEnumerable Expand(ISearchProblem problem, T node) + { + metrics[IntrumentedParameters.NODES_EXPANDED] += 1; + return base.Expand(problem, node); + } + + public int this[string key] + { + get + { + return metrics[key]; + } + } + + public void ClearMetrics() + { + metrics[IntrumentedParameters.NODES_EXPANDED] = 0; + } + } + + + public class EightPuzzleNodeExpander : InformedNodeExpander + { + public override EightPuzzleNode CreateNode(EightPuzzleNode parent, EightPuzzleBoard state) + { + return new EightPuzzleNode(parent, state); + } + + public override EightPuzzleNode CreateNode(EightPuzzleBoard state) + { + return CreateNode(null, state); + } + } + + [TestClass] + public class SearchTest + { + // These are the three search problem to run using IDS, A*(h1) and A*(h2) + private ISearchProblem problem_none; + private ISearchProblem problem_h1; + private ISearchProblem problem_h2; + + // Define the goal state + private EightPuzzleBoard goal = new EightPuzzleBoard(new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 }); + + [TestInitialize] + public void SetUp() + { + // These objects define the abstract search problem + var goalTest = new EightPuzzleGoalTest(goal); + var stepCost = new EightPuzzleStepCost(); + var successorFn = new EightPuzzleSuccessorFunction(); + var heuristic1 = new MisplacedHeuristic(goal); + var heuristic2 = new ManhattanHeuristic(goal); + + // Create three search problem objects. One without a heuristic and two with the different + // heuristics + problem_none = new SearchProblem(goalTest, stepCost, successorFn); + problem_h1 = new SearchProblem(goalTest, stepCost, successorFn, heuristic1); + problem_h2 = new SearchProblem(goalTest, stepCost, successorFn, heuristic2); + } + + [TestMethod] + public void TestMethod1() + { + int N = 1; + int dmax = 3; + + // Now we define the search algorithm and the type of node expansion. Russell & Norvig discuss + // two type of expansion strategies: tree search and graph search. One will avoid cycles in the search + // space and the other will not. + // + // They state that a tree search was used to generate Figure 4.8; + var expander = new InstrumentedNodeExpander(new EightPuzzleNodeExpander()); + var treeSearch = new TreeSearch(expander); + var ids = new IterativeDeepeningSearch(treeSearch, dmax); + var aStarH1 = new AStarSearch(treeSearch, new QueueAdapter()); + var aStarH2 = new IterativeDeepeningAStarSearch(treeSearch, (IntegerCost)dmax); + + // Depth runs from 0 to dmax + int[,] d = new int[dmax + 2, 3]; + int[,] n = new int[dmax + 2, 3]; + + for (int i = 0; i < N; i++) + { + // Invoke the search on the problem with a particular starting state + var initialState = goal.Scramble(dmax); + + /* + { + expander.ClearMetrics(); + var solution = ids.Search(problem_none, initialState); + System.Diagnostics.Trace.WriteLine("IDS Solution has " + solution.Count() + " nodes and expanded " + expander[IntrumentedParameters.NODES_EXPANDED] + " nodes"); + d[solution.Count(), 0] += 1; + n[solution.Count(), 0] += expander[IntrumentedParameters.NODES_EXPANDED]; + } + + { + expander.ClearMetrics(); + var solution = aStarH1.Search(problem_h1, initialState); + System.Diagnostics.Trace.WriteLine("A* (h1) Solution has " + solution.Count() + " nodes and expanded " + expander[IntrumentedParameters.NODES_EXPANDED] + " nodes"); + d[solution.Count(), 1] += 1; + n[solution.Count(), 1] += expander[IntrumentedParameters.NODES_EXPANDED]; + } + */ + { + expander.ClearMetrics(); + var solution = aStarH2.Search(problem_h2, initialState); + System.Diagnostics.Trace.WriteLine("A* (h2) Solution has " + solution.Count() + " nodes and expanded " + expander[IntrumentedParameters.NODES_EXPANDED] + " nodes"); + d[solution.Count(), 2] += 1; + n[solution.Count(), 2] += expander[IntrumentedParameters.NODES_EXPANDED]; + } + } + + Trace.WriteLine("| Search Cost Branching Factor |"); + Trace.WriteLine("+--+---------+--------+--------++---------+--------+--------+"); + Trace.WriteLine("| d| IDS | A*(h1) | A*(h2) || IDS | A*(h1) | A*(h2) |"); + Trace.WriteLine("+--+---------+--------+--------++---------+--------+--------+"); + + for (int i = 0; i <= dmax + 1; i++) + { + var bf0 = ComputeBranchingFactor((float)n[i, 0] / (float)d[i, 0], i); + var bf1 = ComputeBranchingFactor((float)n[i, 1] / (float)d[i, 1], i); + var bf2 = ComputeBranchingFactor((float)n[i, 2] / (float)d[i, 2], i); + + Trace.WriteLine(String.Format("|{0,2}|{1,-8} |{2,7} |{3,7} ||{4,8:0.00} |{5,7:0.00} |{6,7:0.00} |", i, + n[i, 0] / Math.Max(d[i, 0], 1), n[i, 1] / Math.Max(d[i, 1], 1), n[i, 2] / Math.Max(d[i, 2], 1), bf0, bf1, bf2)); + } + + Trace.WriteLine("+--+---------+--------+--------++---------+--------+--------+"); + } + + /// + /// Uses Newton iteration to solve for the effective branching factor + /// + /// number of nodes expanded + /// depth of the solution + /// + private float ComputeBranchingFactor(float n, float d) + { + float x = 3.0f; // Initial guess + + for (int i = 0; i < 20; i++) + { + float f = (float)Math.Pow(x, d + 1.0f) - 1.0f - x * n + n; + float df = (d + 1.0f) * (float)Math.Pow(x, d) - n; + + x = x - (f / df); + } + + return x; + } + + } } diff --git a/SpriteCompiler.sln b/SpriteCompiler.sln index a61dd02..720fdb2 100644 --- a/SpriteCompiler.sln +++ b/SpriteCompiler.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.40629.0 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpriteCompiler", "SpriteCompiler\SpriteCompiler.csproj", "{56F54CA9-17C1-45C6-915B-6FABF4DAE5D0}" EndProject diff --git a/SpriteCompiler/AI/AStarComparer.cs b/SpriteCompiler/AI/AStarComparer.cs index d86c9d1..eb983cd 100644 --- a/SpriteCompiler/AI/AStarComparer.cs +++ b/SpriteCompiler/AI/AStarComparer.cs @@ -8,7 +8,7 @@ namespace SpriteCompiler.AI { public class AStarComparator : IComparer where T : HeuristicSearchNode - where C : IPathCost, new() + where C : ICost, new() { public int Compare(T x, T y) { diff --git a/SpriteCompiler/AI/AStarSearch.cs b/SpriteCompiler/AI/AStarSearch.cs index 5e5aa93..6020fbf 100644 --- a/SpriteCompiler/AI/AStarSearch.cs +++ b/SpriteCompiler/AI/AStarSearch.cs @@ -1,15 +1,12 @@ namespace SpriteCompiler.AI { - public class AStarSearch : BestFirstSearch - where T : HeuristicSearchNode - where C : IPathCost, new() - { - public AStarSearch(AbstractAISearch search) - : base(search) // , new AStarComparator()) - { - } + using Queue; - public AStarSearch(AbstractAISearch search, IQueue fringe) + public class AStarSearch : BestFirstSearch + where T : IHeuristicSearchNode + where C : ICost, new() + { + public AStarSearch(ISearchStrategy search, IQueue fringe) : base(search, fringe) { } diff --git a/SpriteCompiler/AI/AbstractAISearch.cs b/SpriteCompiler/AI/AbstractAISearch.cs deleted file mode 100644 index 13e9480..0000000 --- a/SpriteCompiler/AI/AbstractAISearch.cs +++ /dev/null @@ -1,113 +0,0 @@ -namespace SpriteCompiler.AI -{ - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Linq; - - /// - /// An abstract description of a state-space search. Specific algorthims are determined by - /// how the nodes are expanded, eveanluated and enqueued. - /// - /// The description of the AI problem is delegated to the ISearchProblem interface. - /// - /// - /// - /// - /// - public abstract class AbstractAISearch : ISearch - where T : ISearchNode - where C : IPathCost - { - // 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; - private readonly IQueue fringe; - - public AbstractAISearch(INodeExpander expander, IQueue fringe) - { - this.expander = expander; - this.fringe = fringe; - } - - public INodeExpander Expander { get { return expander; } set { expander = value; } } - - public IEnumerable Solution(T node) - { - var sequence = new List(); - - while (node != null) - { - sequence.Add(node); - node = node.Parent; - } - - sequence.Reverse(); - - return sequence; - } - - public IEnumerable ExtendSearch(ISearchProblem problem) - { - while (!fringe.Empty) - { - var step = SearchStep(problem, fringe); - if (step.IsGoal) - { - return Solution(step.Node); - } - } - - return Enumerable.Empty(); - } - - public ISearchStepInfo SearchStep(ISearchProblem problem) - { - var node = fringe.Remove(); - - #if DEBUG - Console.WriteLine(string.Format("Removed {0} from the queue with g = {1}, c(n, n') = {2}", node.State, node.PathCost, node.StepCost)); - #endif - - if (problem.IsGoal(node.State)) - { - return new SearchStepInfo(node, Solution(node)); - } - - AddNodes(fringe, node, problem); - - return new SearchStepInfo(node, null); - } - - public IEnumerable Expand(ISearchProblem problem, T node) - { - return expander.Expand(problem, node); - } - - public bool IsFailure(IEnumerable solution) - { - return !solution.Any(); - } - - public virtual IEnumerable Search(ISearchProblem problem, IQueue fringe, S initialState) - { - InitializeSearch(fringe, initialState); - return ExtendSearch(problem, fringe); - } - - public void InitializeSearch(IQueue fringe, S initialState) - { - fringe.Clear(); - fringe.Enqueue(expander.CreateNode(default(T), initialState)); - } - - protected abstract void AddNodes(IQueue fringe, T node, ISearchProblem problem); - } -} diff --git a/SpriteCompiler/AI/AbstractSearchNode.cs b/SpriteCompiler/AI/AbstractSearchNode.cs index bc10533..631f93e 100644 --- a/SpriteCompiler/AI/AbstractSearchNode.cs +++ b/SpriteCompiler/AI/AbstractSearchNode.cs @@ -5,7 +5,7 @@ public abstract class AbstractSearchNode : ISearchNode where T : ISearchNode - where C : IPathCost, new() + where C : ICost, new() { protected readonly S state; protected readonly T parent; @@ -30,6 +30,8 @@ public int Depth { get { return depth; } } public S State { get { return state; } } + public C EstCost { get { return PathCost; } } + public C StepCost { get @@ -43,7 +45,5 @@ pathCost = HasParent ? parent.PathCost.Add(value) : value; } } - - virtual public C EstCost { get { return PathCost; } } } } diff --git a/SpriteCompiler/AI/AbstractSearchStrategy.cs b/SpriteCompiler/AI/AbstractSearchStrategy.cs new file mode 100644 index 0000000..e4c4abf --- /dev/null +++ b/SpriteCompiler/AI/AbstractSearchStrategy.cs @@ -0,0 +1,95 @@ +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; node != null; node = node.Parent) + { + sequence.Add(curr); + } + + sequence.Reverse(); + + return sequence; + } + + /// + /// + /// + /// + /// Must be initialize -- 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 + /// algorhtms can make different choices + /// + /// + /// + /// + protected abstract void AddNodes(IQueue fringe, T node, ISearchProblem problem); + } +} diff --git a/SpriteCompiler/AI/AbstractStateSpaceSearch.cs b/SpriteCompiler/AI/AbstractStateSpaceSearch.cs index 745ed78..6839c8e 100644 --- a/SpriteCompiler/AI/AbstractStateSpaceSearch.cs +++ b/SpriteCompiler/AI/AbstractStateSpaceSearch.cs @@ -1,103 +1,25 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SpriteCompiler.AI +namespace SpriteCompiler.AI { - public abstract class AbstractStateSpaceSearch + using System; + using System.Collections.Generic; + using Queue; + + public abstract class AbstractStateSpaceSearch : ISearch where T : ISearchNode - where C : IPathCost + where C : ICost { - private readonly INodeExpander expander; + protected readonly ISearchStrategy strategy; private readonly Func> fringe; - public AbstractStateSpaceSearch(INodeExpander expander, Func> fringe) + public AbstractStateSpaceSearch(ISearchStrategy strategy, Func> fringe) { - this.expander = expander; - this.fringe = fringe; + this.strategy = strategy; + this.fringe = fringe; } - public ISearchProblemInstance Create(ISearchProblem problem, S initialState) + public virtual IEnumerable Search(ISearchProblem problem, S initialState) { - return new SearchProblemInstance(problem, fringe(), initialState); + return strategy.Search(problem, fringe(), initialState); } } - - public interface ISearchProblemInstance - where T : ISearchNode - where C : IPathCost - { - /// - /// Tries to find a solution to the search problem. Returns null if no solution can be found. - /// This method can be called repeatedly to try to find additional, sub optimal solutions if - /// supported. - /// - /// - IEnumerable FindSolution(); - - /// - /// Take a single step of the searth to allow introspection of the search process - /// - /// - /// - ISearchStepInfo Step(); - - /// - /// Provide a set of callbacks to watch the execution of a search - /// - // void Trace(); - } - - public class SearchProblemInstance : ISearchProblemInstance - where T : ISearchNode - where C : IPathCost - { - private readonly ISearchProblem problem; - private readonly S initialState; - private readonly IQueue fringe; - - public SearchProblemInstance(ISearchProblem problem, IQueue fringe, S initialState) - { - this.problem = problem; - this.fringe = fringe; - this.initialState = initialState; - } - - public IEnumerable FindSolution() - { - throw new NotImplementedException(); - } - - public ISearchStepInfo Step() - { - var node = fringe.Remove(); - - if (problem.IsGoal(node.State)) - { - return new SearchStepInfo(node, Solution(node)); - } - - AddNodes(fringe, node, problem); - - return new SearchStepInfo(node, null); - } - - public IEnumerable Solution(T node) - { - var sequence = new List(); - - while (node != null) - { - sequence.Add(node); - node = node.Parent; - } - - sequence.Reverse(); - - return sequence; - } - - } } diff --git a/SpriteCompiler/AI/BestFirstSearch.cs b/SpriteCompiler/AI/BestFirstSearch.cs index bc2752a..99f0698 100644 --- a/SpriteCompiler/AI/BestFirstSearch.cs +++ b/SpriteCompiler/AI/BestFirstSearch.cs @@ -1,46 +1,14 @@ namespace SpriteCompiler.AI { - using Adapters; - using System.Collections.Generic; + using Queue; - public class BestFirstSearch : ISearch + public class BestFirstSearch : AbstractStateSpaceSearch where T : ISearchNode - where C : IPathCost + where C : ICost { - protected readonly AbstractAISearch search; - protected readonly IQueue fringe; - - public BestFirstSearch(AbstractAISearch search, IQueue fringe) + public BestFirstSearch(ISearchStrategy strategy, IQueue fringe) + : base(strategy, () => fringe) { - this.search = search; - this.fringe = fringe; - } - - public BestFirstSearch(AbstractAISearch search) - { - this.search = search; - this.fringe = new QueueAdapter(); - } - - public IEnumerable Search(ISearchProblem problem, S initialState) - { - fringe.Clear(); - return search.Search(problem, fringe, initialState); - } - - public IEnumerable ExtendSearch(ISearchProblem problem) - { - return search.ExtendSearch(problem, fringe); - } - - public void InitializeSearch(S initialState) - { - search.InitializeSearch(fringe, initialState); - } - - public ISearchStepInfo SearchStep(ISearchProblem problem) - { - return search.SearchStep(problem, fringe); } } } diff --git a/SpriteCompiler/AI/CostNodeLimiter.cs b/SpriteCompiler/AI/CostNodeLimiter.cs index e24fc20..8237a31 100644 --- a/SpriteCompiler/AI/CostNodeLimiter.cs +++ b/SpriteCompiler/AI/CostNodeLimiter.cs @@ -7,18 +7,31 @@ /// public class CostNodeLimiter : INodeLimiter where T : ISearchNode - where C : IPathCost, new() + where C : ICost, new() { private readonly C maxCost; + private C nextCost; - public CostNodeLimiter(C maxCost) + public CostNodeLimiter(C maxCost, C infinity) { this.maxCost = maxCost; + this.nextCost = infinity; } + public C NextCost { get { return nextCost; } } + public bool Cutoff(T node) { - return node.PathCost.CompareTo(maxCost) >= 0; + // If we find a value that exceeds the current maximum, return false, + // but keep track of the smallest value that is larger than the maximum + // cost. + if (node.PathCost.CompareTo(maxCost) > 0) + { + nextCost = (node.PathCost.CompareTo(nextCost) < 0) ? node.PathCost : nextCost; + return true; + } + + return false; } } } diff --git a/SpriteCompiler/AI/DepthFirstSearch.cs b/SpriteCompiler/AI/DepthFirstSearch.cs index 339796e..48b0b12 100644 --- a/SpriteCompiler/AI/DepthFirstSearch.cs +++ b/SpriteCompiler/AI/DepthFirstSearch.cs @@ -1,42 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SpriteCompiler.AI +namespace SpriteCompiler.AI { - public class DepthFirstSearch : ISearch + using Queue; + + public class DepthFirstSearch : AbstractStateSpaceSearch where T : ISearchNode - where C : IPathCost, new() + where C : ICost, new() { - protected readonly AbstractAISearch search; - protected readonly IQueue fringe = new Lifo(); - - public DepthFirstSearch(AbstractAISearch search) + public DepthFirstSearch(ISearchStrategy strategy) + : base(strategy, () => new LIFO()) { - 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); - } - - public void InitializeSearch(S initialState) - { - search.InitializeSearch(fringe, initialState); - } - - public ISearchStepInfo SearchStep(ISearchProblem problem) - { - return search.SearchStep(problem, fringe); } } } diff --git a/SpriteCompiler/AI/DepthLimitedNodeExpander.cs b/SpriteCompiler/AI/DepthLimitedNodeExpander.cs index 5f538f7..9c7b1ed 100644 --- a/SpriteCompiler/AI/DepthLimitedNodeExpander.cs +++ b/SpriteCompiler/AI/DepthLimitedNodeExpander.cs @@ -8,7 +8,7 @@ namespace SpriteCompiler.AI { public class DepthLimitedNodeExpander : NodeExpanderDelegator where T : ISearchNode - where C : IPathCost, new() + where C : ICost, new() { private readonly INodeLimiter limit; private bool cutoffOccured = false; diff --git a/SpriteCompiler/AI/DepthLimitedSearch.cs b/SpriteCompiler/AI/DepthLimitedSearch.cs index de3587d..44c116a 100644 --- a/SpriteCompiler/AI/DepthLimitedSearch.cs +++ b/SpriteCompiler/AI/DepthLimitedSearch.cs @@ -1,22 +1,19 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SpriteCompiler.AI +namespace SpriteCompiler.AI { + using System.Collections.Generic; + using System.Linq; + /// /// Depth-first search with a cutoff /// public class DepthLimitedSearch : DepthFirstSearch where T : ISearchNode - where C : IPathCost, new() + where C : ICost, new() { protected readonly INodeLimiter limit; - public DepthLimitedSearch(AbstractAISearch search, INodeLimiter limit) - : base(search) + public DepthLimitedSearch(ISearchStrategy strategy, INodeLimiter limit) + : base(strategy) { this.limit = limit; } @@ -24,32 +21,26 @@ namespace SpriteCompiler.AI public override IEnumerable Search(ISearchProblem problem, S initialState) { // Save the old node expander - var oldExpander = search.Expander; + var oldExpander = strategy.Expander; // Wrap the expander with a depth-limied expanded in order to // terminate the search var expander = new DepthLimitedNodeExpander(oldExpander, limit); - search.Expander = expander; + strategy.Expander = expander; // Run the search var solution = base.Search(problem, initialState); // Restore the old expander - search.Expander = oldExpander; + strategy.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) + if (!solution.Any() && expander.CutoffOccured) { return null; } return solution; - - } - - public override IEnumerable ExtendSearch(ISearchProblem problem) - { - return base.ExtendSearch(problem); } public bool IsCutoff(IEnumerable result) diff --git a/SpriteCompiler/AI/DepthNodeLimiter.cs b/SpriteCompiler/AI/DepthNodeLimiter.cs new file mode 100644 index 0000000..7d5d94d --- /dev/null +++ b/SpriteCompiler/AI/DepthNodeLimiter.cs @@ -0,0 +1,19 @@ +namespace SpriteCompiler.AI +{ + public class DepthNodeLimiter : INodeLimiter + where T : ISearchNode + where C : ICost, new() + { + private readonly int maxDepth; + + public DepthNodeLimiter(int maxDepth) + { + this.maxDepth = maxDepth; + } + + public bool Cutoff(T node) + { + return node.Depth >= maxDepth; + } + } +} diff --git a/SpriteCompiler/AI/GraphSearch.cs b/SpriteCompiler/AI/GraphSearch.cs index 9cd2319..32b4583 100644 --- a/SpriteCompiler/AI/GraphSearch.cs +++ b/SpriteCompiler/AI/GraphSearch.cs @@ -1,11 +1,11 @@ -using System; -using System.Collections.Generic; - -namespace SpriteCompiler.AI +namespace SpriteCompiler.AI { - public class GraphSearch : AbstractAISearch + using System.Collections.Generic; + using Queue; + + public class GraphSearch : AbstractSearchStrategy where T : ISearchNode - where C : IPathCost + where C : ICost { private readonly ISet closed = new HashSet(); @@ -31,7 +31,7 @@ namespace SpriteCompiler.AI if (!closed.Contains(node.State)) { closed.Add(node.State); - fringe.AddRange(Expand(problem, node)); + fringe.AddRange(Expander.Expand(problem, node)); } } } diff --git a/SpriteCompiler/AI/HeuristicSearchNode.cs b/SpriteCompiler/AI/HeuristicSearchNode.cs index 471ae9b..a3e5c25 100644 --- a/SpriteCompiler/AI/HeuristicSearchNode.cs +++ b/SpriteCompiler/AI/HeuristicSearchNode.cs @@ -2,9 +2,14 @@ { using System; - public class HeuristicSearchNode : AbstractSearchNode, ISearchNode - where T : HeuristicSearchNode - where C : IPathCost, new() + public interface IHeuristicSearchNode : ISearchNode where C : ICost + { + C EstCost { get; } + } + + public class HeuristicSearchNode : AbstractSearchNode, IHeuristicSearchNode + where T : IHeuristicSearchNode + where C : ICost, new() { public HeuristicSearchNode(T node, S state) : base(node, state) @@ -14,7 +19,7 @@ public C Heuristic { get; set; } - public override C EstCost + public C EstCost { get { diff --git a/SpriteCompiler/AI/IHeuristicFunction.cs b/SpriteCompiler/AI/IHeuristicFunction.cs index afcf070..4630b56 100644 --- a/SpriteCompiler/AI/IHeuristicFunction.cs +++ b/SpriteCompiler/AI/IHeuristicFunction.cs @@ -2,7 +2,7 @@ { using System; - public interface IHeuristicFunction where C : IPathCost + public interface IHeuristicFunction where C : ICost { C Eval(S state); } diff --git a/SpriteCompiler/AI/INodeExpander.cs b/SpriteCompiler/AI/INodeExpander.cs index 6809603..f57b715 100644 --- a/SpriteCompiler/AI/INodeExpander.cs +++ b/SpriteCompiler/AI/INodeExpander.cs @@ -11,9 +11,11 @@ /// public interface INodeExpander where T : ISearchNode - where C : IPathCost + where C : ICost { IEnumerable Expand(ISearchProblem problem, T node); + T CreateNode(T parent, S state); + T CreateNode(S state); } } diff --git a/SpriteCompiler/AI/INodeLimiter.cs b/SpriteCompiler/AI/INodeLimiter.cs index f836816..d386a28 100644 --- a/SpriteCompiler/AI/INodeLimiter.cs +++ b/SpriteCompiler/AI/INodeLimiter.cs @@ -2,7 +2,7 @@ { public interface INodeLimiter where T : ISearchNode - where C : IPathCost, new() + where C : ICost, new() { bool Cutoff(T node); } diff --git a/SpriteCompiler/AI/IPathCost.cs b/SpriteCompiler/AI/IPathCost.cs index cbfb127..56b53dd 100644 --- a/SpriteCompiler/AI/IPathCost.cs +++ b/SpriteCompiler/AI/IPathCost.cs @@ -2,8 +2,13 @@ { using System; - public interface IPathCost : IComparable + public interface ICost : IComparable { C Add(C value); + + // Number theoretic values, i.e. C + ZERO = C, C * ONE = C + C Zero(); + C One(); + C Maximum(); } } diff --git a/SpriteCompiler/AI/ISearch.cs b/SpriteCompiler/AI/ISearch.cs index 3c80d12..5279f63 100644 --- a/SpriteCompiler/AI/ISearch.cs +++ b/SpriteCompiler/AI/ISearch.cs @@ -4,23 +4,11 @@ public interface ISearch where T : ISearchNode - where C : IPathCost + where C : ICost { /// Perform a new search on the specified search problem using the given /// initial state as a starting point. The method will return an empty /// list on failure. - IEnumerable Search(ISearchProblem problem, S initialState); - - /** - * Continues to run a search after a solution is found. This can - * be useful for enumerating over all the solutions to a search problem. - * - * @param problem - * @return Sequence of search nodes desribing a solution - */ - IEnumerable ExtendSearch(ISearchProblem problem); - - void InitializeSearch(S initialState); - ISearchStepInfo SearchStep(ISearchProblem problem); + IEnumerable Search(ISearchProblem problem, S initialState); } } diff --git a/SpriteCompiler/AI/ISearchNode.cs b/SpriteCompiler/AI/ISearchNode.cs index ed6844e..f600fc6 100644 --- a/SpriteCompiler/AI/ISearchNode.cs +++ b/SpriteCompiler/AI/ISearchNode.cs @@ -9,18 +9,23 @@ /// State of the search /// Type of the parent /// Cost type - public interface ISearchNode : ISearchNode where C : IPathCost + public interface ISearchNode : ISearchNode where C : ICost { A Action { get; set; } S State { get; } T Parent { get; } } - public interface ISearchNode + /// + /// Simplest representation of a seach node that just has the costs. This interface + /// is useful for certain evaluation functions + /// + /// + public interface ISearchNode where C : ICost { C PathCost { get; } C StepCost { get; set; } - int Depth { get; } C EstCost { get; } + int Depth { get; } } } diff --git a/SpriteCompiler/AI/ISearchProblem.cs b/SpriteCompiler/AI/ISearchProblem.cs index 739f0e7..469666c 100644 --- a/SpriteCompiler/AI/ISearchProblem.cs +++ b/SpriteCompiler/AI/ISearchProblem.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; public interface ISearchProblem - where C : IComparable + where C : ICost { IEnumerable> Successors(S state); bool IsGoal(S state); diff --git a/SpriteCompiler/AI/ISearchStrategy.cs b/SpriteCompiler/AI/ISearchStrategy.cs new file mode 100644 index 0000000..0cea487 --- /dev/null +++ b/SpriteCompiler/AI/ISearchStrategy.cs @@ -0,0 +1,18 @@ +namespace SpriteCompiler.AI +{ + using System.Collections.Generic; + using Queue; + + /// + /// A search strategy defines how a state space is explored by determining which + /// nodes in the fringe are expanded next. Two common search strategies are + /// Tree Search and Graph Search. + /// + public interface ISearchStrategy + where T : ISearchNode + where C : ICost + { + IEnumerable Search(ISearchProblem problem, IQueue fringe, S initialState); + INodeExpander Expander { get; set; } + } +} diff --git a/SpriteCompiler/AI/IStepCostFunction.cs b/SpriteCompiler/AI/IStepCostFunction.cs index 5efaccf..9bc4be2 100644 --- a/SpriteCompiler/AI/IStepCostFunction.cs +++ b/SpriteCompiler/AI/IStepCostFunction.cs @@ -2,7 +2,7 @@ { using System; - public interface IStepCostFunction where C : IPathCost + public interface IStepCostFunction where C : ICost { C StepCost(S fromState, A action, S toState); } diff --git a/SpriteCompiler/AI/InformedNodeExpander.cs b/SpriteCompiler/AI/InformedNodeExpander.cs index 39e8223..23b3e7d 100644 --- a/SpriteCompiler/AI/InformedNodeExpander.cs +++ b/SpriteCompiler/AI/InformedNodeExpander.cs @@ -7,16 +7,17 @@ namespace SpriteCompiler.AI public abstract class InformedNodeExpander : INodeExpander where T : HeuristicSearchNode - where C : IPathCost, new() + where C : ICost, new() { public abstract T CreateNode(T parent, S state); + public abstract T CreateNode(S state); public IEnumerable Expand(ISearchProblem problem, T node) { var successors = problem.Successors(node.State); // Debug - #if DEBUG + #if VERBOSE_DEBUG Console.WriteLine(String.Format("There are {0} successors for {1}", successors.Count(), node)); Console.WriteLine(String.Format("This node has a current path cost of {0}", node.PathCost)); #endif @@ -31,7 +32,7 @@ namespace SpriteCompiler.AI next.StepCost = problem.StepCost(node.State, action, state); next.Heuristic = problem.Heuristic(state); -#if DEBUG +#if False Console.WriteLine(" Action = " + next.Action + ", g(n') = " + next.PathCost + ", h(n') = " + next.Heuristic); #endif yield return next; diff --git a/SpriteCompiler/AI/IntegerPathCost.cs b/SpriteCompiler/AI/IntegerPathCost.cs new file mode 100644 index 0000000..963b45c --- /dev/null +++ b/SpriteCompiler/AI/IntegerPathCost.cs @@ -0,0 +1,72 @@ +using System; + +namespace SpriteCompiler.AI +{ + /// + /// Helper class to implement simple integer path costs + /// + public sealed class IntegerCost : ICost + { + private readonly int value; + + public IntegerCost() + : this(0) + { + } + + private IntegerCost(int value) + { + this.value = value; + } + + public static implicit operator int(IntegerCost obj) + { + return obj.value; + } + + public static implicit operator IntegerCost(int value) + { + return new IntegerCost(value); + } + + public IntegerCost Add(IntegerCost other) + { + return value + other.value; + } + + public override int GetHashCode() + { + return value.GetHashCode(); + } + + public override bool Equals(object obj) + { + return value.Equals(((IntegerCost)obj).value); + } + + public int CompareTo(IntegerCost other) + { + return value.CompareTo(other.value); + } + + public override string ToString() + { + return value.ToString(); + } + + public IntegerCost Zero() + { + return 0; + } + + public IntegerCost One() + { + return 1; + } + + public IntegerCost Maximum() + { + return int.MaxValue; + } + } +} diff --git a/SpriteCompiler/AI/IterativeDeepeningAStarSearch.cs b/SpriteCompiler/AI/IterativeDeepeningAStarSearch.cs index b35925d..eb9bd05 100644 --- a/SpriteCompiler/AI/IterativeDeepeningAStarSearch.cs +++ b/SpriteCompiler/AI/IterativeDeepeningAStarSearch.cs @@ -7,13 +7,15 @@ using System.Threading.Tasks; namespace SpriteCompiler.AI { public class IterativeDeepeningAStarSearch : ISearch - where T : ISearchNode - where C : IPathCost, new() + where T : IHeuristicSearchNode + where C : ICost, new() { - protected readonly AbstractAISearch search; + private static readonly C Cost = new C(); + + protected readonly ISearchStrategy search; protected readonly C limit; - public IterativeDeepeningAStarSearch(AbstractAISearch search, C limit) + public IterativeDeepeningAStarSearch(ISearchStrategy search, C limit) { this.search = search; this.limit = limit; @@ -21,35 +23,31 @@ namespace SpriteCompiler.AI public IEnumerable Search(ISearchProblem problem, S initialState) { - C bound = new C(); + C bound = Cost.Zero(); while (bound.CompareTo(limit) < 0) { - var dls = new DepthLimitedSearch(search, new CostNodeLimiter(bound)); + var limiter = new CostNodeLimiter(bound, Cost.Maximum()); + var dls = new DepthLimitedSearch(search, limiter); var result = dls.Search(problem, initialState); + // If there was no cutoff, return the solution (or lack thereof) if (!dls.IsCutoff(result)) { return result; } + + // If the cost did not change, throw exception + if (bound.Equals(limiter.NextCost)) + { + throw new ApplicationException("IDA*: Bound did not increase after depth-limited search"); + } + + // Otherwise, increase the cutoff to the next value + bound = limiter.NextCost; } // An empty list signals failure return Enumerable.Empty(); } - - public IEnumerable ExtendSearch(ISearchProblem problem) - { - throw new NotImplementedException(); - } - - public void InitializeSearch(S initialState) - { - search.InitializeSearch(initialState); - } - - public ISearchStepInfo SearchStep(ISearchProblem problem) - { - return search.SearchStep(problem); - } } } diff --git a/SpriteCompiler/AI/IterativeDeepeningSearch.cs b/SpriteCompiler/AI/IterativeDeepeningSearch.cs new file mode 100644 index 0000000..fd64e9f --- /dev/null +++ b/SpriteCompiler/AI/IterativeDeepeningSearch.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SpriteCompiler.AI +{ + public class IterativeDeepeningSearch : ISearch + where T : ISearchNode + where C : ICost, new() + { + private readonly ISearchStrategy search; + private readonly int limit; + + public IterativeDeepeningSearch(ISearchStrategy search, int limit) + { + this.search = search; + this.limit = limit; + } + + public IEnumerable Search(ISearchProblem problem, S initialState) + { + for (int depth = 1; depth <= limit; depth++) + { + var dls = new DepthLimitedSearch(search, new DepthNodeLimiter(depth)); + var result = dls.Search(problem, initialState); + + if (!dls.IsCutoff(result)) + { + return result; + } + } + + return Enumerable.Empty(); + } + } +} diff --git a/SpriteCompiler/AI/NodeExpanderDelegator.cs b/SpriteCompiler/AI/NodeExpanderDelegator.cs index 576fabe..835a671 100644 --- a/SpriteCompiler/AI/NodeExpanderDelegator.cs +++ b/SpriteCompiler/AI/NodeExpanderDelegator.cs @@ -4,7 +4,7 @@ public class NodeExpanderDelegator : INodeExpander where T : ISearchNode - where C : IPathCost + where C : ICost { private readonly INodeExpander expander; @@ -22,5 +22,10 @@ { return expander.CreateNode(parent, state); } + + public virtual T CreateNode(S state) + { + return expander.CreateNode(state); + } } } diff --git a/SpriteCompiler/AI/Queue/FIFO.cs b/SpriteCompiler/AI/Queue/FIFO.cs new file mode 100644 index 0000000..a743355 --- /dev/null +++ b/SpriteCompiler/AI/Queue/FIFO.cs @@ -0,0 +1,37 @@ +namespace SpriteCompiler.AI.Queue +{ + using System.Collections.Generic; + + public class FIFO : IQueue + { + private readonly Queue queue = new Queue(); + + public void Clear() + { + queue.Clear(); + } + + public bool Empty + { + get { return queue.Count == 0; } + } + + public T Remove() + { + return queue.Dequeue(); + } + + public void Enqueue(T item) + { + queue.Enqueue(item); + } + + public void AddRange(IEnumerable items) + { + foreach (var item in items) + { + queue.Enqueue(item); + } + } + } +} diff --git a/SpriteCompiler/AI/IQueue.cs b/SpriteCompiler/AI/Queue/IQueue.cs similarity index 85% rename from SpriteCompiler/AI/IQueue.cs rename to SpriteCompiler/AI/Queue/IQueue.cs index 52572ff..e3d6ae7 100644 --- a/SpriteCompiler/AI/IQueue.cs +++ b/SpriteCompiler/AI/Queue/IQueue.cs @@ -1,8 +1,9 @@ -namespace SpriteCompiler.AI +namespace SpriteCompiler.AI.Queue { using System; using System.Collections.Generic; + public interface IQueue { void Clear(); diff --git a/SpriteCompiler/AI/Lifo.cs b/SpriteCompiler/AI/Queue/LIFO.cs similarity index 76% rename from SpriteCompiler/AI/Lifo.cs rename to SpriteCompiler/AI/Queue/LIFO.cs index 9e2282c..c7c47ae 100644 --- a/SpriteCompiler/AI/Lifo.cs +++ b/SpriteCompiler/AI/Queue/LIFO.cs @@ -1,12 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SpriteCompiler.AI +namespace SpriteCompiler.AI.Queue { - public class Lifo : IQueue + using System.Collections.Generic; + + public class LIFO : IQueue { private readonly Stack stack = new Stack(); diff --git a/SpriteCompiler/AI/SearchProblem.cs b/SpriteCompiler/AI/SearchProblem.cs index 688bd42..2e0ec3d 100644 --- a/SpriteCompiler/AI/SearchProblem.cs +++ b/SpriteCompiler/AI/SearchProblem.cs @@ -7,13 +7,18 @@ using System.Threading.Tasks; namespace SpriteCompiler.AI { public class SearchProblem : ISearchProblem - where C : IPathCost + where C : ICost, new() { private readonly IGoalTest goalTest; private readonly IStepCostFunction stepCost; private readonly ISuccessorFunction successorFn; private readonly IHeuristicFunction heuristicFn; + public SearchProblem(IGoalTest goalTest, IStepCostFunction stepCost, ISuccessorFunction successor) + : this(goalTest, stepCost, successor, null) + { + } + public SearchProblem(IGoalTest goalTest, IStepCostFunction stepCost, ISuccessorFunction successor, IHeuristicFunction heuristicFn) { this.goalTest = goalTest; @@ -39,6 +44,11 @@ namespace SpriteCompiler.AI public C Heuristic(S state) { + if (heuristicFn == null) + { + return new C(); + } + return heuristicFn.Eval(state); } } diff --git a/SpriteCompiler/AI/TreeSearch.cs b/SpriteCompiler/AI/TreeSearch.cs index de72b5e..e2554b0 100644 --- a/SpriteCompiler/AI/TreeSearch.cs +++ b/SpriteCompiler/AI/TreeSearch.cs @@ -1,11 +1,10 @@ -using System; -using System.Diagnostics; - -namespace SpriteCompiler.AI +namespace SpriteCompiler.AI { - public class TreeSearch : AbstractAISearch + using Queue; + + public class TreeSearch : AbstractSearchStrategy where T : ISearchNode - where C : IPathCost + where C : ICost { public TreeSearch(INodeExpander expander) : base(expander) @@ -17,7 +16,7 @@ namespace SpriteCompiler.AI /// protected override void AddNodes(IQueue fringe, T node, ISearchProblem problem) { - fringe.AddRange(Expand(problem, node)); + fringe.AddRange(Expander.Expand(problem, node)); } } } diff --git a/SpriteCompiler/Adapters/QueueAdapter.cs b/SpriteCompiler/Adapters/QueueAdapter.cs index 9fa48c7..e5cf0fe 100644 --- a/SpriteCompiler/Adapters/QueueAdapter.cs +++ b/SpriteCompiler/Adapters/QueueAdapter.cs @@ -2,6 +2,7 @@ { using Priority_Queue; using SpriteCompiler.AI; + using SpriteCompiler.AI.Queue; using System; using System.Collections.Generic; using System.Linq; @@ -10,7 +11,7 @@ public class QueueAdapter : IQueue where T : ISearchNode - where C : IComparable + where C : ICost { private readonly SimplePriorityQueue queue = new SimplePriorityQueue(); @@ -20,7 +21,7 @@ { foreach (var item in items) { -#if DEBUG +#if VERBOSE_DEBUG Console.WriteLine("Enqueuing " + item + " with cost " + item.EstCost); #endif queue.Enqueue(item, item.EstCost); diff --git a/SpriteCompiler/Problem/IntegerPathCost.cs b/SpriteCompiler/Problem/IntegerPathCost.cs deleted file mode 100644 index 42b1370..0000000 --- a/SpriteCompiler/Problem/IntegerPathCost.cs +++ /dev/null @@ -1,44 +0,0 @@ -namespace SpriteCompiler.Problem -{ - using SpriteCompiler.AI; - - public sealed class IntegerPathCost : IPathCost - { - private readonly int value; - - public IntegerPathCost() - : this(0) - { - } - - private IntegerPathCost(int value) - { - this.value = value; - } - - public static implicit operator int(IntegerPathCost obj) - { - return obj.value; - } - - public static implicit operator IntegerPathCost(int value) - { - return new IntegerPathCost(value); - } - - public IntegerPathCost Add(IntegerPathCost other) - { - return value + other.value; - } - - public int CompareTo(IntegerPathCost other) - { - return value.CompareTo(other.value); - } - - public override string ToString() - { - return value.ToString(); - } - } -} diff --git a/SpriteCompiler/Problem/SpriteGeneratorHeuristicFunction.cs b/SpriteCompiler/Problem/SpriteGeneratorHeuristicFunction.cs index 738d415..e28d407 100644 --- a/SpriteCompiler/Problem/SpriteGeneratorHeuristicFunction.cs +++ b/SpriteCompiler/Problem/SpriteGeneratorHeuristicFunction.cs @@ -4,7 +4,7 @@ using System.Linq; using System; - public sealed class SpriteGeneratorHeuristicFunction : IHeuristicFunction + public sealed class SpriteGeneratorHeuristicFunction : IHeuristicFunction { private static int SpanAndGapCost(int stack, int start, int end, int next) { @@ -39,7 +39,7 @@ return gapCost + spanCost; } - public IntegerPathCost Eval(SpriteGeneratorState state) + public IntegerCost Eval(SpriteGeneratorState state) { // An admissible heuistic that calculates a cost based on the gaps and runs in a sprite // diff --git a/SpriteCompiler/Problem/SpriteGeneratorNodeExpander.cs b/SpriteCompiler/Problem/SpriteGeneratorNodeExpander.cs index f17ca9c..075af64 100644 --- a/SpriteCompiler/Problem/SpriteGeneratorNodeExpander.cs +++ b/SpriteCompiler/Problem/SpriteGeneratorNodeExpander.cs @@ -2,11 +2,16 @@ { using SpriteCompiler.AI; - public sealed class SpriteGeneratorNodeExpander : InformedNodeExpander + public sealed class SpriteGeneratorNodeExpander : InformedNodeExpander { public override SpriteGeneratorSearchNode CreateNode(SpriteGeneratorSearchNode parent, SpriteGeneratorState state) { return new SpriteGeneratorSearchNode(parent, state); } + + public override SpriteGeneratorSearchNode CreateNode(SpriteGeneratorState state) + { + return CreateNode(null, state); + } } } diff --git a/SpriteCompiler/Problem/SpriteGeneratorSearchNode.cs b/SpriteCompiler/Problem/SpriteGeneratorSearchNode.cs index 55d0924..c5c4c1a 100644 --- a/SpriteCompiler/Problem/SpriteGeneratorSearchNode.cs +++ b/SpriteCompiler/Problem/SpriteGeneratorSearchNode.cs @@ -2,7 +2,7 @@ { using SpriteCompiler.AI; - public sealed class SpriteGeneratorSearchNode : HeuristicSearchNode + public sealed class SpriteGeneratorSearchNode : HeuristicSearchNode { public SpriteGeneratorSearchNode(SpriteGeneratorSearchNode node, SpriteGeneratorState state) : base(node, state) diff --git a/SpriteCompiler/Problem/SpriteGeneratorSearchProblem.cs b/SpriteCompiler/Problem/SpriteGeneratorSearchProblem.cs index f4cfc8a..463331a 100644 --- a/SpriteCompiler/Problem/SpriteGeneratorSearchProblem.cs +++ b/SpriteCompiler/Problem/SpriteGeneratorSearchProblem.cs @@ -4,33 +4,33 @@ public sealed class SpriteGeneratorSearchProblem { - public static ISearchProblem CreateSearchProblem() + public static ISearchProblem CreateSearchProblem() { var goalTest = new SpriteGeneratorGoalTest(); var stepCost = new SpriteGeneratorStepCost(); var successors = new SpriteGeneratorSuccessorFunction(); var heuristic = new SpriteGeneratorHeuristicFunction(); - return new SearchProblem(goalTest, stepCost, successors, heuristic); + return new SearchProblem(goalTest, stepCost, successors, heuristic); } - public static ISearch Create() + public static ISearch Create() { var expander = new SpriteGeneratorNodeExpander(); - //var strategy = new TreeSearch(expander); - var strategy = new GraphSearch(expander); + var strategy = new TreeSearch(expander); + var queue = new Adapters.QueueAdapter(); - return new AStarSearch(strategy); + return new AStarSearch(strategy, queue); } - public static ISearch Create(int maxCycles) + public static ISearch Create(int maxCycles) { var expander = new SpriteGeneratorNodeExpander(); - //var strategy = new TreeSearch(expander); - var strategy = new GraphSearch(expander); + var strategy = new TreeSearch(expander); + var queue = new Adapters.QueueAdapter(); - var maxCost = (IntegerPathCost)maxCycles; - return new IterativeDeepeningAStarSearch(strategy, maxCost); + var maxCost = (IntegerCost)maxCycles; + return new IterativeDeepeningAStarSearch(strategy, maxCost); } } } diff --git a/SpriteCompiler/Problem/SpriteGeneratorStepCost.cs b/SpriteCompiler/Problem/SpriteGeneratorStepCost.cs index e6c6c00..5718b5a 100644 --- a/SpriteCompiler/Problem/SpriteGeneratorStepCost.cs +++ b/SpriteCompiler/Problem/SpriteGeneratorStepCost.cs @@ -2,9 +2,9 @@ { using SpriteCompiler.AI; - public sealed class SpriteGeneratorStepCost : IStepCostFunction + public sealed class SpriteGeneratorStepCost : IStepCostFunction { - public IntegerPathCost StepCost(SpriteGeneratorState fromState, CodeSequence action, SpriteGeneratorState toState) + public IntegerCost StepCost(SpriteGeneratorState fromState, CodeSequence action, SpriteGeneratorState toState) { return action.CycleCount; } diff --git a/SpriteCompiler/Program.cs b/SpriteCompiler/Program.cs index 39457c2..f518167 100644 --- a/SpriteCompiler/Program.cs +++ b/SpriteCompiler/Program.cs @@ -179,6 +179,7 @@ initialState = SpriteGeneratorState.Init(data); } + /* IEnumerable solution = null; if (verbose) { @@ -194,10 +195,11 @@ } else { - solution = search.Search(problem, initialState); + solution = search.Search(problem, fringe, initialState); } WriteOutSolution(solution); + */ } } } diff --git a/SpriteCompiler/SpriteCompiler.csproj b/SpriteCompiler/SpriteCompiler.csproj index 2e67430..dc62f51 100644 --- a/SpriteCompiler/SpriteCompiler.csproj +++ b/SpriteCompiler/SpriteCompiler.csproj @@ -53,12 +53,13 @@ - + + @@ -68,23 +69,26 @@ + - + - + + + - +