From 69599c5643b07d42a83e8057d0f1190021aa91f0 Mon Sep 17 00:00:00 2001 From: Lucas Scharenbroich Date: Sun, 18 Dec 2016 00:03:23 -0600 Subject: [PATCH] Additional tests --- AI.Test/SearchTest.cs | 39 ------- SpriteCompiler.Test/MarioTests.cs | 26 +++-- .../SpriteCompiler.Test.csproj | 1 + SpriteCompiler.Test/SuccessorTests.cs | 35 ++++++ SpriteCompiler.Test/Tests.cs | 102 ++++++++++++++---- SpriteCompiler/AI/AbstractSearchNode.cs | 14 +++ SpriteCompiler/AI/CostNodeLimiter.cs | 2 + 7 files changed, 155 insertions(+), 64 deletions(-) create mode 100644 SpriteCompiler.Test/SuccessorTests.cs diff --git a/AI.Test/SearchTest.cs b/AI.Test/SearchTest.cs index 93cb137..a6ffeec 100644 --- a/AI.Test/SearchTest.cs +++ b/AI.Test/SearchTest.cs @@ -95,45 +95,6 @@ namespace AI.Test } } - 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) diff --git a/SpriteCompiler.Test/MarioTests.cs b/SpriteCompiler.Test/MarioTests.cs index 56ae88f..7c1e7b7 100644 --- a/SpriteCompiler.Test/MarioTests.cs +++ b/SpriteCompiler.Test/MarioTests.cs @@ -5,6 +5,7 @@ using SpriteCompiler.Problem; using SpriteCompiler.AI; using System.Diagnostics; using System.Collections.Generic; +using FluentAssertions; namespace SpriteCompiler.Test { @@ -53,10 +54,15 @@ namespace SpriteCompiler.Test }; // Act : solve the problem - var solution = search.Search(problem, SpriteGeneratorState.Init(sprite)); + var initialState = SpriteGeneratorState.Init(sprite); + var initialHeuristic = problem.Heuristic(initialState); - // Assert : The initial state IS the goal state - WriteOutSolution(solution); + var solution = search.Search(problem, initialState); + + // Assert : The initial state IS the goal state (47 cycles is the currrent best solution) + WriteOutSolution(solution, initialHeuristic); + + initialHeuristic.Should().BeLessOrEqualTo(solution.Last().PathCost); } [TestMethod] @@ -64,7 +70,7 @@ namespace SpriteCompiler.Test { // Arrange var problem = SpriteGeneratorSearchProblem.CreateSearchProblem(); - var search = SpriteGeneratorSearchProblem.Create(); // max budget of 80 cycles + var search = SpriteGeneratorSearchProblem.Create(80); // max budget of 80 cycles var sprite = new List { new SpriteByte(0x11, 0x00, 3), @@ -84,13 +90,16 @@ namespace SpriteCompiler.Test }; // Act : solve the problem - var solution = search.Search(problem, SpriteGeneratorState.Init(sprite)); + var initialState = SpriteGeneratorState.Init(sprite); + var initialHeuristic = problem.Heuristic(initialState); + + var solution = search.Search(problem, initialState); // Assert : The initial state IS the goal state WriteOutSolution(solution); } - private void WriteOutSolution(IEnumerable solution) + private void WriteOutSolution(IEnumerable solution, IntegerCost h0 = null) { foreach (var step in solution.Skip(1)) { @@ -98,6 +107,11 @@ namespace SpriteCompiler.Test } Trace.WriteLine(string.Format("; Total Cost = {0} cycles", (int)solution.Last().PathCost)); + + if (h0 != null) + { + Trace.WriteLine(string.Format("; h(0) = {0} cycles", (int)h0)); + } } } } diff --git a/SpriteCompiler.Test/SpriteCompiler.Test.csproj b/SpriteCompiler.Test/SpriteCompiler.Test.csproj index 9ccf4a5..a2c708d 100644 --- a/SpriteCompiler.Test/SpriteCompiler.Test.csproj +++ b/SpriteCompiler.Test/SpriteCompiler.Test.csproj @@ -60,6 +60,7 @@ + diff --git a/SpriteCompiler.Test/SuccessorTests.cs b/SpriteCompiler.Test/SuccessorTests.cs new file mode 100644 index 0000000..63cb7bf --- /dev/null +++ b/SpriteCompiler.Test/SuccessorTests.cs @@ -0,0 +1,35 @@ +using System; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using SpriteCompiler.Problem; +using SpriteCompiler.AI; +using System.Diagnostics; +using System.Collections.Generic; +using FluentAssertions; + +namespace SpriteCompiler.Test +{ + [TestClass] + public class SuccessorTests + { + private SpriteGeneratorSuccessorFunction successor = new SpriteGeneratorSuccessorFunction(); + + [TestMethod] + public void TestInitialState() + { + var data = new[] + { + new SpriteByte(0x11, 0), + new SpriteByte(0x11, 160), + new SpriteByte(0x11, 320) + }; + + var state = SpriteGeneratorState.Init(data); + var successors = successor.Successors(state); + + // Should pick only the first data item + Assert.AreEqual(1, successors.Count()); + Assert.AreEqual(successors.First().Item1.GetType(), typeof(MOVE_STACK)); + } + } +} \ No newline at end of file diff --git a/SpriteCompiler.Test/Tests.cs b/SpriteCompiler.Test/Tests.cs index 43ec52a..1530d90 100644 --- a/SpriteCompiler.Test/Tests.cs +++ b/SpriteCompiler.Test/Tests.cs @@ -5,6 +5,7 @@ using SpriteCompiler.Problem; using SpriteCompiler.AI; using System.Diagnostics; using System.Collections.Generic; +using FluentAssertions; namespace SpriteCompiler.Test { @@ -19,9 +20,15 @@ namespace SpriteCompiler.Test var search = SpriteGeneratorSearchProblem.Create(); // Act : solve the problem - var solution = search.Search(problem, SpriteGeneratorState.Init(Enumerable.Empty())); + var initialState = SpriteGeneratorState.Init(Enumerable.Empty()); + var initialHeuristic = problem.Heuristic(initialState); + + var solution = search.Search(problem, initialState); // Assert : The initial state IS the goal state + WriteOutSolution(solution, initialHeuristic); + + initialHeuristic.Should().BeLessOrEqualTo(solution.Last().PathCost); Assert.AreEqual(1, solution.Count()); } @@ -35,7 +42,12 @@ namespace SpriteCompiler.Test var search = SpriteGeneratorSearchProblem.Create(); // Act : solve the problem - var solution = search.Search(problem, SpriteGeneratorState.Init(new byte[] { 0xAA })); + var data = new byte[] { 0xAA }; + + var initialState = SpriteGeneratorState.Init(data); + var initialHeuristic = problem.Heuristic(initialState); + + var solution = search.Search(problem, initialState); // Assert // @@ -48,8 +60,9 @@ namespace SpriteCompiler.Test // LONG A = 13 cycles // Write out the solution - WriteOutSolution(solution); + WriteOutSolution(solution, initialHeuristic); + initialHeuristic.Should().BeLessOrEqualTo(solution.Last().PathCost); Assert.AreEqual(5, solution.Count()); Assert.AreEqual(13, (int)solution.Last().PathCost); } @@ -62,7 +75,12 @@ namespace SpriteCompiler.Test var search = SpriteGeneratorSearchProblem.Create(); // Act : solve the problem - var solution = search.Search(problem, SpriteGeneratorState.Init(new byte[] { 0xAA, 0x55 })); + var data = new byte[] { 0xAA, 0x55 }; + + var initialState = SpriteGeneratorState.Init(data); + var initialHeuristic = problem.Heuristic(initialState); + + var solution = search.Search(problem, initialState); // Assert // @@ -71,10 +89,17 @@ namespace SpriteCompiler.Test // TCS // LDA #$55AA // STA 0,s = 10 cycles - + // + // Alternate + // + // ADC #1 + // TCS + // PEA #$55AA = 10 cycles + // Write out the solution - WriteOutSolution(solution); + WriteOutSolution(solution, initialHeuristic); + initialHeuristic.Should().BeLessOrEqualTo(solution.Last().PathCost); Assert.AreEqual(3, solution.Count()); Assert.AreEqual(10, (int)solution.Last().PathCost); } @@ -87,7 +112,12 @@ namespace SpriteCompiler.Test var search = SpriteGeneratorSearchProblem.Create(); // Act : solve the problem - var solution = search.Search(problem, SpriteGeneratorState.Init(new byte[] { 0x11, 0x22, 0x22 })); + var data = new byte[] { 0x11, 0x22, 0x22 }; + + var initialState = SpriteGeneratorState.Init(data); + var initialHeuristic = problem.Heuristic(initialState); + + var solution = search.Search(problem, initialState); // Assert // @@ -100,8 +130,9 @@ namespace SpriteCompiler.Test // STA 1,s = 18 cycles // Write out the solution - WriteOutSolution(solution); + WriteOutSolution(solution, initialHeuristic); + initialHeuristic.Should().BeLessOrEqualTo(solution.Last().PathCost); Assert.AreEqual(18, (int)solution.Last().PathCost); } @@ -116,7 +147,10 @@ namespace SpriteCompiler.Test var data = new byte[] { 0x00, 0x11 }; var mask = new byte[] { 0xFF, 0x00 }; - var solution = search.Search(problem, SpriteGeneratorState.Init(data, mask)); + var initialState = SpriteGeneratorState.Init(data, mask); + var initialHeuristic = problem.Heuristic(initialState); + + var solution = search.Search(problem, initialState); // Assert // @@ -124,8 +158,9 @@ namespace SpriteCompiler.Test // the store happens at offset 1, instead of 0. // Write out the solution - WriteOutSolution(solution); + WriteOutSolution(solution, initialHeuristic); + initialHeuristic.Should().BeLessOrEqualTo(solution.Last().PathCost); Assert.AreEqual(5, solution.Count()); Assert.AreEqual(14, (int)solution.Last().PathCost); } @@ -141,7 +176,10 @@ namespace SpriteCompiler.Test var data = new byte[] { 0x01, 0x11 }; var mask = new byte[] { 0xF0, 0x00 }; - var solution = search.Search(problem, SpriteGeneratorState.Init(data, mask)); + var initialState = SpriteGeneratorState.Init(data, mask); + var initialHeuristic = problem.Heuristic(initialState); + + var solution = search.Search(problem, initialState); // Assert // @@ -154,8 +192,9 @@ namespace SpriteCompiler.Test // STA 0,s = 18 cycles // Write out the solution - WriteOutSolution(solution); + WriteOutSolution(solution, initialHeuristic); + initialHeuristic.Should().BeLessOrEqualTo(solution.Last().PathCost); Assert.AreEqual(18, (int)solution.Last().PathCost); } @@ -167,7 +206,12 @@ namespace SpriteCompiler.Test var search = SpriteGeneratorSearchProblem.Create(); // Act : solve the problem - var solution = search.Search(problem, SpriteGeneratorState.Init(new byte[] { 0xAA, 0x55, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 })); + var data = new byte[] { 0xAA, 0x55, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 }; + + var initialState = SpriteGeneratorState.Init(data); + var initialHeuristic = problem.Heuristic(initialState); + + var solution = search.Search(problem, initialState); // Assert // @@ -181,8 +225,9 @@ namespace SpriteCompiler.Test // PEA $55AA = 25 cycles // Write out the solution - WriteOutSolution(solution); + WriteOutSolution(solution, initialHeuristic); + initialHeuristic.Should().BeLessOrEqualTo(solution.Last().PathCost); Assert.AreEqual(6, solution.Count()); Assert.AreEqual(25, (int)solution.Last().PathCost); } @@ -198,7 +243,10 @@ namespace SpriteCompiler.Test var data = new byte[] { 0x01, 0x11, 0x22, 0x11, 0x33 }; var mask = new byte[] { 0xF0, 0x00, 0x00, 0x00, 0x00 }; - var solution = search.Search(problem, SpriteGeneratorState.Init(data, mask)); + var initialState = SpriteGeneratorState.Init(data, mask); + var initialHeuristic = problem.Heuristic(initialState); + + var solution = search.Search(problem, initialState); // Assert // @@ -214,8 +262,9 @@ namespace SpriteCompiler.Test // STA 0,s = 31 cycles // Write out the solution - WriteOutSolution(solution); + WriteOutSolution(solution, initialHeuristic); + initialHeuristic.Should().BeLessOrEqualTo(solution.Last().PathCost); Assert.AreEqual(31, (int)solution.Last().PathCost); } @@ -234,7 +283,10 @@ namespace SpriteCompiler.Test new SpriteByte(0x11, 320) }; - var solution = search.Search(problem, SpriteGeneratorState.Init(data)); + var initialState = SpriteGeneratorState.Init(data); + var initialHeuristic = problem.Heuristic(initialState); + + var solution = search.Search(problem, initialState); // Current best solution // @@ -269,19 +321,31 @@ namespace SpriteCompiler.Test //; Total Cost = 29 cycles // Write out the solution - WriteOutSolution(solution); + WriteOutSolution(solution, initialHeuristic); + initialHeuristic.Should().BeLessOrEqualTo(solution.Last().PathCost); Assert.AreEqual(35, (int)solution.Last().PathCost); } - private void WriteOutSolution(IEnumerable solution) + private void WriteOutSolution(IEnumerable solution, IntegerCost h0 = null) { + if (!solution.Any()) + { + Trace.WriteLine("No solution found"); + return; + } + foreach (var step in solution.Skip(1)) { Trace.WriteLine(step.Action.Emit()); } Trace.WriteLine(string.Format("; Total Cost = {0} cycles", (int)solution.Last().PathCost)); + + if (h0 != null) + { + Trace.WriteLine(string.Format("; h(0) = {0} cycles", (int)h0)); + } } } } diff --git a/SpriteCompiler/AI/AbstractSearchNode.cs b/SpriteCompiler/AI/AbstractSearchNode.cs index 631f93e..5c5ad8e 100644 --- a/SpriteCompiler/AI/AbstractSearchNode.cs +++ b/SpriteCompiler/AI/AbstractSearchNode.cs @@ -45,5 +45,19 @@ pathCost = HasParent ? parent.PathCost.Add(value) : value; } } + + public string WriteSolution() + { + var actions = new List(); + for (ISearchNode node = this; node != null; node = node.Parent) + { + if (node.Action != null) + { + actions.Add(node.Action.ToString()); + } + } + actions.Reverse(); + return String.Join(" ", actions); + } } } diff --git a/SpriteCompiler/AI/CostNodeLimiter.cs b/SpriteCompiler/AI/CostNodeLimiter.cs index 8237a31..e0ad19c 100644 --- a/SpriteCompiler/AI/CostNodeLimiter.cs +++ b/SpriteCompiler/AI/CostNodeLimiter.cs @@ -25,6 +25,8 @@ // 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. + + // Current bug -- three line sprite, never exceeds cost of 23 if (node.PathCost.CompareTo(maxCost) > 0) { nextCost = (node.PathCost.CompareTo(nextCost) < 0) ? node.PathCost : nextCost;