mirror of
https://github.com/lscharen/iigs-sprite-compiler.git
synced 2024-06-15 23:29:46 +00:00
Work toward improved heuristic functions ad add core unit tests
This commit is contained in:
parent
9d7aeb355c
commit
b7a3af9437
48
SpriteCompiler.Test/RegisterTests.cs
Normal file
48
SpriteCompiler.Test/RegisterTests.cs
Normal file
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using SpriteCompiler.Problem;
|
||||
using SpriteCompiler.AI;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SpriteCompiler.Test
|
||||
{
|
||||
[TestClass]
|
||||
public class RegisterTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void TestRegisterEquality()
|
||||
{
|
||||
var uninitialized1 = Register.UNINITIALIZED;
|
||||
var uninitialized2 = Register.UNINITIALIZED;
|
||||
|
||||
Assert.AreEqual(uninitialized1, uninitialized2);
|
||||
|
||||
var addr1 = Register.INITIAL_OFFSET;
|
||||
var addr2 = Register.INITIAL_OFFSET;
|
||||
var addr3 = Register.INITIAL_OFFSET.Add(1);
|
||||
var addr4 = addr3.Add(-1);
|
||||
|
||||
Assert.AreEqual(addr1, addr2);
|
||||
Assert.AreNotEqual(addr2, addr3);
|
||||
Assert.AreEqual(addr1, addr4);
|
||||
|
||||
var literal1 = Register.UNINITIALIZED.LoadConstant(0);
|
||||
var literal2 = Register.UNINITIALIZED.LoadConstant(1);
|
||||
var literal3 = Register.UNINITIALIZED.LoadConstant(1);
|
||||
|
||||
Assert.AreNotEqual(literal1, literal2);
|
||||
Assert.AreNotEqual(literal1, addr1);
|
||||
Assert.AreNotEqual(literal1, uninitialized1);
|
||||
Assert.AreEqual(literal2, literal3);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentException))]
|
||||
public void TestUninitializedAdd()
|
||||
{
|
||||
Register.UNINITIALIZED.Add(1);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -50,6 +50,7 @@
|
|||
</Otherwise>
|
||||
</Choose>
|
||||
<ItemGroup>
|
||||
<Compile Include="RegisterTests.cs" />
|
||||
<Compile Include="Tests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -47,11 +47,11 @@ namespace SpriteCompiler.Test
|
|||
// STA 0,s
|
||||
// LONG A = 14 cycles
|
||||
|
||||
Assert.AreEqual(5, solution.Count());
|
||||
Assert.AreEqual(14, (int)solution.Last().PathCost);
|
||||
|
||||
// Write out the solution
|
||||
WriteOutSolution(solution);
|
||||
|
||||
Assert.AreEqual(5, solution.Count());
|
||||
Assert.AreEqual(14, (int)solution.Last().PathCost);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@ -72,11 +72,39 @@ namespace SpriteCompiler.Test
|
|||
// LDA #$55AA
|
||||
// STA 0,s = 10 cycles
|
||||
|
||||
// Write out the solution
|
||||
WriteOutSolution(solution);
|
||||
|
||||
Assert.AreEqual(3, solution.Count());
|
||||
Assert.AreEqual(10, (int)solution.Last().PathCost);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestConsecutiveWordSprite()
|
||||
{
|
||||
// Arrange
|
||||
var problem = SpriteGeneratorSearchProblem.CreateSearchProblem();
|
||||
var search = SpriteGeneratorSearchProblem.Create();
|
||||
|
||||
// Act : solve the problem
|
||||
var solution = search.Search(problem, new SpriteGeneratorState(new byte[] { 0xAA, 0x55, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 }));
|
||||
|
||||
// Assert
|
||||
//
|
||||
// The fastest way to draw a consecutive workds hould be
|
||||
//
|
||||
// ADC #7
|
||||
// TCS
|
||||
// PEA $6655
|
||||
// PEA $4433
|
||||
// PEA $2211
|
||||
// PEA $55AA = 25 cycles
|
||||
|
||||
// Write out the solution
|
||||
WriteOutSolution(solution);
|
||||
|
||||
//Assert.AreEqual(3, solution.Count());
|
||||
//Assert.AreEqual(10, (int)solution.Last().PathCost);
|
||||
}
|
||||
|
||||
private void WriteOutSolution(IEnumerable<SpriteGeneratorSearchNode> solution)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{
|
||||
using System;
|
||||
|
||||
public sealed class Register
|
||||
public sealed class Register : IEquatable<Register>
|
||||
{
|
||||
public static readonly Register UNINITIALIZED = new Register(0, DataType.UNINITIALIZED);
|
||||
public static readonly Register INITIAL_OFFSET = new Register(0, DataType.SCREEN_OFFSET);
|
||||
|
@ -52,5 +52,35 @@
|
|||
{
|
||||
return string.Format("{0} ({1})", Tag, Value.ToString("X4"));
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals(obj as Register);
|
||||
}
|
||||
|
||||
public bool Equals(Register other)
|
||||
{
|
||||
if (other == null)
|
||||
return false;
|
||||
|
||||
// Unititialized is equal to unititialized. Otherwise, compare the value, too.
|
||||
return Tag.Equals(other.Tag) && (Tag.Equals(DataType.UNINITIALIZED) || Value == other.Value);
|
||||
}
|
||||
|
||||
public static bool operator ==(Register reg1, Register reg2)
|
||||
{
|
||||
if (((object)reg1) == null || ((object)reg2) == null)
|
||||
return Object.Equals(reg1, reg2);
|
||||
|
||||
return reg1.Equals(reg2);
|
||||
}
|
||||
|
||||
public static bool operator !=(Register reg1, Register reg2)
|
||||
{
|
||||
if (((object)reg1) == null || ((object)reg2) == null)
|
||||
return ! Object.Equals(reg1, reg2);
|
||||
|
||||
return ! (reg1.Equals(reg2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,64 @@
|
|||
namespace SpriteCompiler.Problem
|
||||
{
|
||||
using SpriteCompiler.AI;
|
||||
using System.Linq;
|
||||
|
||||
public sealed class SpriteGeneratorHeuristicFunction : IHeuristicFunction<SpriteGeneratorState, IntegerPathCost>
|
||||
{
|
||||
private static int SpanAndGapCost(int start, int end, int next)
|
||||
{
|
||||
// [start, end] is the span
|
||||
// (end, next) is the gap
|
||||
//
|
||||
// start <= end <= next
|
||||
var len = end - start + 1;
|
||||
|
||||
// No gap, no penalty
|
||||
var gapCost = (end == next) ? 0 : 5;
|
||||
var spanCost = 4 * (len / 2) + 3 * (len % 2);
|
||||
|
||||
return gapCost + spanCost;
|
||||
}
|
||||
|
||||
public IntegerPathCost Eval(SpriteGeneratorState state)
|
||||
{
|
||||
return 0;
|
||||
// An admissible heuistic calculates a cost based on the gaps and runs in a sprite
|
||||
//
|
||||
// An even-length run can be done, at best in 4 cycles/word
|
||||
// An odd-length run is even + 3 cycles/byte
|
||||
//
|
||||
// Each gap needs at least 5 cycles to cover (ADC # / TCS)
|
||||
|
||||
var count = state.Bytes.Count;
|
||||
|
||||
if (count == 0) return 0;
|
||||
|
||||
var offsets = state.Bytes.Select(x => x.Offset).OrderBy(x => x).ToList();
|
||||
var start = offsets[0];
|
||||
var curr = start;
|
||||
var cost = 0;
|
||||
|
||||
for (int i = 1; i < count; i++)
|
||||
{
|
||||
var prev = curr;
|
||||
curr = offsets[i];
|
||||
|
||||
if (prev == (curr - 1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate the estimate cost
|
||||
cost += SpanAndGapCost(start, prev, curr);
|
||||
|
||||
// Start a new sppan
|
||||
start = curr;
|
||||
}
|
||||
|
||||
// End with the span
|
||||
cost += SpanAndGapCost(start, curr, curr);
|
||||
|
||||
return cost;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
|
||||
public static ISearch<CodeSequence, SpriteGeneratorState, SpriteGeneratorSearchNode, IntegerPathCost> Create()
|
||||
{
|
||||
//var problem = CreateSearchProblem();
|
||||
var strategy = new TreeSearch<CodeSequence, SpriteGeneratorState, SpriteGeneratorSearchNode, IntegerPathCost>(new SpriteGeneratorNodeExpander());
|
||||
var expander = new SpriteGeneratorNodeExpander();
|
||||
var strategy = new TreeSearch<CodeSequence, SpriteGeneratorState, SpriteGeneratorSearchNode, IntegerPathCost>(expander);
|
||||
|
||||
return new AStarSearch<CodeSequence, SpriteGeneratorState, SpriteGeneratorSearchNode, IntegerPathCost>(strategy);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
public class SpriteGeneratorState
|
||||
public class SpriteGeneratorState : IEquatable<SpriteGeneratorState>
|
||||
{
|
||||
public SpriteGeneratorState()
|
||||
: this(new SpriteByte[0])
|
||||
|
@ -86,5 +86,39 @@
|
|||
public Register S { get; set; } // S is always an offset, not a literal number
|
||||
|
||||
public byte P { get; set; }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals(obj as SpriteGeneratorState);
|
||||
}
|
||||
|
||||
public bool Equals(SpriteGeneratorState other)
|
||||
{
|
||||
// Two states are equal if the bytes are the same and all registers are the same
|
||||
return Bytes.SequenceEqual(other.Bytes) &&
|
||||
A.Equals(other.A) &&
|
||||
X.Equals(other.X) &&
|
||||
Y.Equals(other.Y) &&
|
||||
D.Equals(other.D) &&
|
||||
S.Equals(other.S) &&
|
||||
P.Equals(other.P)
|
||||
;
|
||||
}
|
||||
|
||||
public static bool operator ==(SpriteGeneratorState state1, SpriteGeneratorState state2)
|
||||
{
|
||||
if (((object)state1) == null || ((object)state2) == null)
|
||||
return Object.Equals(state1, state2);
|
||||
|
||||
return state1.Equals(state2);
|
||||
}
|
||||
|
||||
public static bool operator !=(SpriteGeneratorState state1, SpriteGeneratorState state2)
|
||||
{
|
||||
if (((object)state1) == null || ((object)state2) == null)
|
||||
return !Object.Equals(state1, state2);
|
||||
|
||||
return !(state1.Equals(state2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user