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;