1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
using Godosa.Core.Runs;
using Xunit;
namespace Godosa.Core.Tests.Runs;
public class RunsTests
{
private static RunFlags Parse(params string[] args)
{
var flags = new RunFlags();
for (var i = 0; i < args.Length; i++)
Assert.True(RunFlags.TryTake(args, ref i, ref flags), args[i]);
return flags;
}
[Fact]
public void Flags_DeterministicWithAndWithoutSeed()
{
Assert.Equal(new RunFlags(Deterministic: true, Seed: 7), Parse("--deterministic", "7"));
Assert.Equal(new RunFlags(Deterministic: true), Parse("--deterministic"));
Assert.Equal(1UL, Parse("--deterministic").SeedOr(1));
Assert.Equal(7UL, Parse("--deterministic", "7").SeedOr(1));
}
[Fact]
public void Flags_SeedNotTakenWhenNextIsAnOption()
{
string[] args = ["--deterministic", "--mute"];
var flags = new RunFlags();
var i = 0;
Assert.True(RunFlags.TryTake(args, ref i, ref flags));
Assert.Equal(0, i);
i = 1;
Assert.False(RunFlags.TryTake(args, ref i, ref flags));
}
[Fact]
public void Flags_DigestEveryAndHeadless()
{
Assert.Equal(new RunFlags(Digest: true), Parse("--digest"));
Assert.Equal(new RunFlags(Digest: true, DigestEvery: 60), Parse("--digest", "every=60"));
Assert.Equal(new RunFlags(Deterministic: true, Headless: true), Parse("--headless"));
Assert.Equal(new RunFlags(Deterministic: true, Seed: 3, Headless: true), Parse("--headless", "--deterministic", "3"));
}
[Fact]
public void Flags_BadDigestEvery_Throws() =>
Assert.Throws<ArgumentException>(() => Parse("--digest", "every=0"));
[Fact]
public void Clock_DeterministicFixedStep()
{
var clock = new FrameClock(true);
Assert.Equal(1.0 / 60, clock.Next(0.5));
Assert.Equal(1.0 / 60, clock.Next(0.001));
Assert.Equal(2, clock.Frame);
Assert.Equal(2.0 / 60, clock.Seconds, 12);
}
[Fact]
public void Clock_WallClamped()
{
var clock = new FrameClock(false);
Assert.Equal(0.1, clock.Next(0.5));
Assert.Equal(0.02, clock.Next(0.02));
Assert.Equal(0, clock.Next(-1));
Assert.Equal(3, clock.Frame);
}
[Fact]
public void Clock_Held_NoTimeNoFrame()
{
var clock = new FrameClock(true) { Held = true };
Assert.Equal(0, clock.Next(1.0 / 60));
Assert.Equal(0, clock.Frame);
Assert.Equal(0, clock.Seconds);
clock.Held = false;
clock.Next(0);
Assert.Equal(1, clock.Frame);
}
// Published FNV-1a 64 vectors (isthe.com/chongo/tech/comp/fnv).
[Theory]
[InlineData("", 0xcbf29ce484222325UL)]
[InlineData("a", 0xaf63dc4c8601ec8cUL)]
[InlineData("foobar", 0x85944171f73967e8UL)]
public void Digest_FnvVectors(string text, ulong expected)
{
var d = new StateDigest();
d.Add(text);
Assert.Equal(expected, d.Value);
Assert.Equal(expected, StateDigest.Of(System.Text.Encoding.UTF8.GetBytes(text)));
}
// Hand-checked: FNV-1a 64 of the little-endian bytes 01 00 00 00 and of 1.5 as an IEEE double.
[Fact]
public void Digest_NumbersLittleEndian()
{
var u = new StateDigest();
u.Add(1u);
Assert.Equal(0xad2aca7747985764UL, u.Value);
var d = new StateDigest();
d.Add(1.5);
Assert.Equal(0xaa95e93229a27c80UL, d.Value);
Assert.Equal("aa95e93229a27c80", d.Hex);
}
[Fact]
public void Line_Format() =>
Assert.Equal("digest 120 3.5 000000000000beef", DigestLine.Format(120, 3.5, 0xbeef));
[Fact]
public void Line_FormatRoundsGameTime() =>
Assert.Equal("digest 1 0.017 0000000000000001", DigestLine.Format(1, 1.0 / 60, 1));
[Fact]
public void Line_Due()
{
Assert.True(DigestLine.Due(60, 60));
Assert.False(DigestLine.Due(59, 60));
Assert.False(DigestLine.Due(0, 60));
Assert.False(DigestLine.Due(60, 0));
}
// SplitMix64 seeded 0: outputs e220a8397b1dcdaf, 6e789e6aa1b965f4 (published reference sequence).
[Fact]
public void Seeds_SplitMixReference()
{
Assert.Equal(0xe220a8397b1dcdafUL, SeedStreams.Of(0, 0));
Assert.Equal(0x6e789e6aa1b965f4UL, SeedStreams.Of(0, 1));
Assert.Equal(0x63cbe1e459320dd7UL, SeedStreams.Of(7, 0));
}
[Fact]
public void Loop_StopsWhenFrameReturnsFalse()
{
var seen = new List<long>();
var (frames, capped) = HeadlessLoop.Run(n => { seen.Add(n); return n < 3; }, null);
Assert.Equal([1L, 2, 3], seen);
Assert.Equal(3, frames);
Assert.False(capped);
}
[Fact]
public void Loop_FramesLimit() => Assert.Equal((5L, false), HeadlessLoop.Run(_ => true, 5));
[Fact]
public void Loop_Cap() => Assert.Equal((10L, true), HeadlessLoop.Run(_ => true, null, cap: 10));
}
|