Skip to content

Commit

Permalink
Improve and test NextOf
Browse files Browse the repository at this point in the history
  • Loading branch information
Helco committed Nov 16, 2024
1 parent f47ebc8 commit a30ff55
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 16 deletions.
64 changes: 63 additions & 1 deletion zzre.core.tests/math/TestMath.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using System.Numerics;
using NUnit.Framework;

namespace zzre.core.tests.math;
namespace zzre.tests;

[TestFixture]
public class TestMath
Expand Down Expand Up @@ -58,4 +58,66 @@ public static IEnumerable<Vector3> GenerateUniformPoints()
yield return vec;
}
}

public readonly record struct AlmostANumber(int value)
: IComparable<AlmostANumber>, IComparisonOperators<AlmostANumber, AlmostANumber, bool>
{
public int CompareTo(AlmostANumber other) => value - other.value;
public static bool operator <(AlmostANumber left, AlmostANumber right) => left.CompareTo(right) < 0;
public static bool operator >(AlmostANumber left, AlmostANumber right) => left.CompareTo(right) > 0;
public static bool operator <=(AlmostANumber left, AlmostANumber right) => left.CompareTo(right) <= 0;
public static bool operator >=(AlmostANumber left, AlmostANumber right) => left.CompareTo(right) >= 0;
}
public static readonly AlmostANumber A = new(1), B = new(2), C = new(3), D = new(4), E = new(5);

[Test, Repeat(1000)]
public void TestNextOf()
{
var value = Random.Shared.NextOf(new[] { A, B, C, D, E });
Assert.That(value, Is.AnyOf(A, B, C, D, E));
}

[Test]
public void TestNextOfEmpty()
{
Assert.That(() =>
{
Random.Shared.NextOf(new AlmostANumber[] { });
}, Throws.ArgumentException);
}

[Test, Repeat(1000)]
public void TestNextOfExceptEmpty()
{
var value = Random.Shared.NextOf<AlmostANumber>([A, B, C, D, E], []);
Assert.That(value, Is.AnyOf(A, B, C, D, E));
}

[Test, Repeat(1000)]
public void TestNextOfDisjunct()
{
var value = Random.Shared.NextOf<AlmostANumber>([A, B, C], [D, E]);
Assert.That(value, Is.AnyOf(A, B, C));
}

[Test, Repeat(1000)]
public void TestNextOfEmptyInput()
{
var value = Random.Shared.NextOf<AlmostANumber>([], [A, B, C, D, E]);
Assert.That(value, Is.Null);
}

[Test, Repeat(1000)]
public void TestNextOfSuper()
{
var value = Random.Shared.NextOf<AlmostANumber>([B, C, D], [A, B, C, D, E]);
Assert.That(value, Is.Null);
}

[Test, Repeat(1000)]
public void TestNextOfExcept()
{
var value = Random.Shared.NextOf<AlmostANumber>([A, B, C, D, E], [B, C, D]);
Assert.That(value, Is.AnyOf(A, E));
}
}
71 changes: 61 additions & 10 deletions zzre.core/math/NumericsExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
Expand Down Expand Up @@ -140,26 +141,76 @@ public static bool IsSorted<T>(this ReadOnlySpan<T> list)
return true;
}

public static T NextOf<T>(this Random random, ReadOnlySpan<T> from, ReadOnlySpan<T> except)
// the original code would just give up after 20 tries, I will at least try a bit more
private const int NextOfRandomCount = 20;

public static T? NextOf<T>(this Random random, ReadOnlySpan<T> from, ReadOnlySpan<T> except)
where T : struct, IComparable<T>, IEquatable<T>, IComparisonOperators<T, T, bool>
{
// Used in the path finder the original code would only try 20 times and without sorting check
int index;
if (from.IsEmpty)
return null;
if (except.IsEmpty)
return from[random.Next(from.Length)];

if (except.Length > 8 && except.IsSorted())
{
do
for (int i = 0; i < NextOfRandomCount; i++)
{
index = random.Next(from.Length);
} while (except.BinarySearch(from[index]) >= 0);
int index = random.Next(from.Length);
if (except.BinarySearch(from[index]) < 0)
return from[index];
}
}
else
{
do
for (int i = 0; i < NextOfRandomCount; i++)
{
index = random.Next(from.Length);
} while (except.Contains(from[index]));
int index = random.Next(from.Length);
if (!except.Contains(from[index]))
return from[index];
}
}
return from[index];

return NextOfPrefilter(random, from, except);
}

public static T? NextOfPrefilter<T>(this Random random, ReadOnlySpan<T> fromOriginal, ReadOnlySpan<T> exceptOriginal)
where T : struct, IComparable<T>, IEquatable<T>, IComparisonOperators<T, T, bool>
{
if (fromOriginal.IsEmpty)
return null;
if (exceptOriginal.IsEmpty)
return fromOriginal[random.Next(fromOriginal.Length)];

var buffer = ArrayPool<T>.Shared.Rent(fromOriginal.Length * 2 + exceptOriginal.Length);
var from = buffer.AsSpan(0, fromOriginal.Length);
var except = buffer.AsSpan(from.Length, exceptOriginal.Length);

var destination = buffer.AsSpan(from.Length + except.Length);
fromOriginal.CopyTo(buffer);
exceptOriginal.CopyTo(buffer.AsSpan(from.Length));
Array.Sort(buffer, 0, from.Length);
Array.Sort(buffer, from.Length, except.Length);

int j = 0;
int k = 0;
for (int i = 0; i < from.Length; i++)
{
while (j < except.Length && except[j] < from[i])
j++;
if (j >= except.Length)
{
from[i..].CopyTo(destination[k..]);
k += from.Length - i;
break;
}
if (except[j] > from[i])
destination[k++] = from[i];
}

T? result = k > 0 ? destination[random.Next(k)] : null;
ArrayPool<T>.Shared.Return(buffer);
return result;
}

public static bool IsFinite(this Vector2 v) =>
Expand Down
9 changes: 4 additions & 5 deletions zzre/game/PathFinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,18 +213,17 @@ public uint TryRandomNextTraversable(uint fromId, ReadOnlySpan<uint> except, out
// which oculd be implemented a bit more efficiently and cleaner.

ref readonly var from = ref wpSystem.Waypoints[idToIndex[fromId]];
var toId = random.NextOf(from.WalkableIds, except);
if (IsTraversable(toId))

if (random.NextOf(from.WalkableIds, except) is uint toId && IsTraversable(toId))
{
edgeKind = WaypointEdgeKind.Walkable;
return toId;
}

toId = random.NextOf(from.JumpableIds, except);
if (IsTraversable(toId))
if (random.NextOf(from.JumpableIds, except) is uint toId2 && IsTraversable(toId2))
{
edgeKind = WaypointEdgeKind.Jumpable;
return toId;
return toId2;
}

edgeKind = WaypointEdgeKind.None;
Expand Down

0 comments on commit a30ff55

Please sign in to comment.