godosa-engine

git clone https://git.godosa.eu/godosa-engine

master

raw · 2845 bytes

// 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<string> Chain, string Step, string? Label, string Reason,
    IReadOnlyList<string> Recent, string Dump, IReadOnlyDictionary<string, string> 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<CheckpointRecord> Checkpoints,
    IReadOnlyList<string> 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);
    }
}