From 39066900773e7857faf2d02e7ed51b71d219d97d Mon Sep 17 00:00:00 2001 From: godosa Date: Tue, 6 Oct 2026 23:39:36 +0200 Subject: godosa-engine: initial public history --- src/Godosa.Core/Runs/Scripts/RunReport.cs | 58 +++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/Godosa.Core/Runs/Scripts/RunReport.cs (limited to 'src/Godosa.Core/Runs/Scripts/RunReport.cs') diff --git a/src/Godosa.Core/Runs/Scripts/RunReport.cs b/src/Godosa.Core/Runs/Scripts/RunReport.cs new file mode 100644 index 0000000..f0ecf0d --- /dev/null +++ b/src/Godosa.Core/Runs/Scripts/RunReport.cs @@ -0,0 +1,58 @@ +// Provenance: own design — shared run scripts (spec §7.2, §7.3) +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Godosa.Core.Runs.Scripts; + +public sealed record CheckpointRecord(string Name, long Frame, string Digest, string Source); + +public sealed record FailureInfo(string At, IReadOnlyList Chain, string Step, string? Label, string Reason, + IReadOnlyList Recent, string Dump, IReadOnlyDictionary Vars, string? Snapshot); + +public sealed record RunReport(string Script, RunMode Mode, ulong Seed, bool Deterministic, RunOutcome Outcome, long Frames, + int Steps, double WallSeconds, int CacheHits, int CacheMisses, IReadOnlyList Checkpoints, + IReadOnlyList Warnings, FailureInfo? Failure); + +public static class ReportWriter +{ + private static readonly JsonSerializerOptions Json = new() { WriteIndented = true, Converters = { new JsonStringEnumConverter() } }; + + public static string Text(RunReport r) + { + var sb = new StringBuilder(); + sb.AppendLine($"script: {r.Script} mode: {r.Mode.ToString().ToLowerInvariant()} seed: {r.Seed} deterministic: {(r.Deterministic ? "yes" : "no")}"); + sb.AppendLine($"outcome: {r.Outcome} frames: {r.Frames} steps: {r.Steps} wall: {r.WallSeconds:0.0} s cache: {r.CacheHits} hit / {r.CacheMisses} miss"); + foreach (var c in r.Checkpoints) + sb.AppendLine($"checkpoint {c.Name} f={c.Frame} {c.Digest} ({c.Source})"); + foreach (var w in r.Warnings) + sb.AppendLine($"warning: {w}"); + if (r.Failure is { } f) + { + sb.AppendLine(); + sb.AppendLine($"FAILED at {string.Join(" → ", f.Chain.Append(f.At))}{(f.Label == null ? "" : $" [{f.Label}]")}"); + sb.AppendLine($" step: {f.Step}"); + sb.AppendLine($" reason: {f.Reason}"); + sb.AppendLine("recent steps:"); + foreach (var s in f.Recent) + sb.AppendLine($" {s}"); + sb.AppendLine("variables:"); + foreach (var (k, v) in f.Vars.OrderBy(kv => kv.Key, StringComparer.Ordinal)) + sb.AppendLine($" {k} = {v}"); + sb.AppendLine("game state:"); + sb.AppendLine(f.Dump); + if (f.Snapshot != null) + sb.AppendLine($"failure save: {f.Snapshot}"); + } + return sb.ToString(); + } + + public static void Write(string dir, RunReport report, byte[]? failSnapshot) + { + Directory.CreateDirectory(dir); + File.WriteAllText(Path.Combine(dir, "report.txt"), Text(report)); + File.WriteAllText(Path.Combine(dir, "report.json"), JsonSerializer.Serialize(report, Json)); + if (failSnapshot != null) + File.WriteAllBytes(Path.Combine(dir, "fail.snap"), failSnapshot); + } +} -- cgit