mirror of
https://github.com/lscharen/iigs-sprite-compiler.git
synced 2024-12-26 16:29:16 +00:00
Significant refactoring and addition of more test cases to exercise the base AI seach code
This commit is contained in:
parent
e4ae474a67
commit
ac9850998b
@ -8,7 +8,7 @@
|
|||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
<RootNamespace>AI.Test</RootNamespace>
|
<RootNamespace>AI.Test</RootNamespace>
|
||||||
<AssemblyName>AI.Test</AssemblyName>
|
<AssemblyName>AI.Test</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||||
@ -16,6 +16,7 @@
|
|||||||
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
|
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
|
||||||
<IsCodedUITest>False</IsCodedUITest>
|
<IsCodedUITest>False</IsCodedUITest>
|
||||||
<TestProjectType>UnitTest</TestProjectType>
|
<TestProjectType>UnitTest</TestProjectType>
|
||||||
|
<TargetFrameworkProfile />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
@ -54,6 +55,12 @@
|
|||||||
<Compile Include="SearchTest.cs" />
|
<Compile Include="SearchTest.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\SpriteCompiler\SpriteCompiler.csproj">
|
||||||
|
<Project>{56f54ca9-17c1-45c6-915b-6fabf4dae5d0}</Project>
|
||||||
|
<Name>SpriteCompiler</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
<Choose>
|
<Choose>
|
||||||
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
|
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -6,17 +6,47 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace AI.Test
|
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
|
public class EightPuzzleBoard
|
||||||
{
|
{
|
||||||
protected static Random rng = new Random();
|
protected static Random rng = new Random();
|
||||||
|
|
||||||
public enum Direction
|
|
||||||
{
|
|
||||||
LEFT,
|
|
||||||
RIGHT,
|
|
||||||
UP,
|
|
||||||
DOWN
|
|
||||||
}
|
|
||||||
|
|
||||||
private int[] board;
|
private int[] board;
|
||||||
|
|
||||||
@ -35,6 +65,8 @@ namespace AI.Test
|
|||||||
this.board = (int[])aBoard.board.Clone();
|
this.board = (int[])aBoard.board.Clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int[] Board { get { return board; } }
|
||||||
|
|
||||||
public EightPuzzleBoard Scramble()
|
public EightPuzzleBoard Scramble()
|
||||||
{
|
{
|
||||||
return Scramble(10);
|
return Scramble(10);
|
||||||
@ -45,12 +77,13 @@ namespace AI.Test
|
|||||||
var newPuzzle = new EightPuzzleBoard(this);
|
var newPuzzle = new EightPuzzleBoard(this);
|
||||||
var direction = Enum.GetValues(typeof(Direction)).Cast<Direction>().ToList();
|
var direction = Enum.GetValues(typeof(Direction)).Cast<Direction>().ToList();
|
||||||
|
|
||||||
for (int i = 0; i < n; i++)
|
for (int i = 0; i < n;)
|
||||||
{
|
{
|
||||||
int j = rng.Next(direction.Count);
|
int j = rng.Next(direction.Count);
|
||||||
if (newPuzzle.CanMoveGap(direction[j]))
|
if (newPuzzle.CanMoveGap(direction[j]))
|
||||||
{
|
{
|
||||||
newPuzzle.MoveGap(direction[j]);
|
newPuzzle.MoveGap(direction[j]);
|
||||||
|
i += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,8 +146,51 @@ namespace AI.Test
|
|||||||
return retVal;
|
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];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,270 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using SpriteCompiler.AI;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using SpriteCompiler.Adapters;
|
||||||
|
|
||||||
namespace AI.Test
|
namespace AI.Test
|
||||||
{
|
{
|
||||||
[TestClass]
|
public class EightPuzzleNode : HeuristicSearchNode<Direction, EightPuzzleBoard, EightPuzzleNode, IntegerCost>
|
||||||
public class SearchTest
|
|
||||||
{
|
{
|
||||||
[TestMethod]
|
public EightPuzzleNode(EightPuzzleNode node, EightPuzzleBoard state) : base(node, state)
|
||||||
public void TestMethod1()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class EightPuzzleGoalTest : IGoalTest<EightPuzzleBoard>
|
||||||
|
{
|
||||||
|
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<Direction, EightPuzzleBoard>
|
||||||
|
{
|
||||||
|
public IEnumerable<Tuple<Direction, EightPuzzleBoard>> Successors(EightPuzzleBoard board)
|
||||||
|
{
|
||||||
|
foreach (var direction in Enum.GetValues(typeof(Direction)).Cast<Direction>())
|
||||||
|
{
|
||||||
|
if (board.CanMoveGap(direction))
|
||||||
|
{
|
||||||
|
yield return Tuple.Create(direction, new EightPuzzleBoard(board).MoveGap(direction));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EightPuzzleStepCost : IStepCostFunction<Direction, EightPuzzleBoard, IntegerCost>
|
||||||
|
{
|
||||||
|
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<EightPuzzleBoard, IntegerCost>
|
||||||
|
{
|
||||||
|
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<EightPuzzleBoard, IntegerCost>
|
||||||
|
{
|
||||||
|
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<A, S, T, C> : NodeExpanderDelegator<A, S, T, C>
|
||||||
|
where T : ISearchNode<A, S, T, C>
|
||||||
|
where C : ICost<C>
|
||||||
|
{
|
||||||
|
private readonly IDictionary<string, int> metrics = new Dictionary<string, int>();
|
||||||
|
private readonly INodeExpander<A, S, T, C> expander;
|
||||||
|
|
||||||
|
public InstrumentedNodeExpander(INodeExpander<A, S, T, C> expander)
|
||||||
|
: base(expander)
|
||||||
|
{
|
||||||
|
ClearMetrics();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<T> Expand(ISearchProblem<A, S, C> 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<Direction, EightPuzzleBoard, EightPuzzleNode, IntegerCost>
|
||||||
|
{
|
||||||
|
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<Direction, EightPuzzleBoard, IntegerCost> problem_none;
|
||||||
|
private ISearchProblem<Direction, EightPuzzleBoard, IntegerCost> problem_h1;
|
||||||
|
private ISearchProblem<Direction, EightPuzzleBoard, IntegerCost> 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<Direction, EightPuzzleBoard, IntegerCost>(goalTest, stepCost, successorFn);
|
||||||
|
problem_h1 = new SearchProblem<Direction, EightPuzzleBoard, IntegerCost>(goalTest, stepCost, successorFn, heuristic1);
|
||||||
|
problem_h2 = new SearchProblem<Direction, EightPuzzleBoard, IntegerCost>(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<Direction, EightPuzzleBoard, EightPuzzleNode, IntegerCost>(new EightPuzzleNodeExpander());
|
||||||
|
var treeSearch = new TreeSearch<Direction, EightPuzzleBoard, EightPuzzleNode, IntegerCost>(expander);
|
||||||
|
var ids = new IterativeDeepeningSearch<Direction, EightPuzzleBoard, EightPuzzleNode, IntegerCost>(treeSearch, dmax);
|
||||||
|
var aStarH1 = new AStarSearch<Direction, EightPuzzleBoard, EightPuzzleNode, IntegerCost>(treeSearch, new QueueAdapter<EightPuzzleNode, IntegerCost>());
|
||||||
|
var aStarH2 = new IterativeDeepeningAStarSearch<Direction, EightPuzzleBoard, EightPuzzleNode, IntegerCost>(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("+--+---------+--------+--------++---------+--------+--------+");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Uses Newton iteration to solve for the effective branching factor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="n">number of nodes expanded</param>
|
||||||
|
/// <param name="d">depth of the solution</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 2013
|
# Visual Studio 14
|
||||||
VisualStudioVersion = 12.0.40629.0
|
VisualStudioVersion = 14.0.25420.1
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpriteCompiler", "SpriteCompiler\SpriteCompiler.csproj", "{56F54CA9-17C1-45C6-915B-6FABF4DAE5D0}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpriteCompiler", "SpriteCompiler\SpriteCompiler.csproj", "{56F54CA9-17C1-45C6-915B-6FABF4DAE5D0}"
|
||||||
EndProject
|
EndProject
|
||||||
|
@ -8,7 +8,7 @@ namespace SpriteCompiler.AI
|
|||||||
{
|
{
|
||||||
public class AStarComparator<A, S, T, C> : IComparer<T>
|
public class AStarComparator<A, S, T, C> : IComparer<T>
|
||||||
where T : HeuristicSearchNode<A, S, T, C>
|
where T : HeuristicSearchNode<A, S, T, C>
|
||||||
where C : IPathCost<C>, new()
|
where C : ICost<C>, new()
|
||||||
{
|
{
|
||||||
public int Compare(T x, T y)
|
public int Compare(T x, T y)
|
||||||
{
|
{
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
namespace SpriteCompiler.AI
|
namespace SpriteCompiler.AI
|
||||||
{
|
{
|
||||||
public class AStarSearch<A, S, T, C> : BestFirstSearch<A, S, T, C>
|
using Queue;
|
||||||
where T : HeuristicSearchNode<A, S, T, C>
|
|
||||||
where C : IPathCost<C>, new()
|
|
||||||
{
|
|
||||||
public AStarSearch(AbstractAISearch<A, S, T, C> search)
|
|
||||||
: base(search) // , new AStarComparator<A, S, T, C>())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public AStarSearch(AbstractAISearch<A, S, T, C> search, IQueue<T> fringe)
|
public class AStarSearch<A, S, T, C> : BestFirstSearch<A, S, T, C>
|
||||||
|
where T : IHeuristicSearchNode<A, S, T, C>
|
||||||
|
where C : ICost<C>, new()
|
||||||
|
{
|
||||||
|
public AStarSearch(ISearchStrategy<A, S, T, C> search, IQueue<T> fringe)
|
||||||
: base(search, fringe)
|
: base(search, fringe)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -1,113 +0,0 @@
|
|||||||
namespace SpriteCompiler.AI
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="A"></typeparam>
|
|
||||||
/// <typeparam name="S"></typeparam>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <typeparam name="C"></typeparam>
|
|
||||||
public abstract class AbstractAISearch<A, S, T, C> : ISearch<A, S, T, C>
|
|
||||||
where T : ISearchNode<A, S, T, C>
|
|
||||||
where C : IPathCost<C>
|
|
||||||
{
|
|
||||||
// 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<A, S, T, C> expander;
|
|
||||||
private readonly IQueue<T> fringe;
|
|
||||||
|
|
||||||
public AbstractAISearch(INodeExpander<A, S, T, C> expander, IQueue<T> fringe)
|
|
||||||
{
|
|
||||||
this.expander = expander;
|
|
||||||
this.fringe = fringe;
|
|
||||||
}
|
|
||||||
|
|
||||||
public INodeExpander<A, S, T, C> Expander { get { return expander; } set { expander = value; } }
|
|
||||||
|
|
||||||
public IEnumerable<T> Solution(T node)
|
|
||||||
{
|
|
||||||
var sequence = new List<T>();
|
|
||||||
|
|
||||||
while (node != null)
|
|
||||||
{
|
|
||||||
sequence.Add(node);
|
|
||||||
node = node.Parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
sequence.Reverse();
|
|
||||||
|
|
||||||
return sequence;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<T> ExtendSearch(ISearchProblem<A, S, C> problem)
|
|
||||||
{
|
|
||||||
while (!fringe.Empty)
|
|
||||||
{
|
|
||||||
var step = SearchStep(problem, fringe);
|
|
||||||
if (step.IsGoal)
|
|
||||||
{
|
|
||||||
return Solution(step.Node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Enumerable.Empty<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISearchStepInfo<T> SearchStep(ISearchProblem<A, S, C> 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<T>(node, Solution(node));
|
|
||||||
}
|
|
||||||
|
|
||||||
AddNodes(fringe, node, problem);
|
|
||||||
|
|
||||||
return new SearchStepInfo<T>(node, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<T> Expand(ISearchProblem<A, S, C> problem, T node)
|
|
||||||
{
|
|
||||||
return expander.Expand(problem, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsFailure(IEnumerable<T> solution)
|
|
||||||
{
|
|
||||||
return !solution.Any();
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual IEnumerable<T> Search(ISearchProblem<A, S, C> problem, IQueue<T> fringe, S initialState)
|
|
||||||
{
|
|
||||||
InitializeSearch(fringe, initialState);
|
|
||||||
return ExtendSearch(problem, fringe);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InitializeSearch(IQueue<T> fringe, S initialState)
|
|
||||||
{
|
|
||||||
fringe.Clear();
|
|
||||||
fringe.Enqueue(expander.CreateNode(default(T), initialState));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void AddNodes(IQueue<T> fringe, T node, ISearchProblem<A, S, C> problem);
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
public abstract class AbstractSearchNode<A, S, T, C> : ISearchNode<A, S, T, C>
|
public abstract class AbstractSearchNode<A, S, T, C> : ISearchNode<A, S, T, C>
|
||||||
where T : ISearchNode<A, S, T, C>
|
where T : ISearchNode<A, S, T, C>
|
||||||
where C : IPathCost<C>, new()
|
where C : ICost<C>, new()
|
||||||
{
|
{
|
||||||
protected readonly S state;
|
protected readonly S state;
|
||||||
protected readonly T parent;
|
protected readonly T parent;
|
||||||
@ -30,6 +30,8 @@
|
|||||||
public int Depth { get { return depth; } }
|
public int Depth { get { return depth; } }
|
||||||
public S State { get { return state; } }
|
public S State { get { return state; } }
|
||||||
|
|
||||||
|
public C EstCost { get { return PathCost; } }
|
||||||
|
|
||||||
public C StepCost
|
public C StepCost
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -43,7 +45,5 @@
|
|||||||
pathCost = HasParent ? parent.PathCost.Add(value) : value;
|
pathCost = HasParent ? parent.PathCost.Add(value) : value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual public C EstCost { get { return PathCost; } }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
95
SpriteCompiler/AI/AbstractSearchStrategy.cs
Normal file
95
SpriteCompiler/AI/AbstractSearchStrategy.cs
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
namespace SpriteCompiler.AI
|
||||||
|
{
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Queue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="A"></typeparam>
|
||||||
|
/// <typeparam name="S"></typeparam>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <typeparam name="C"></typeparam>
|
||||||
|
public abstract class AbstractSearchStrategy<A, S, T, C> : ISearchStrategy<A, S, T, C>
|
||||||
|
where T : ISearchNode<A, S, T, C>
|
||||||
|
where C : ICost<C>
|
||||||
|
{
|
||||||
|
// 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<A, S, T, C> expander;
|
||||||
|
|
||||||
|
public AbstractSearchStrategy(INodeExpander<A, S, T, C> expander)
|
||||||
|
{
|
||||||
|
this.expander = expander;
|
||||||
|
}
|
||||||
|
|
||||||
|
public INodeExpander<A, S, T, C> Expander { get { return expander; } set { expander = value; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper method to walk a solution node to the root and then reverse the list
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="node"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public IEnumerable<T> Solution(T node)
|
||||||
|
{
|
||||||
|
var sequence = new List<T>();
|
||||||
|
|
||||||
|
for (var curr = node; node != null; node = node.Parent)
|
||||||
|
{
|
||||||
|
sequence.Add(curr);
|
||||||
|
}
|
||||||
|
|
||||||
|
sequence.Reverse();
|
||||||
|
|
||||||
|
return sequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="problem"></param>
|
||||||
|
/// <param name="fringe">Must be initialize -- usually that means being empty</param>
|
||||||
|
/// <param name="initialState"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public virtual IEnumerable<T> Search(ISearchProblem<A, S, C> problem, IQueue<T> 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<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When it's time to actually expand a node and add the new states to the fringe, different
|
||||||
|
/// algorhtms can make different choices
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fringe"></param>
|
||||||
|
/// <param name="node"></param>
|
||||||
|
/// <param name="problem"></param>
|
||||||
|
protected abstract void AddNodes(IQueue<T> fringe, T node, ISearchProblem<A, S, C> problem);
|
||||||
|
}
|
||||||
|
}
|
@ -1,103 +1,25 @@
|
|||||||
using System;
|
namespace SpriteCompiler.AI
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace SpriteCompiler.AI
|
|
||||||
{
|
{
|
||||||
public abstract class AbstractStateSpaceSearch<A, S, T, C>
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Queue;
|
||||||
|
|
||||||
|
public abstract class AbstractStateSpaceSearch<A, S, T, C> : ISearch<A, S, T, C>
|
||||||
where T : ISearchNode<A, S, T, C>
|
where T : ISearchNode<A, S, T, C>
|
||||||
where C : IPathCost<C>
|
where C : ICost<C>
|
||||||
{
|
{
|
||||||
private readonly INodeExpander<A, S, T, C> expander;
|
protected readonly ISearchStrategy<A, S, T, C> strategy;
|
||||||
private readonly Func<IQueue<T>> fringe;
|
private readonly Func<IQueue<T>> fringe;
|
||||||
|
|
||||||
public AbstractStateSpaceSearch(INodeExpander<A, S, T, C> expander, Func<IQueue<T>> fringe)
|
public AbstractStateSpaceSearch(ISearchStrategy<A, S, T, C> strategy, Func<IQueue<T>> fringe)
|
||||||
{
|
{
|
||||||
this.expander = expander;
|
this.strategy = strategy;
|
||||||
this.fringe = fringe;
|
this.fringe = fringe;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ISearchProblemInstance<A, S, T, C> Create(ISearchProblem<A, S, C> problem, S initialState)
|
public virtual IEnumerable<T> Search(ISearchProblem<A, S, C> problem, S initialState)
|
||||||
{
|
{
|
||||||
return new SearchProblemInstance<A, S, T, C>(problem, fringe(), initialState);
|
return strategy.Search(problem, fringe(), initialState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ISearchProblemInstance<A, S, T, C>
|
|
||||||
where T : ISearchNode<A, S, T, C>
|
|
||||||
where C : IPathCost<C>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
IEnumerable<T> FindSolution();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Take a single step of the searth to allow introspection of the search process
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="problem"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
ISearchStepInfo<T> Step();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provide a set of callbacks to watch the execution of a search
|
|
||||||
/// </summary>
|
|
||||||
// void Trace();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SearchProblemInstance<A, S, T, C> : ISearchProblemInstance<A, S, T, C>
|
|
||||||
where T : ISearchNode<A, S, T, C>
|
|
||||||
where C : IPathCost<C>
|
|
||||||
{
|
|
||||||
private readonly ISearchProblem<A, S, C> problem;
|
|
||||||
private readonly S initialState;
|
|
||||||
private readonly IQueue<T> fringe;
|
|
||||||
|
|
||||||
public SearchProblemInstance(ISearchProblem<A, S, C> problem, IQueue<T> fringe, S initialState)
|
|
||||||
{
|
|
||||||
this.problem = problem;
|
|
||||||
this.fringe = fringe;
|
|
||||||
this.initialState = initialState;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<T> FindSolution()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISearchStepInfo<T> Step()
|
|
||||||
{
|
|
||||||
var node = fringe.Remove();
|
|
||||||
|
|
||||||
if (problem.IsGoal(node.State))
|
|
||||||
{
|
|
||||||
return new SearchStepInfo<T>(node, Solution(node));
|
|
||||||
}
|
|
||||||
|
|
||||||
AddNodes(fringe, node, problem);
|
|
||||||
|
|
||||||
return new SearchStepInfo<T>(node, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<T> Solution(T node)
|
|
||||||
{
|
|
||||||
var sequence = new List<T>();
|
|
||||||
|
|
||||||
while (node != null)
|
|
||||||
{
|
|
||||||
sequence.Add(node);
|
|
||||||
node = node.Parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
sequence.Reverse();
|
|
||||||
|
|
||||||
return sequence;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,46 +1,14 @@
|
|||||||
namespace SpriteCompiler.AI
|
namespace SpriteCompiler.AI
|
||||||
{
|
{
|
||||||
using Adapters;
|
using Queue;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
public class BestFirstSearch<A, S, T, C> : ISearch<A, S, T, C>
|
public class BestFirstSearch<A, S, T, C> : AbstractStateSpaceSearch<A, S, T, C>
|
||||||
where T : ISearchNode<A, S, T, C>
|
where T : ISearchNode<A, S, T, C>
|
||||||
where C : IPathCost<C>
|
where C : ICost<C>
|
||||||
{
|
{
|
||||||
protected readonly AbstractAISearch<A, S, T, C> search;
|
public BestFirstSearch(ISearchStrategy<A, S, T, C> strategy, IQueue<T> fringe)
|
||||||
protected readonly IQueue<T> fringe;
|
: base(strategy, () => fringe)
|
||||||
|
|
||||||
public BestFirstSearch(AbstractAISearch<A, S, T, C> search, IQueue<T> fringe)
|
|
||||||
{
|
{
|
||||||
this.search = search;
|
|
||||||
this.fringe = fringe;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BestFirstSearch(AbstractAISearch<A, S, T, C> search)
|
|
||||||
{
|
|
||||||
this.search = search;
|
|
||||||
this.fringe = new QueueAdapter<T, C>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<T> Search(ISearchProblem<A, S, C> problem, S initialState)
|
|
||||||
{
|
|
||||||
fringe.Clear();
|
|
||||||
return search.Search(problem, fringe, initialState);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<T> ExtendSearch(ISearchProblem<A, S, C> problem)
|
|
||||||
{
|
|
||||||
return search.ExtendSearch(problem, fringe);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InitializeSearch(S initialState)
|
|
||||||
{
|
|
||||||
search.InitializeSearch(fringe, initialState);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISearchStepInfo<T> SearchStep(ISearchProblem<A,S,C> problem)
|
|
||||||
{
|
|
||||||
return search.SearchStep(problem, fringe);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,18 +7,31 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class CostNodeLimiter<T, C> : INodeLimiter<T, C>
|
public class CostNodeLimiter<T, C> : INodeLimiter<T, C>
|
||||||
where T : ISearchNode<C>
|
where T : ISearchNode<C>
|
||||||
where C : IPathCost<C>, new()
|
where C : ICost<C>, new()
|
||||||
{
|
{
|
||||||
private readonly C maxCost;
|
private readonly C maxCost;
|
||||||
|
private C nextCost;
|
||||||
|
|
||||||
public CostNodeLimiter(C maxCost)
|
public CostNodeLimiter(C maxCost, C infinity)
|
||||||
{
|
{
|
||||||
this.maxCost = maxCost;
|
this.maxCost = maxCost;
|
||||||
|
this.nextCost = infinity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public C NextCost { get { return nextCost; } }
|
||||||
|
|
||||||
public bool Cutoff(T node)
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,42 +1,14 @@
|
|||||||
using System;
|
namespace SpriteCompiler.AI
|
||||||
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>
|
using Queue;
|
||||||
|
|
||||||
|
public class DepthFirstSearch<A, S, T, C> : AbstractStateSpaceSearch<A, S, T, C>
|
||||||
where T : ISearchNode<A, S, T, C>
|
where T : ISearchNode<A, S, T, C>
|
||||||
where C : IPathCost<C>, new()
|
where C : ICost<C>, new()
|
||||||
{
|
{
|
||||||
protected readonly AbstractAISearch<A, S, T, C> search;
|
public DepthFirstSearch(ISearchStrategy<A, S, T, C> strategy)
|
||||||
protected readonly IQueue<T> fringe = new Lifo<T>();
|
: base(strategy, () => 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InitializeSearch(S initialState)
|
|
||||||
{
|
|
||||||
search.InitializeSearch(fringe, initialState);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISearchStepInfo<T> SearchStep(ISearchProblem<A, S, C> problem)
|
|
||||||
{
|
|
||||||
return search.SearchStep(problem, fringe);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ namespace SpriteCompiler.AI
|
|||||||
{
|
{
|
||||||
public class DepthLimitedNodeExpander<A, S, T, C> : NodeExpanderDelegator<A, S, T, C>
|
public class DepthLimitedNodeExpander<A, S, T, C> : NodeExpanderDelegator<A, S, T, C>
|
||||||
where T : ISearchNode<A, S, T, C>
|
where T : ISearchNode<A, S, T, C>
|
||||||
where C : IPathCost<C>, new()
|
where C : ICost<C>, new()
|
||||||
{
|
{
|
||||||
private readonly INodeLimiter<T, C> limit;
|
private readonly INodeLimiter<T, C> limit;
|
||||||
private bool cutoffOccured = false;
|
private bool cutoffOccured = false;
|
||||||
|
@ -1,22 +1,19 @@
|
|||||||
using System;
|
namespace SpriteCompiler.AI
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace SpriteCompiler.AI
|
|
||||||
{
|
{
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Depth-first search with a cutoff
|
/// Depth-first search with a cutoff
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DepthLimitedSearch<A, S, T, C> : DepthFirstSearch<A, S, T, C>
|
public class DepthLimitedSearch<A, S, T, C> : DepthFirstSearch<A, S, T, C>
|
||||||
where T : ISearchNode<A, S, T, C>
|
where T : ISearchNode<A, S, T, C>
|
||||||
where C : IPathCost<C>, new()
|
where C : ICost<C>, new()
|
||||||
{
|
{
|
||||||
protected readonly INodeLimiter<T, C> limit;
|
protected readonly INodeLimiter<T, C> limit;
|
||||||
|
|
||||||
public DepthLimitedSearch(AbstractAISearch<A, S, T, C> search, INodeLimiter<T, C> limit)
|
public DepthLimitedSearch(ISearchStrategy<A, S, T, C> strategy, INodeLimiter<T, C> limit)
|
||||||
: base(search)
|
: base(strategy)
|
||||||
{
|
{
|
||||||
this.limit = limit;
|
this.limit = limit;
|
||||||
}
|
}
|
||||||
@ -24,32 +21,26 @@ namespace SpriteCompiler.AI
|
|||||||
public override IEnumerable<T> Search(ISearchProblem<A, S, C> problem, S initialState)
|
public override IEnumerable<T> Search(ISearchProblem<A, S, C> problem, S initialState)
|
||||||
{
|
{
|
||||||
// Save the old node expander
|
// Save the old node expander
|
||||||
var oldExpander = search.Expander;
|
var oldExpander = strategy.Expander;
|
||||||
|
|
||||||
// Wrap the expander with a depth-limied expanded in order to
|
// Wrap the expander with a depth-limied expanded in order to
|
||||||
// terminate the search
|
// terminate the search
|
||||||
var expander = new DepthLimitedNodeExpander<A, S, T, C>(oldExpander, limit);
|
var expander = new DepthLimitedNodeExpander<A, S, T, C>(oldExpander, limit);
|
||||||
search.Expander = expander;
|
strategy.Expander = expander;
|
||||||
|
|
||||||
// Run the search
|
// Run the search
|
||||||
var solution = base.Search(problem, initialState);
|
var solution = base.Search(problem, initialState);
|
||||||
|
|
||||||
// Restore the old expander
|
// 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.
|
// 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 null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return solution;
|
return solution;
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IEnumerable<T> ExtendSearch(ISearchProblem<A, S, C> problem)
|
|
||||||
{
|
|
||||||
return base.ExtendSearch(problem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsCutoff(IEnumerable<T> result)
|
public bool IsCutoff(IEnumerable<T> result)
|
||||||
|
19
SpriteCompiler/AI/DepthNodeLimiter.cs
Normal file
19
SpriteCompiler/AI/DepthNodeLimiter.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
namespace SpriteCompiler.AI
|
||||||
|
{
|
||||||
|
public class DepthNodeLimiter<T, C> : INodeLimiter<T, C>
|
||||||
|
where T : ISearchNode<C>
|
||||||
|
where C : ICost<C>, new()
|
||||||
|
{
|
||||||
|
private readonly int maxDepth;
|
||||||
|
|
||||||
|
public DepthNodeLimiter(int maxDepth)
|
||||||
|
{
|
||||||
|
this.maxDepth = maxDepth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Cutoff(T node)
|
||||||
|
{
|
||||||
|
return node.Depth >= maxDepth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,11 @@
|
|||||||
using System;
|
namespace SpriteCompiler.AI
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace SpriteCompiler.AI
|
|
||||||
{
|
{
|
||||||
public class GraphSearch<A, S, T, C> : AbstractAISearch<A, S, T, C>
|
using System.Collections.Generic;
|
||||||
|
using Queue;
|
||||||
|
|
||||||
|
public class GraphSearch<A, S, T, C> : AbstractSearchStrategy<A, S, T, C>
|
||||||
where T : ISearchNode<A, S, T, C>
|
where T : ISearchNode<A, S, T, C>
|
||||||
where C : IPathCost<C>
|
where C : ICost<C>
|
||||||
{
|
{
|
||||||
private readonly ISet<S> closed = new HashSet<S>();
|
private readonly ISet<S> closed = new HashSet<S>();
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ namespace SpriteCompiler.AI
|
|||||||
if (!closed.Contains(node.State))
|
if (!closed.Contains(node.State))
|
||||||
{
|
{
|
||||||
closed.Add(node.State);
|
closed.Add(node.State);
|
||||||
fringe.AddRange(Expand(problem, node));
|
fringe.AddRange(Expander.Expand(problem, node));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,14 @@
|
|||||||
{
|
{
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
public class HeuristicSearchNode<A, S, T, C> : AbstractSearchNode<A, S, T, C>, ISearchNode<A, S, T, C>
|
public interface IHeuristicSearchNode<A, S, T, C> : ISearchNode<A, S, T, C> where C : ICost<C>
|
||||||
where T : HeuristicSearchNode<A, S, T, C>
|
{
|
||||||
where C : IPathCost<C>, new()
|
C EstCost { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HeuristicSearchNode<A, S, T, C> : AbstractSearchNode<A, S, T, C>, IHeuristicSearchNode<A, S, T, C>
|
||||||
|
where T : IHeuristicSearchNode<A, S, T, C>
|
||||||
|
where C : ICost<C>, new()
|
||||||
{
|
{
|
||||||
public HeuristicSearchNode(T node, S state)
|
public HeuristicSearchNode(T node, S state)
|
||||||
: base(node, state)
|
: base(node, state)
|
||||||
@ -14,7 +19,7 @@
|
|||||||
|
|
||||||
public C Heuristic { get; set; }
|
public C Heuristic { get; set; }
|
||||||
|
|
||||||
public override C EstCost
|
public C EstCost
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{
|
{
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
public interface IHeuristicFunction<S, C> where C : IPathCost<C>
|
public interface IHeuristicFunction<S, C> where C : ICost<C>
|
||||||
{
|
{
|
||||||
C Eval(S state);
|
C Eval(S state);
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,11 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface INodeExpander<A, S, T, C>
|
public interface INodeExpander<A, S, T, C>
|
||||||
where T : ISearchNode<A, S, T, C>
|
where T : ISearchNode<A, S, T, C>
|
||||||
where C : IPathCost<C>
|
where C : ICost<C>
|
||||||
{
|
{
|
||||||
IEnumerable<T> Expand(ISearchProblem<A, S, C> problem, T node);
|
IEnumerable<T> Expand(ISearchProblem<A, S, C> problem, T node);
|
||||||
|
|
||||||
T CreateNode(T parent, S state);
|
T CreateNode(T parent, S state);
|
||||||
|
T CreateNode(S state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{
|
{
|
||||||
public interface INodeLimiter<T, C>
|
public interface INodeLimiter<T, C>
|
||||||
where T : ISearchNode<C>
|
where T : ISearchNode<C>
|
||||||
where C : IPathCost<C>, new()
|
where C : ICost<C>, new()
|
||||||
{
|
{
|
||||||
bool Cutoff(T node);
|
bool Cutoff(T node);
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,13 @@
|
|||||||
{
|
{
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
public interface IPathCost<C> : IComparable<C>
|
public interface ICost<C> : IComparable<C>
|
||||||
{
|
{
|
||||||
C Add(C value);
|
C Add(C value);
|
||||||
|
|
||||||
|
// Number theoretic values, i.e. C + ZERO = C, C * ONE = C
|
||||||
|
C Zero();
|
||||||
|
C One();
|
||||||
|
C Maximum();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,23 +4,11 @@
|
|||||||
|
|
||||||
public interface ISearch<A, S, T, C>
|
public interface ISearch<A, S, T, C>
|
||||||
where T : ISearchNode<A, S, T, C>
|
where T : ISearchNode<A, S, T, C>
|
||||||
where C : IPathCost<C>
|
where C : ICost<C>
|
||||||
{
|
{
|
||||||
/// Perform a new search on the specified search problem using the given
|
/// Perform a new search on the specified search problem using the given
|
||||||
/// initial state as a starting point. The method will return an empty
|
/// initial state as a starting point. The method will return an empty
|
||||||
/// list on failure.
|
/// list on failure.
|
||||||
IEnumerable<T> Search(ISearchProblem<A, S, C> problem, S initialState);
|
IEnumerable<T> Search(ISearchProblem<A, S, C> 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<T> ExtendSearch(ISearchProblem<A, S, C> problem);
|
|
||||||
|
|
||||||
void InitializeSearch(S initialState);
|
|
||||||
ISearchStepInfo<T> SearchStep(ISearchProblem<A, S, C> problem);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,18 +9,23 @@
|
|||||||
/// <typeparam name="S">State of the search</typeparam>
|
/// <typeparam name="S">State of the search</typeparam>
|
||||||
/// <typeparam name="T">Type of the parent</typeparam>
|
/// <typeparam name="T">Type of the parent</typeparam>
|
||||||
/// <typeparam name="C">Cost type</typeparam>
|
/// <typeparam name="C">Cost type</typeparam>
|
||||||
public interface ISearchNode<A, S, T, C> : ISearchNode<C> where C : IPathCost<C>
|
public interface ISearchNode<A, S, T, C> : ISearchNode<C> where C : ICost<C>
|
||||||
{
|
{
|
||||||
A Action { get; set; }
|
A Action { get; set; }
|
||||||
S State { get; }
|
S State { get; }
|
||||||
T Parent { get; }
|
T Parent { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ISearchNode<C>
|
/// <summary>
|
||||||
|
/// Simplest representation of a seach node that just has the costs. This interface
|
||||||
|
/// is useful for certain evaluation functions
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="C"></typeparam>
|
||||||
|
public interface ISearchNode<C> where C : ICost<C>
|
||||||
{
|
{
|
||||||
C PathCost { get; }
|
C PathCost { get; }
|
||||||
C StepCost { get; set; }
|
C StepCost { get; set; }
|
||||||
int Depth { get; }
|
|
||||||
C EstCost { get; }
|
C EstCost { get; }
|
||||||
|
int Depth { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
public interface ISearchProblem<A, S, C>
|
public interface ISearchProblem<A, S, C>
|
||||||
where C : IComparable<C>
|
where C : ICost<C>
|
||||||
{
|
{
|
||||||
IEnumerable<Tuple<A, S>> Successors(S state);
|
IEnumerable<Tuple<A, S>> Successors(S state);
|
||||||
bool IsGoal(S state);
|
bool IsGoal(S state);
|
||||||
|
18
SpriteCompiler/AI/ISearchStrategy.cs
Normal file
18
SpriteCompiler/AI/ISearchStrategy.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
namespace SpriteCompiler.AI
|
||||||
|
{
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Queue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
public interface ISearchStrategy<A, S, T, C>
|
||||||
|
where T : ISearchNode<A, S, T, C>
|
||||||
|
where C : ICost<C>
|
||||||
|
{
|
||||||
|
IEnumerable<T> Search(ISearchProblem<A, S, C> problem, IQueue<T> fringe, S initialState);
|
||||||
|
INodeExpander<A, S, T, C> Expander { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,7 @@
|
|||||||
{
|
{
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
public interface IStepCostFunction<A, S, C> where C : IPathCost<C>
|
public interface IStepCostFunction<A, S, C> where C : ICost<C>
|
||||||
{
|
{
|
||||||
C StepCost(S fromState, A action, S toState);
|
C StepCost(S fromState, A action, S toState);
|
||||||
}
|
}
|
||||||
|
@ -7,16 +7,17 @@ namespace SpriteCompiler.AI
|
|||||||
|
|
||||||
public abstract class InformedNodeExpander<A, S, T, C> : INodeExpander<A, S, T, C>
|
public abstract class InformedNodeExpander<A, S, T, C> : INodeExpander<A, S, T, C>
|
||||||
where T : HeuristicSearchNode<A, S, T, C>
|
where T : HeuristicSearchNode<A, S, T, C>
|
||||||
where C : IPathCost<C>, new()
|
where C : ICost<C>, new()
|
||||||
{
|
{
|
||||||
public abstract T CreateNode(T parent, S state);
|
public abstract T CreateNode(T parent, S state);
|
||||||
|
public abstract T CreateNode(S state);
|
||||||
|
|
||||||
public IEnumerable<T> Expand(ISearchProblem<A, S, C> problem, T node)
|
public IEnumerable<T> Expand(ISearchProblem<A, S, C> problem, T node)
|
||||||
{
|
{
|
||||||
var successors = problem.Successors(node.State);
|
var successors = problem.Successors(node.State);
|
||||||
|
|
||||||
// Debug
|
// Debug
|
||||||
#if DEBUG
|
#if VERBOSE_DEBUG
|
||||||
Console.WriteLine(String.Format("There are {0} successors for {1}", successors.Count(), node));
|
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));
|
Console.WriteLine(String.Format("This node has a current path cost of {0}", node.PathCost));
|
||||||
#endif
|
#endif
|
||||||
@ -31,7 +32,7 @@ namespace SpriteCompiler.AI
|
|||||||
next.StepCost = problem.StepCost(node.State, action, state);
|
next.StepCost = problem.StepCost(node.State, action, state);
|
||||||
next.Heuristic = problem.Heuristic(state);
|
next.Heuristic = problem.Heuristic(state);
|
||||||
|
|
||||||
#if DEBUG
|
#if False
|
||||||
Console.WriteLine(" Action = " + next.Action + ", g(n') = " + next.PathCost + ", h(n') = " + next.Heuristic);
|
Console.WriteLine(" Action = " + next.Action + ", g(n') = " + next.PathCost + ", h(n') = " + next.Heuristic);
|
||||||
#endif
|
#endif
|
||||||
yield return next;
|
yield return next;
|
||||||
|
72
SpriteCompiler/AI/IntegerPathCost.cs
Normal file
72
SpriteCompiler/AI/IntegerPathCost.cs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace SpriteCompiler.AI
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Helper class to implement simple integer path costs
|
||||||
|
/// </summary>
|
||||||
|
public sealed class IntegerCost : ICost<IntegerCost>
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,13 +7,15 @@ using System.Threading.Tasks;
|
|||||||
namespace SpriteCompiler.AI
|
namespace SpriteCompiler.AI
|
||||||
{
|
{
|
||||||
public class IterativeDeepeningAStarSearch<A, S, T, C> : ISearch<A, S, T, C>
|
public class IterativeDeepeningAStarSearch<A, S, T, C> : ISearch<A, S, T, C>
|
||||||
where T : ISearchNode<A, S, T, C>
|
where T : IHeuristicSearchNode<A, S, T, C>
|
||||||
where C : IPathCost<C>, new()
|
where C : ICost<C>, new()
|
||||||
{
|
{
|
||||||
protected readonly AbstractAISearch<A, S, T, C> search;
|
private static readonly C Cost = new C();
|
||||||
|
|
||||||
|
protected readonly ISearchStrategy<A, S, T, C> search;
|
||||||
protected readonly C limit;
|
protected readonly C limit;
|
||||||
|
|
||||||
public IterativeDeepeningAStarSearch(AbstractAISearch<A, S, T, C> search, C limit)
|
public IterativeDeepeningAStarSearch(ISearchStrategy<A, S, T, C> search, C limit)
|
||||||
{
|
{
|
||||||
this.search = search;
|
this.search = search;
|
||||||
this.limit = limit;
|
this.limit = limit;
|
||||||
@ -21,35 +23,31 @@ namespace SpriteCompiler.AI
|
|||||||
|
|
||||||
public IEnumerable<T> Search(ISearchProblem<A, S, C> problem, S initialState)
|
public IEnumerable<T> Search(ISearchProblem<A, S, C> problem, S initialState)
|
||||||
{
|
{
|
||||||
C bound = new C();
|
C bound = Cost.Zero();
|
||||||
while (bound.CompareTo(limit) < 0)
|
while (bound.CompareTo(limit) < 0)
|
||||||
{
|
{
|
||||||
var dls = new DepthLimitedSearch<A, S, T, C>(search, new CostNodeLimiter<T, C>(bound));
|
var limiter = new CostNodeLimiter<T, C>(bound, Cost.Maximum());
|
||||||
|
var dls = new DepthLimitedSearch<A, S, T, C>(search, limiter);
|
||||||
var result = dls.Search(problem, initialState);
|
var result = dls.Search(problem, initialState);
|
||||||
|
|
||||||
|
// If there was no cutoff, return the solution (or lack thereof)
|
||||||
if (!dls.IsCutoff(result))
|
if (!dls.IsCutoff(result))
|
||||||
{
|
{
|
||||||
return 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
|
// An empty list signals failure
|
||||||
return Enumerable.Empty<T>();
|
return Enumerable.Empty<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<T> ExtendSearch(ISearchProblem<A, S, C> problem)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InitializeSearch(S initialState)
|
|
||||||
{
|
|
||||||
search.InitializeSearch(initialState);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISearchStepInfo<T> SearchStep(ISearchProblem<A, S, C> problem)
|
|
||||||
{
|
|
||||||
return search.SearchStep(problem);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
38
SpriteCompiler/AI/IterativeDeepeningSearch.cs
Normal file
38
SpriteCompiler/AI/IterativeDeepeningSearch.cs
Normal file
@ -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<A, S, T, C> : ISearch<A, S, T, C>
|
||||||
|
where T : ISearchNode<A, S, T, C>
|
||||||
|
where C : ICost<C>, new()
|
||||||
|
{
|
||||||
|
private readonly ISearchStrategy<A, S, T, C> search;
|
||||||
|
private readonly int limit;
|
||||||
|
|
||||||
|
public IterativeDeepeningSearch(ISearchStrategy<A, S, T, C> search, int limit)
|
||||||
|
{
|
||||||
|
this.search = search;
|
||||||
|
this.limit = limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<T> Search(ISearchProblem<A, S, C> problem, S initialState)
|
||||||
|
{
|
||||||
|
for (int depth = 1; depth <= limit; depth++)
|
||||||
|
{
|
||||||
|
var dls = new DepthLimitedSearch<A, S, T, C>(search, new DepthNodeLimiter<T, C>(depth));
|
||||||
|
var result = dls.Search(problem, initialState);
|
||||||
|
|
||||||
|
if (!dls.IsCutoff(result))
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Enumerable.Empty<T>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
public class NodeExpanderDelegator<A, S, T, C> : INodeExpander<A, S, T, C>
|
public class NodeExpanderDelegator<A, S, T, C> : INodeExpander<A, S, T, C>
|
||||||
where T : ISearchNode<A, S, T, C>
|
where T : ISearchNode<A, S, T, C>
|
||||||
where C : IPathCost<C>
|
where C : ICost<C>
|
||||||
{
|
{
|
||||||
private readonly INodeExpander<A, S, T, C> expander;
|
private readonly INodeExpander<A, S, T, C> expander;
|
||||||
|
|
||||||
@ -22,5 +22,10 @@
|
|||||||
{
|
{
|
||||||
return expander.CreateNode(parent, state);
|
return expander.CreateNode(parent, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual T CreateNode(S state)
|
||||||
|
{
|
||||||
|
return expander.CreateNode(state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
37
SpriteCompiler/AI/Queue/FIFO.cs
Normal file
37
SpriteCompiler/AI/Queue/FIFO.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
namespace SpriteCompiler.AI.Queue
|
||||||
|
{
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
public class FIFO<T> : IQueue<T>
|
||||||
|
{
|
||||||
|
private readonly Queue<T> queue = new Queue<T>();
|
||||||
|
|
||||||
|
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<T> items)
|
||||||
|
{
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
queue.Enqueue(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,9 @@
|
|||||||
namespace SpriteCompiler.AI
|
namespace SpriteCompiler.AI.Queue
|
||||||
{
|
{
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
||||||
public interface IQueue<T>
|
public interface IQueue<T>
|
||||||
{
|
{
|
||||||
void Clear();
|
void Clear();
|
@ -1,12 +1,8 @@
|
|||||||
using System;
|
namespace SpriteCompiler.AI.Queue
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace SpriteCompiler.AI
|
|
||||||
{
|
{
|
||||||
public class Lifo<T> : IQueue<T>
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
public class LIFO<T> : IQueue<T>
|
||||||
{
|
{
|
||||||
private readonly Stack<T> stack = new Stack<T>();
|
private readonly Stack<T> stack = new Stack<T>();
|
||||||
|
|
@ -7,13 +7,18 @@ using System.Threading.Tasks;
|
|||||||
namespace SpriteCompiler.AI
|
namespace SpriteCompiler.AI
|
||||||
{
|
{
|
||||||
public class SearchProblem<A, S, C> : ISearchProblem<A, S, C>
|
public class SearchProblem<A, S, C> : ISearchProblem<A, S, C>
|
||||||
where C : IPathCost<C>
|
where C : ICost<C>, new()
|
||||||
{
|
{
|
||||||
private readonly IGoalTest<S> goalTest;
|
private readonly IGoalTest<S> goalTest;
|
||||||
private readonly IStepCostFunction<A, S, C> stepCost;
|
private readonly IStepCostFunction<A, S, C> stepCost;
|
||||||
private readonly ISuccessorFunction<A, S> successorFn;
|
private readonly ISuccessorFunction<A, S> successorFn;
|
||||||
private readonly IHeuristicFunction<S, C> heuristicFn;
|
private readonly IHeuristicFunction<S, C> heuristicFn;
|
||||||
|
|
||||||
|
public SearchProblem(IGoalTest<S> goalTest, IStepCostFunction<A, S, C> stepCost, ISuccessorFunction<A, S> successor)
|
||||||
|
: this(goalTest, stepCost, successor, null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public SearchProblem(IGoalTest<S> goalTest, IStepCostFunction<A, S, C> stepCost, ISuccessorFunction<A, S> successor, IHeuristicFunction<S, C> heuristicFn)
|
public SearchProblem(IGoalTest<S> goalTest, IStepCostFunction<A, S, C> stepCost, ISuccessorFunction<A, S> successor, IHeuristicFunction<S, C> heuristicFn)
|
||||||
{
|
{
|
||||||
this.goalTest = goalTest;
|
this.goalTest = goalTest;
|
||||||
@ -39,6 +44,11 @@ namespace SpriteCompiler.AI
|
|||||||
|
|
||||||
public C Heuristic(S state)
|
public C Heuristic(S state)
|
||||||
{
|
{
|
||||||
|
if (heuristicFn == null)
|
||||||
|
{
|
||||||
|
return new C();
|
||||||
|
}
|
||||||
|
|
||||||
return heuristicFn.Eval(state);
|
return heuristicFn.Eval(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
using System;
|
namespace SpriteCompiler.AI
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace SpriteCompiler.AI
|
|
||||||
{
|
{
|
||||||
public class TreeSearch<A, S, T, C> : AbstractAISearch<A, S, T, C>
|
using Queue;
|
||||||
|
|
||||||
|
public class TreeSearch<A, S, T, C> : AbstractSearchStrategy<A, S, T, C>
|
||||||
where T : ISearchNode<A, S, T, C>
|
where T : ISearchNode<A, S, T, C>
|
||||||
where C : IPathCost<C>
|
where C : ICost<C>
|
||||||
{
|
{
|
||||||
public TreeSearch(INodeExpander<A, S, T, C> expander)
|
public TreeSearch(INodeExpander<A, S, T, C> expander)
|
||||||
: base(expander)
|
: base(expander)
|
||||||
@ -17,7 +16,7 @@ namespace SpriteCompiler.AI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected override void AddNodes(IQueue<T> fringe, T node, ISearchProblem<A, S, C> problem)
|
protected override void AddNodes(IQueue<T> fringe, T node, ISearchProblem<A, S, C> problem)
|
||||||
{
|
{
|
||||||
fringe.AddRange(Expand(problem, node));
|
fringe.AddRange(Expander.Expand(problem, node));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
{
|
{
|
||||||
using Priority_Queue;
|
using Priority_Queue;
|
||||||
using SpriteCompiler.AI;
|
using SpriteCompiler.AI;
|
||||||
|
using SpriteCompiler.AI.Queue;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -10,7 +11,7 @@
|
|||||||
|
|
||||||
public class QueueAdapter<T, C> : IQueue<T>
|
public class QueueAdapter<T, C> : IQueue<T>
|
||||||
where T : ISearchNode<C>
|
where T : ISearchNode<C>
|
||||||
where C : IComparable<C>
|
where C : ICost<C>
|
||||||
{
|
{
|
||||||
private readonly SimplePriorityQueue<T, C> queue = new SimplePriorityQueue<T, C>();
|
private readonly SimplePriorityQueue<T, C> queue = new SimplePriorityQueue<T, C>();
|
||||||
|
|
||||||
@ -20,7 +21,7 @@
|
|||||||
{
|
{
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if VERBOSE_DEBUG
|
||||||
Console.WriteLine("Enqueuing " + item + " with cost " + item.EstCost);
|
Console.WriteLine("Enqueuing " + item + " with cost " + item.EstCost);
|
||||||
#endif
|
#endif
|
||||||
queue.Enqueue(item, item.EstCost);
|
queue.Enqueue(item, item.EstCost);
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
namespace SpriteCompiler.Problem
|
|
||||||
{
|
|
||||||
using SpriteCompiler.AI;
|
|
||||||
|
|
||||||
public sealed class IntegerPathCost : IPathCost<IntegerPathCost>
|
|
||||||
{
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,7 +4,7 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
public sealed class SpriteGeneratorHeuristicFunction : IHeuristicFunction<SpriteGeneratorState, IntegerPathCost>
|
public sealed class SpriteGeneratorHeuristicFunction : IHeuristicFunction<SpriteGeneratorState, IntegerCost>
|
||||||
{
|
{
|
||||||
private static int SpanAndGapCost(int stack, int start, int end, int next)
|
private static int SpanAndGapCost(int stack, int start, int end, int next)
|
||||||
{
|
{
|
||||||
@ -39,7 +39,7 @@
|
|||||||
return gapCost + spanCost;
|
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
|
// An admissible heuistic that calculates a cost based on the gaps and runs in a sprite
|
||||||
//
|
//
|
||||||
|
@ -2,11 +2,16 @@
|
|||||||
{
|
{
|
||||||
using SpriteCompiler.AI;
|
using SpriteCompiler.AI;
|
||||||
|
|
||||||
public sealed class SpriteGeneratorNodeExpander : InformedNodeExpander<CodeSequence, SpriteGeneratorState, SpriteGeneratorSearchNode, IntegerPathCost>
|
public sealed class SpriteGeneratorNodeExpander : InformedNodeExpander<CodeSequence, SpriteGeneratorState, SpriteGeneratorSearchNode, IntegerCost>
|
||||||
{
|
{
|
||||||
public override SpriteGeneratorSearchNode CreateNode(SpriteGeneratorSearchNode parent, SpriteGeneratorState state)
|
public override SpriteGeneratorSearchNode CreateNode(SpriteGeneratorSearchNode parent, SpriteGeneratorState state)
|
||||||
{
|
{
|
||||||
return new SpriteGeneratorSearchNode(parent, state);
|
return new SpriteGeneratorSearchNode(parent, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override SpriteGeneratorSearchNode CreateNode(SpriteGeneratorState state)
|
||||||
|
{
|
||||||
|
return CreateNode(null, state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{
|
{
|
||||||
using SpriteCompiler.AI;
|
using SpriteCompiler.AI;
|
||||||
|
|
||||||
public sealed class SpriteGeneratorSearchNode : HeuristicSearchNode<CodeSequence, SpriteGeneratorState, SpriteGeneratorSearchNode, IntegerPathCost>
|
public sealed class SpriteGeneratorSearchNode : HeuristicSearchNode<CodeSequence, SpriteGeneratorState, SpriteGeneratorSearchNode, IntegerCost>
|
||||||
{
|
{
|
||||||
public SpriteGeneratorSearchNode(SpriteGeneratorSearchNode node, SpriteGeneratorState state)
|
public SpriteGeneratorSearchNode(SpriteGeneratorSearchNode node, SpriteGeneratorState state)
|
||||||
: base(node, state)
|
: base(node, state)
|
||||||
|
@ -4,33 +4,33 @@
|
|||||||
|
|
||||||
public sealed class SpriteGeneratorSearchProblem
|
public sealed class SpriteGeneratorSearchProblem
|
||||||
{
|
{
|
||||||
public static ISearchProblem<CodeSequence, SpriteGeneratorState, IntegerPathCost> CreateSearchProblem()
|
public static ISearchProblem<CodeSequence, SpriteGeneratorState, IntegerCost> CreateSearchProblem()
|
||||||
{
|
{
|
||||||
var goalTest = new SpriteGeneratorGoalTest();
|
var goalTest = new SpriteGeneratorGoalTest();
|
||||||
var stepCost = new SpriteGeneratorStepCost();
|
var stepCost = new SpriteGeneratorStepCost();
|
||||||
var successors = new SpriteGeneratorSuccessorFunction();
|
var successors = new SpriteGeneratorSuccessorFunction();
|
||||||
var heuristic = new SpriteGeneratorHeuristicFunction();
|
var heuristic = new SpriteGeneratorHeuristicFunction();
|
||||||
|
|
||||||
return new SearchProblem<CodeSequence, SpriteGeneratorState, IntegerPathCost>(goalTest, stepCost, successors, heuristic);
|
return new SearchProblem<CodeSequence, SpriteGeneratorState, IntegerCost>(goalTest, stepCost, successors, heuristic);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ISearch<CodeSequence, SpriteGeneratorState, SpriteGeneratorSearchNode, IntegerPathCost> Create()
|
public static ISearch<CodeSequence, SpriteGeneratorState, SpriteGeneratorSearchNode, IntegerCost> Create()
|
||||||
{
|
{
|
||||||
var expander = new SpriteGeneratorNodeExpander();
|
var expander = new SpriteGeneratorNodeExpander();
|
||||||
//var strategy = new TreeSearch<CodeSequence, SpriteGeneratorState, SpriteGeneratorSearchNode, IntegerPathCost>(expander);
|
var strategy = new TreeSearch<CodeSequence, SpriteGeneratorState, SpriteGeneratorSearchNode, IntegerCost>(expander);
|
||||||
var strategy = new GraphSearch<CodeSequence, SpriteGeneratorState, SpriteGeneratorSearchNode, IntegerPathCost>(expander);
|
var queue = new Adapters.QueueAdapter<SpriteGeneratorSearchNode, IntegerCost>();
|
||||||
|
|
||||||
return new AStarSearch<CodeSequence, SpriteGeneratorState, SpriteGeneratorSearchNode, IntegerPathCost>(strategy);
|
return new AStarSearch<CodeSequence, SpriteGeneratorState, SpriteGeneratorSearchNode, IntegerCost>(strategy, queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ISearch<CodeSequence, SpriteGeneratorState, SpriteGeneratorSearchNode, IntegerPathCost> Create(int maxCycles)
|
public static ISearch<CodeSequence, SpriteGeneratorState, SpriteGeneratorSearchNode, IntegerCost> Create(int maxCycles)
|
||||||
{
|
{
|
||||||
var expander = new SpriteGeneratorNodeExpander();
|
var expander = new SpriteGeneratorNodeExpander();
|
||||||
//var strategy = new TreeSearch<CodeSequence, SpriteGeneratorState, SpriteGeneratorSearchNode, IntegerPathCost>(expander);
|
var strategy = new TreeSearch<CodeSequence, SpriteGeneratorState, SpriteGeneratorSearchNode, IntegerCost>(expander);
|
||||||
var strategy = new GraphSearch<CodeSequence, SpriteGeneratorState, SpriteGeneratorSearchNode, IntegerPathCost>(expander);
|
var queue = new Adapters.QueueAdapter<SpriteGeneratorSearchNode, IntegerCost>();
|
||||||
|
|
||||||
var maxCost = (IntegerPathCost)maxCycles;
|
var maxCost = (IntegerCost)maxCycles;
|
||||||
return new IterativeDeepeningAStarSearch<CodeSequence, SpriteGeneratorState, SpriteGeneratorSearchNode, IntegerPathCost>(strategy, maxCost);
|
return new IterativeDeepeningAStarSearch<CodeSequence, SpriteGeneratorState, SpriteGeneratorSearchNode, IntegerCost>(strategy, maxCost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
{
|
{
|
||||||
using SpriteCompiler.AI;
|
using SpriteCompiler.AI;
|
||||||
|
|
||||||
public sealed class SpriteGeneratorStepCost : IStepCostFunction<CodeSequence, SpriteGeneratorState, IntegerPathCost>
|
public sealed class SpriteGeneratorStepCost : IStepCostFunction<CodeSequence, SpriteGeneratorState, IntegerCost>
|
||||||
{
|
{
|
||||||
public IntegerPathCost StepCost(SpriteGeneratorState fromState, CodeSequence action, SpriteGeneratorState toState)
|
public IntegerCost StepCost(SpriteGeneratorState fromState, CodeSequence action, SpriteGeneratorState toState)
|
||||||
{
|
{
|
||||||
return action.CycleCount;
|
return action.CycleCount;
|
||||||
}
|
}
|
||||||
|
@ -179,6 +179,7 @@
|
|||||||
initialState = SpriteGeneratorState.Init(data);
|
initialState = SpriteGeneratorState.Init(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
IEnumerable<SpriteGeneratorSearchNode> solution = null;
|
IEnumerable<SpriteGeneratorSearchNode> solution = null;
|
||||||
if (verbose)
|
if (verbose)
|
||||||
{
|
{
|
||||||
@ -194,10 +195,11 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
solution = search.Search(problem, initialState);
|
solution = search.Search(problem, fringe, initialState);
|
||||||
}
|
}
|
||||||
|
|
||||||
WriteOutSolution(solution);
|
WriteOutSolution(solution);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,12 +53,13 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Adapters\QueueAdapter.cs" />
|
<Compile Include="Adapters\QueueAdapter.cs" />
|
||||||
<Compile Include="AI\AbstractAISearch.cs" />
|
<Compile Include="AI\AbstractSearchStrategy.cs" />
|
||||||
<Compile Include="AI\AbstractSearchNode.cs" />
|
<Compile Include="AI\AbstractSearchNode.cs" />
|
||||||
<Compile Include="AI\AbstractStateSpaceSearch.cs" />
|
<Compile Include="AI\AbstractStateSpaceSearch.cs" />
|
||||||
<Compile Include="AI\AStarComparer.cs" />
|
<Compile Include="AI\AStarComparer.cs" />
|
||||||
<Compile Include="AI\AStarSearch.cs" />
|
<Compile Include="AI\AStarSearch.cs" />
|
||||||
<Compile Include="AI\BestFirstSearch.cs" />
|
<Compile Include="AI\BestFirstSearch.cs" />
|
||||||
|
<Compile Include="AI\DepthNodeLimiter.cs" />
|
||||||
<Compile Include="AI\CostNodeLimiter.cs" />
|
<Compile Include="AI\CostNodeLimiter.cs" />
|
||||||
<Compile Include="AI\DepthFirstSearch.cs" />
|
<Compile Include="AI\DepthFirstSearch.cs" />
|
||||||
<Compile Include="AI\DepthLimitedNodeExpander.cs" />
|
<Compile Include="AI\DepthLimitedNodeExpander.cs" />
|
||||||
@ -68,23 +69,26 @@
|
|||||||
<Compile Include="AI\InformedNodeExpander.cs" />
|
<Compile Include="AI\InformedNodeExpander.cs" />
|
||||||
<Compile Include="AI\INodeLimiter.cs" />
|
<Compile Include="AI\INodeLimiter.cs" />
|
||||||
<Compile Include="AI\ISearchStepInfo.cs" />
|
<Compile Include="AI\ISearchStepInfo.cs" />
|
||||||
|
<Compile Include="AI\ISearchStrategy.cs" />
|
||||||
<Compile Include="AI\ISuccessorFunction.cs" />
|
<Compile Include="AI\ISuccessorFunction.cs" />
|
||||||
<Compile Include="AI\IGoalTest.cs" />
|
<Compile Include="AI\IGoalTest.cs" />
|
||||||
<Compile Include="AI\INodeExpander.cs" />
|
<Compile Include="AI\INodeExpander.cs" />
|
||||||
<Compile Include="AI\IPathCost.cs" />
|
<Compile Include="AI\IPathCost.cs" />
|
||||||
<Compile Include="AI\IQueue.cs" />
|
<Compile Include="AI\Queue\IQueue.cs" />
|
||||||
<Compile Include="AI\ISearch.cs" />
|
<Compile Include="AI\ISearch.cs" />
|
||||||
<Compile Include="AI\ISearchNode.cs" />
|
<Compile Include="AI\ISearchNode.cs" />
|
||||||
<Compile Include="AI\ISearchProblem.cs" />
|
<Compile Include="AI\ISearchProblem.cs" />
|
||||||
<Compile Include="AI\IStepCostFunction.cs" />
|
<Compile Include="AI\IStepCostFunction.cs" />
|
||||||
<Compile Include="AI\IterativeDeepeningAStarSearch.cs" />
|
<Compile Include="AI\IterativeDeepeningAStarSearch.cs" />
|
||||||
<Compile Include="AI\Lifo.cs" />
|
<Compile Include="AI\IterativeDeepeningSearch.cs" />
|
||||||
|
<Compile Include="AI\Queue\FIFO.cs" />
|
||||||
|
<Compile Include="AI\Queue\LIFO.cs" />
|
||||||
<Compile Include="AI\NodeExpanderDelegator.cs" />
|
<Compile Include="AI\NodeExpanderDelegator.cs" />
|
||||||
<Compile Include="AI\SearchProblem.cs" />
|
<Compile Include="AI\SearchProblem.cs" />
|
||||||
<Compile Include="AI\GraphSearch.cs" />
|
<Compile Include="AI\GraphSearch.cs" />
|
||||||
<Compile Include="AI\TreeSearch.cs" />
|
<Compile Include="AI\TreeSearch.cs" />
|
||||||
<Compile Include="Problem\CodeSequence.cs" />
|
<Compile Include="Problem\CodeSequence.cs" />
|
||||||
<Compile Include="Problem\IntegerPathCost.cs" />
|
<Compile Include="AI\IntegerPathCost.cs" />
|
||||||
<Compile Include="Problem\Register.cs" />
|
<Compile Include="Problem\Register.cs" />
|
||||||
<Compile Include="Problem\SpriteByte.cs" />
|
<Compile Include="Problem\SpriteByte.cs" />
|
||||||
<Compile Include="Problem\SpriteGeneratorGoalTest.cs" />
|
<Compile Include="Problem\SpriteGeneratorGoalTest.cs" />
|
||||||
|
Loading…
Reference in New Issue
Block a user