- Projekt mit Unity 6.3 LTS (6000.3.20f1, Personal) geöffnet: ProjectSettings, .meta-Dateien, URP-Global-Settings, packages-lock.json. - Manifest auf die benötigten Pakete zurückgeführt; Unity hatte beim ersten Öffnen Vorlagenpakete ergänzt (Purchasing, Analytics, Collab, XR, 2D, Navigation). - Test Runner: Die U1-Suiten laufen unverändert als EditMode-Tests; mit LUNE_TEST_REPORT schreiben .NET und Unity dasselbe JSON-Protokoll (SuiteReport). - Golden-Vergleich zusätzlich je Fachbereich (Gold, HP, Positionen, Türme, Ancients, Wellen, Helden, Boss, Gewinner, ...). Nur Bericht; Testwerte und Prüfungsanzahl unverändert. - Diagnose: FloatProbe (Kern-Arithmetik gegen Godot-Bits und Auswertungsart der Laufzeit), DivergenceTrace und tools/port/trace_diff.py (erster abweichender Schritt), CoreBenchmark für .NET und Editor, CoreBatch als -executeMethod-Einstieg. - .NET-Harness baut nach tools/out/, weil Unity Temp/ beim Beenden löscht. - .gitignore ignoriert nur noch die csproj/sln im Projektwurzelverzeichnis; die Projektdateien des .NET-Harness fehlten deshalb in U1 und kommen jetzt dazu. Stand dieses Commits im Unity-Editor (Mono): 4 von 5 Suiten grün; golden 152/157, weil Mono float32-Zwischenwerte doppelt genau rechnet (Teil 2/2). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
64 lines
3.1 KiB
C#
64 lines
3.1 KiB
C#
using System;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Text;
|
|
|
|
namespace Luneraria.Core.Tests
|
|
{
|
|
/// <summary>
|
|
/// Step-by-step trace of one combat round of a bot match, for comparing two runtimes
|
|
/// (docs/port/U1.5-UNITY.md): per step and unit the discrete state (alive, target, hits,
|
|
/// zone) and the raw bits of position and hit points. Two traces are diffed outside
|
|
/// (tools/port/trace_diff.py) to find the first step where a decision differs.
|
|
/// </summary>
|
|
public static class DivergenceTrace
|
|
{
|
|
/// <summary>Plays rounds 1..round of a bot match; writes the trace of the last round.</summary>
|
|
public static void Write(string path, int seed, string[] bunds, string profile, int round)
|
|
{
|
|
var match = new LuneMatch();
|
|
var options = new MatchOptions { Seed = seed, HumanSlot = -1 };
|
|
if (bunds != null)
|
|
options.Bunds = new System.Collections.Generic.List<string>(bunds);
|
|
match.Setup(options);
|
|
using (var writer = new StreamWriter(path, false, new UTF8Encoding(false)))
|
|
{
|
|
while (match.Phase != Phase.Over && match.RoundNumber <= round)
|
|
{
|
|
for (int slot = 0; slot < 6; slot++)
|
|
LuneBot.Plan(match, slot, profile);
|
|
int fought = match.RoundNumber;
|
|
bool traced = fought == round;
|
|
match.BeginCombat();
|
|
if (traced)
|
|
{
|
|
Dump(writer, match.Combat);
|
|
while (!match.Combat.Finished)
|
|
{
|
|
match.StepCombat();
|
|
Dump(writer, match.Combat);
|
|
}
|
|
}
|
|
match.FinishCombat();
|
|
writer.WriteLine("settled round {0} combat_time {1} phase {2}", fought, match.LastSettlement.CombatTime.ToString("R", CultureInfo.InvariantCulture), match.Phase);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void Dump(StreamWriter writer, LuneCombat combat)
|
|
{
|
|
var line = new StringBuilder();
|
|
line.Append("step ").Append(combat.Steps).Append(" t ").Append(combat.Elapsed.ToString("R", CultureInfo.InvariantCulture));
|
|
line.Append(" finished ").Append(combat.Finished ? 1 : 0).Append(" reason ").Append(combat.EndReason);
|
|
writer.WriteLine(line.ToString());
|
|
foreach (LuneUnit unit in combat.Units)
|
|
{
|
|
if (!unit.Alive && unit.DeathTime < combat.Elapsed - 0.001)
|
|
continue;
|
|
writer.WriteLine(" u {0} {1} t{2} {3} {4} alive {5} target {6} hits {7} | hp {8:X16} pos {9:X8} {10:X8}",
|
|
unit.Id, unit.Kind, unit.Team, unit.Lane, unit.Zone, unit.Alive ? 1 : 0, unit.Target != null ? unit.Target.Id : -1, unit.HitCount,
|
|
BitConverter.DoubleToInt64Bits(unit.Hp), BitConverter.ToInt32(BitConverter.GetBytes(unit.Position.X), 0), BitConverter.ToInt32(BitConverter.GetBytes(unit.Position.Y), 0));
|
|
}
|
|
}
|
|
}
|
|
}
|