Implement dynamic previous hitobject retention for Skill class

There is no reason we should be limiting skills to knowing only the previous 2 objects. This originally existed as an angle implementation detail of the original pp+ codebase which made its way here, but didn't get used in the same way.
This commit is contained in:
Samuel Cattini-Schultz
2021-02-06 15:48:14 +11:00
parent eb1e850f99
commit fe66b84bed
5 changed files with 284 additions and 209 deletions

View File

@ -1,115 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using NUnit.Framework;
using osu.Game.Rulesets.Difficulty.Utils;
namespace osu.Game.Tests.NonVisual
{
[TestFixture]
public class LimitedCapacityStackTest
{
private const int capacity = 3;
private LimitedCapacityStack<int> stack;
[SetUp]
public void Setup()
{
stack = new LimitedCapacityStack<int>(capacity);
}
[Test]
public void TestEmptyStack()
{
Assert.AreEqual(0, stack.Count);
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
int unused = stack[0];
});
int count = 0;
foreach (var unused in stack)
count++;
Assert.AreEqual(0, count);
}
[TestCase(1)]
[TestCase(2)]
[TestCase(3)]
public void TestInRangeElements(int count)
{
// e.g. 0 -> 1 -> 2
for (int i = 0; i < count; i++)
stack.Push(i);
Assert.AreEqual(count, stack.Count);
// e.g. 2 -> 1 -> 0 (reverse order)
for (int i = 0; i < stack.Count; i++)
Assert.AreEqual(count - 1 - i, stack[i]);
// e.g. indices 3, 4, 5, 6 (out of range)
for (int i = stack.Count; i < stack.Count + capacity; i++)
{
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
int unused = stack[i];
});
}
}
[TestCase(4)]
[TestCase(5)]
[TestCase(6)]
public void TestOverflowElements(int count)
{
// e.g. 0 -> 1 -> 2 -> 3
for (int i = 0; i < count; i++)
stack.Push(i);
Assert.AreEqual(capacity, stack.Count);
// e.g. 3 -> 2 -> 1 (reverse order)
for (int i = 0; i < stack.Count; i++)
Assert.AreEqual(count - 1 - i, stack[i]);
// e.g. indices 3, 4, 5, 6 (out of range)
for (int i = stack.Count; i < stack.Count + capacity; i++)
{
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
int unused = stack[i];
});
}
}
[TestCase(1)]
[TestCase(2)]
[TestCase(3)]
[TestCase(4)]
[TestCase(5)]
[TestCase(6)]
public void TestEnumerator(int count)
{
// e.g. 0 -> 1 -> 2 -> 3
for (int i = 0; i < count; i++)
stack.Push(i);
int enumeratorCount = 0;
int expectedValue = count - 1;
foreach (var item in stack)
{
Assert.AreEqual(expectedValue, item);
enumeratorCount++;
expectedValue--;
}
Assert.AreEqual(stack.Count, enumeratorCount);
}
}
}

View File

@ -0,0 +1,143 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using NUnit.Framework;
using osu.Game.Rulesets.Difficulty.Utils;
namespace osu.Game.Tests.NonVisual
{
[TestFixture]
public class ReverseQueueTest
{
private ReverseQueue<char> queue;
[SetUp]
public void Setup()
{
queue = new ReverseQueue<char>(4);
}
[Test]
public void TestEmptyQueue()
{
Assert.AreEqual(0, queue.Count);
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
char unused = queue[0];
});
int count = 0;
foreach (var unused in queue)
count++;
Assert.AreEqual(0, count);
}
[Test]
public void TestEnqueue()
{
// Assert correct values and reverse index after enqueueing
queue.Enqueue('a');
queue.Enqueue('b');
queue.Enqueue('c');
Assert.AreEqual('c', queue[0]);
Assert.AreEqual('b', queue[1]);
Assert.AreEqual('a', queue[2]);
// Assert correct values and reverse index after enqueueing beyond initial capacity of 4
queue.Enqueue('d');
queue.Enqueue('e');
queue.Enqueue('f');
Assert.AreEqual('f', queue[0]);
Assert.AreEqual('e', queue[1]);
Assert.AreEqual('d', queue[2]);
Assert.AreEqual('c', queue[3]);
Assert.AreEqual('b', queue[4]);
Assert.AreEqual('a', queue[5]);
}
[Test]
public void TestDequeue()
{
queue.Enqueue('a');
queue.Enqueue('b');
queue.Enqueue('c');
queue.Enqueue('d');
queue.Enqueue('e');
queue.Enqueue('f');
// Assert correct item return and no longer in queue after dequeueing
Assert.AreEqual('a', queue[5]);
var dequeuedItem = queue.Dequeue();
Assert.AreEqual('a', dequeuedItem);
Assert.AreEqual(5, queue.Count);
Assert.AreEqual('f', queue[0]);
Assert.AreEqual('b', queue[4]);
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
char unused = queue[5];
});
// Assert correct state after enough enqueues and dequeues to wrap around array (queue.start = 0 again)
queue.Enqueue('g');
queue.Enqueue('h');
queue.Enqueue('i');
queue.Dequeue();
queue.Dequeue();
queue.Dequeue();
queue.Dequeue();
queue.Dequeue();
queue.Dequeue();
queue.Dequeue();
Assert.AreEqual(1, queue.Count);
Assert.AreEqual('i', queue[0]);
}
[Test]
public void TestClear()
{
queue.Enqueue('a');
queue.Enqueue('b');
queue.Enqueue('c');
queue.Enqueue('d');
queue.Enqueue('e');
queue.Enqueue('f');
// Assert queue is empty after clearing
queue.Clear();
Assert.AreEqual(0, queue.Count);
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
char unused = queue[0];
});
}
[Test]
public void TestEnumerator()
{
queue.Enqueue('a');
queue.Enqueue('b');
queue.Enqueue('c');
queue.Enqueue('d');
queue.Enqueue('e');
queue.Enqueue('f');
char[] expectedValues = { 'f', 'e', 'd', 'c', 'b', 'a' };
int expectedValueIndex = 0;
// Assert items are enumerated in correct order
foreach (var item in queue)
{
Assert.AreEqual(expectedValues[expectedValueIndex], item);
expectedValueIndex++;
}
}
}
}