Files
ProjectQuint/Verification/ProjectQuint.Probes/ReplayRunner.cs
T

48 lines
2.2 KiB
C#

using System.Diagnostics;
using ProjectQuint.Core.Judgement;
namespace ProjectQuint.Probes;
public readonly record struct JudgementSnapshot(int Score, int Combo, int MaxCombo,
int MissCount, int KoolCount, int CoolCount, int GoodCount, int JudgedUnits, bool Finished);
public sealed record ReplayObservation(JudgementSnapshot Judgement, double SetupMilliseconds,
double ReplayMilliseconds, long SetupAllocatedBytes, long ReplayAllocatedBytes);
public static class ReplayRunner
{
public static ReplayObservation Run(SyntheticWorkload workload, int frameRate)
{
if (frameRate < 1 || frameRate > 10_000)
throw new ArgumentOutOfRangeException(nameof(frameRate), "Use a frame rate from 1 to 10000.");
var allocationStart = GC.GetAllocatedBytesForCurrentThread();
var setupStart = Stopwatch.GetTimestamp();
var engine = new RhythmJudgementEngine(workload.Notes, laneCount: 4);
var setupEnd = Stopwatch.GetTimestamp();
var replayAllocationStart = GC.GetAllocatedBytesForCurrentThread();
long frame = 0;
foreach (var input in workload.Events)
{
// Merge two timestamped streams; no sleeps or real-time waiting affect this trace.
while ((double)frame / frameRate <= input.Time)
engine.Update((double)frame++ / frameRate);
if (input.Pressed)
engine.Press(input.Lane, input.Time);
else
engine.Release(input.Lane, input.Time);
}
engine.Update(workload.Notes[^1].EndTime + 1);
var replayEnd = Stopwatch.GetTimestamp();
var allocationEnd = GC.GetAllocatedBytesForCurrentThread();
return new ReplayObservation(new JudgementSnapshot(engine.Score, engine.Combo, engine.MaxCombo,
engine.MissCount, engine.KoolCount, engine.CoolCount, engine.GoodCount,
engine.JudgedNoteCount, engine.IsFinished),
Milliseconds(setupStart, setupEnd), Milliseconds(setupEnd, replayEnd),
replayAllocationStart - allocationStart, allocationEnd - replayAllocationStart);
}
private static double Milliseconds(long start, long end) =>
(end - start) * 1000.0 / Stopwatch.Frequency;
}