Clean up test framework a bit

This commit is contained in:
Lucas Scharenbroich 2017-02-24 22:51:46 -06:00
parent 69599c5643
commit b1a3bd8f4e
2 changed files with 90 additions and 47 deletions

View File

@ -8,6 +8,19 @@ namespace AI.Test
{ {
public static class DirectionExtensions public static class DirectionExtensions
{ {
public static bool IsOppositeOf(this Direction direction, Direction otherDirection)
{
switch (direction)
{
case Direction.LEFT: return otherDirection.Equals(Direction.RIGHT);
case Direction.RIGHT: return otherDirection.Equals(Direction.LEFT);
case Direction.UP: return otherDirection.Equals(Direction.DOWN);
case Direction.DOWN: return otherDirection.Equals(Direction.UP);
}
throw new ArgumentException();
}
public static bool CanMove(this Direction direction, int p) public static bool CanMove(this Direction direction, int p)
{ {
switch (direction) switch (direction)
@ -45,6 +58,9 @@ namespace AI.Test
public class EightPuzzleBoard public class EightPuzzleBoard
{ {
// The goal state is the canonical end state
public static EightPuzzleBoard GOAL = new EightPuzzleBoard();
protected static Random rng = new Random(); protected static Random rng = new Random();
@ -75,14 +91,16 @@ namespace AI.Test
public EightPuzzleBoard Scramble(int n) public EightPuzzleBoard Scramble(int n)
{ {
var newPuzzle = new EightPuzzleBoard(this); var newPuzzle = new EightPuzzleBoard(this);
var direction = Enum.GetValues(typeof(Direction)).Cast<Direction>().ToList(); var directions = Enum.GetValues(typeof(Direction)).Cast<Direction>().ToList();
var lastDirection = Direction.DOWN;
for (int i = 0; i < n;) for (int i = 0; i < n;)
{ {
int j = rng.Next(direction.Count); var direction = directions[rng.Next(directions.Count)];
if (newPuzzle.CanMoveGap(direction[j])) if (newPuzzle.CanMoveGap(direction) && (i == 0 || !direction.IsOppositeOf(lastDirection)))
{ {
newPuzzle.MoveGap(direction[j]); newPuzzle.MoveGap(direction);
lastDirection = direction;
i += 1; i += 1;
} }
} }

View File

@ -46,11 +46,9 @@ namespace AI.Test
public class EightPuzzleStepCost : IStepCostFunction<Direction, EightPuzzleBoard, IntegerCost> 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) public IntegerCost StepCost(EightPuzzleBoard fromState, Direction action, EightPuzzleBoard toState)
{ {
return UNIT_STEP_COST; return IntegerCost.ONE;
} }
} }
@ -116,18 +114,15 @@ namespace AI.Test
private ISearchProblem<Direction, EightPuzzleBoard, IntegerCost> problem_h1; private ISearchProblem<Direction, EightPuzzleBoard, IntegerCost> problem_h1;
private ISearchProblem<Direction, EightPuzzleBoard, IntegerCost> problem_h2; 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] [TestInitialize]
public void SetUp() public void SetUp()
{ {
// These objects define the abstract search problem // These objects define the abstract search problem
var goalTest = new EightPuzzleGoalTest(goal); var goalTest = new EightPuzzleGoalTest(EightPuzzleBoard.GOAL);
var stepCost = new EightPuzzleStepCost(); var stepCost = new EightPuzzleStepCost();
var successorFn = new EightPuzzleSuccessorFunction(); var successorFn = new EightPuzzleSuccessorFunction();
var heuristic1 = new MisplacedHeuristic(goal); var heuristic1 = new MisplacedHeuristic(EightPuzzleBoard.GOAL);
var heuristic2 = new ManhattanHeuristic(goal); var heuristic2 = new ManhattanHeuristic(EightPuzzleBoard.GOAL);
// Create three search problem objects. One without a heuristic and two with the different // Create three search problem objects. One without a heuristic and two with the different
// heuristics // heuristics
@ -136,11 +131,31 @@ namespace AI.Test
problem_h2 = new SearchProblem<Direction, EightPuzzleBoard, IntegerCost>(goalTest, stepCost, successorFn, heuristic2); problem_h2 = new SearchProblem<Direction, EightPuzzleBoard, IntegerCost>(goalTest, stepCost, successorFn, heuristic2);
} }
[TestMethod] private void PrintSolution(IEnumerable<EightPuzzleNode> solution)
public void TestMethod1()
{ {
int N = 1; if (solution == null)
int dmax = 3; {
System.Diagnostics.Trace.WriteLine("No solution");
return;
}
System.Diagnostics.Trace.WriteLine(solution.First().State.ToString());
foreach (var node in solution.Skip(1))
{
System.Diagnostics.Trace.WriteLine("");
System.Diagnostics.Trace.WriteLine(node.Action.ToString());
System.Diagnostics.Trace.WriteLine(node.State.ToString());
}
}
[TestMethod]
public void EightPuzzleTest()
{
// Number of trials to run
int N = 10;
// Maximum depth of the solution
int dmax = 6;
// Now we define the search algorithm and the type of node expansion. Russell & Norvig discuss // 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 // two type of expansion strategies: tree search and graph search. One will avoid cycles in the search
@ -149,51 +164,61 @@ namespace AI.Test
// They state that a tree search was used to generate Figure 4.8; // They state that a tree search was used to generate Figure 4.8;
var expander = new InstrumentedNodeExpander<Direction, EightPuzzleBoard, EightPuzzleNode, IntegerCost>(new EightPuzzleNodeExpander()); var expander = new InstrumentedNodeExpander<Direction, EightPuzzleBoard, EightPuzzleNode, IntegerCost>(new EightPuzzleNodeExpander());
var treeSearch = new TreeSearch<Direction, EightPuzzleBoard, EightPuzzleNode, IntegerCost>(expander); var treeSearch = new TreeSearch<Direction, EightPuzzleBoard, EightPuzzleNode, IntegerCost>(expander);
var ids = new IterativeDeepeningSearch<Direction, EightPuzzleBoard, EightPuzzleNode, IntegerCost>(treeSearch, dmax); var ids = new IterativeDeepeningSearch<Direction, EightPuzzleBoard, EightPuzzleNode, IntegerCost>(treeSearch, dmax);
var aStarH1 = new AStarSearch<Direction, EightPuzzleBoard, EightPuzzleNode, IntegerCost>(treeSearch, new QueueAdapter<EightPuzzleNode, IntegerCost>()); var aStarH1 = new AStarSearch<Direction, EightPuzzleBoard, EightPuzzleNode, IntegerCost>(treeSearch, new QueueAdapter<EightPuzzleNode, IntegerCost>());
var aStarH2 = new IterativeDeepeningAStarSearch<Direction, EightPuzzleBoard, EightPuzzleNode, IntegerCost>(treeSearch, (IntegerCost)dmax); var aStarH2 = new IterativeDeepeningAStarSearch<Direction, EightPuzzleBoard, EightPuzzleNode, IntegerCost>(treeSearch, (IntegerCost)dmax);
// Depth runs from 0 to dmax // Depth runs from 0 to dmax
int[,] d = new int[dmax + 2, 3]; int[,] d = new int[dmax + 1, 3];
int[,] n = new int[dmax + 2, 3]; int[,] n = new int[dmax + 1, 3];
for (int _d = 1; _d <= dmax; _d++)
{
for (int i = 0; i < N; i++) for (int i = 0; i < N; i++)
{ {
// Invoke the search on the problem with a particular starting state // Invoke the search on the problem with a particular starting state. Scramble creates a new instance
var initialState = goal.Scramble(dmax); var initialState = EightPuzzleBoard.GOAL.Scramble(_d);
/*
{ {
expander.ClearMetrics(); expander.ClearMetrics();
var solution = ids.Search(problem_none, initialState); var solution = ids.Search(problem_none, new EightPuzzleBoard(initialState));
System.Diagnostics.Trace.WriteLine("IDS Solution has " + solution.Count() + " nodes and expanded " + expander[IntrumentedParameters.NODES_EXPANDED] + " nodes"); var depth = solution.Count() - 1;
d[solution.Count(), 0] += 1;
n[solution.Count(), 0] += expander[IntrumentedParameters.NODES_EXPANDED]; System.Diagnostics.Trace.WriteLine("IDS Solution has depth " + depth + " and expanded " + expander[IntrumentedParameters.NODES_EXPANDED] + " nodes");
d[depth, 0] += 1;
n[depth, 0] += expander[IntrumentedParameters.NODES_EXPANDED];
} }
{ {
expander.ClearMetrics(); expander.ClearMetrics();
var solution = aStarH1.Search(problem_h1, initialState); var solution = aStarH1.Search(problem_h1, new EightPuzzleBoard(initialState));
System.Diagnostics.Trace.WriteLine("A* (h1) Solution has " + solution.Count() + " nodes and expanded " + expander[IntrumentedParameters.NODES_EXPANDED] + " nodes"); var depth = solution.Count() - 1;
d[solution.Count(), 1] += 1;
n[solution.Count(), 1] += expander[IntrumentedParameters.NODES_EXPANDED]; System.Diagnostics.Trace.WriteLine("A* (h1) Solution has depth " + depth + " nodes and expanded " + expander[IntrumentedParameters.NODES_EXPANDED] + " nodes");
} d[depth, 1] += 1;
*/ n[depth, 1] += expander[IntrumentedParameters.NODES_EXPANDED];
{
expander.ClearMetrics(); // PrintSolution(solution);
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];
}
} }
{
expander.ClearMetrics();
var solution = aStarH2.Search(problem_h2, new EightPuzzleBoard(initialState));
var depth = solution.Count() - 1;
System.Diagnostics.Trace.WriteLine("A* (h2) Solution has depth " + depth + " nodes and expanded " + expander[IntrumentedParameters.NODES_EXPANDED] + " nodes");
d[depth, 2] += 1;
n[depth, 2] += expander[IntrumentedParameters.NODES_EXPANDED];
}
}
}
Trace.WriteLine("| Search Cost Branching Factor |"); Trace.WriteLine("| Search Cost Branching Factor |");
Trace.WriteLine("+--+---------+--------+--------++---------+--------+--------+"); Trace.WriteLine("+--+---------+--------+--------++---------+--------+--------+");
Trace.WriteLine("| d| IDS | A*(h1) | A*(h2) || IDS | A*(h1) | A*(h2) |"); Trace.WriteLine("| d| IDS | A*(h1) | A*(h2) || IDS | A*(h1) | A*(h2) |");
Trace.WriteLine("+--+---------+--------+--------++---------+--------+--------+"); Trace.WriteLine("+--+---------+--------+--------++---------+--------+--------+");
for (int i = 0; i <= dmax + 1; i++) for (int i = 0; i <= dmax; i++)
{ {
var bf0 = ComputeBranchingFactor((float)n[i, 0] / (float)d[i, 0], 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 bf1 = ComputeBranchingFactor((float)n[i, 1] / (float)d[i, 1], i);