독립 검증 도구 추가와 앱 상태 책임 분리

This commit is contained in:
2026-09-06 18:09:11 +09:00
parent 0f0431de5a
commit 002a922e1b
62 changed files with 3454 additions and 2008 deletions
@@ -0,0 +1,47 @@
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;
}