From 9bf397e765ed6ab548840041225923ce27148413 Mon Sep 17 00:00:00 2001 From: Martin Stickelbroeck Date: Wed, 30 Sep 2026 15:22:36 +0200 Subject: [PATCH] U1.5 Unity Core Integration (2/2): explizite float32-Rundung, Nachweise MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Befund: Unitys Mono 6.13 rechnet float32-Zwischenwerte doppelt genau (ECMA-335 I.12.1.3). Der erste Editor-Lauf fand 5 von 157 Golden- Abweichungen: Blickrichtung bis 1,6e-3 rad; in mirror_match Runde 7 verliert eine Einheit in Schritt 599 ihr Ziel, und der Kampf dauert 4,8 s länger. Eine Toleranz ist dafür nicht begründbar. Maßnahme: Vec2 rundet jeden Zwischenwert explizit auf float32 (conv.r4). Keine Regeländerung, R1 unverändert. .NET 8: 402/402, Golden 0, Trace byteidentisch zu vorher. Unity-Editor (Release und Debug): 402/402, Golden bitgleich in allen Bereichen, Float-Probe 4000/4000. - CoreIntegrationTests: keine Engine-Referenzen im Kern, Float-Arithmetik wie Godot. - docs/port/U1.5-UNITY.md: Versionen, Lizenz, Pakete, Tests, Golden je Bereich, Float-Befund, Editor-Benchmark, fehlende Build-Module. - docs/port/KNOWN-BEHAVIOR-DIFFERENCES.md mit "Legacy defects retained for parity" (R1) und F1. - Protokolle unter docs/port/logs/u1_5/. Co-Authored-By: Claude Opus 5.5 --- Assets/Luneraria/Core/Model/Vec2.cs | 54 ++-- .../Tests/Core/Unity/CoreIntegrationTests.cs | 31 +++ .../Core/Unity/CoreIntegrationTests.cs.meta | 2 + README.md | 14 +- docs/port/KNOWN-BEHAVIOR-DIFFERENCES.md | 49 ++++ docs/port/PORT-QA.md | 22 +- docs/port/PORT-STATUS.md | 23 +- docs/port/U1-RULES.md | 8 +- docs/port/U1.5-UNITY.md | 240 ++++++++++++++++++ docs/port/logs/u1_5/core-tests-dotnet8.json | 12 + .../u1_5/core-tests-unity-editor-debug.json | 12 + .../u1_5/core-tests-unity-editor-release.json | 12 + .../logs/u1_5/editmode-results-before-fix.xml | 109 ++++++++ .../port/logs/u1_5/editmode-results-debug.xml | 114 +++++++++ .../logs/u1_5/editmode-results-release.xml | 114 +++++++++ docs/port/logs/u1_5/float-probe-dotnet8.json | 42 +++ .../logs/u1_5/float-probe-unity-debug.json | 42 +++ .../float-probe-unity-release-before-fix.json | 42 +++ .../logs/u1_5/float-probe-unity-release.json | 42 +++ docs/port/logs/u1_5/sim-bench-dotnet8.json | 11 + docs/port/logs/u1_5/sim-bench-godot.json | 15 ++ .../u1_5/sim-bench-unity-editor-debug.json | 11 + .../u1_5/sim-bench-unity-editor-release.json | 11 + docs/port/logs/u1_5/unity-environment.json | 15 ++ 24 files changed, 1003 insertions(+), 44 deletions(-) create mode 100644 Assets/Luneraria/Tests/Core/Unity/CoreIntegrationTests.cs create mode 100644 Assets/Luneraria/Tests/Core/Unity/CoreIntegrationTests.cs.meta create mode 100644 docs/port/KNOWN-BEHAVIOR-DIFFERENCES.md create mode 100644 docs/port/U1.5-UNITY.md create mode 100644 docs/port/logs/u1_5/core-tests-dotnet8.json create mode 100644 docs/port/logs/u1_5/core-tests-unity-editor-debug.json create mode 100644 docs/port/logs/u1_5/core-tests-unity-editor-release.json create mode 100644 docs/port/logs/u1_5/editmode-results-before-fix.xml create mode 100644 docs/port/logs/u1_5/editmode-results-debug.xml create mode 100644 docs/port/logs/u1_5/editmode-results-release.xml create mode 100644 docs/port/logs/u1_5/float-probe-dotnet8.json create mode 100644 docs/port/logs/u1_5/float-probe-unity-debug.json create mode 100644 docs/port/logs/u1_5/float-probe-unity-release-before-fix.json create mode 100644 docs/port/logs/u1_5/float-probe-unity-release.json create mode 100644 docs/port/logs/u1_5/sim-bench-dotnet8.json create mode 100644 docs/port/logs/u1_5/sim-bench-godot.json create mode 100644 docs/port/logs/u1_5/sim-bench-unity-editor-debug.json create mode 100644 docs/port/logs/u1_5/sim-bench-unity-editor-release.json create mode 100644 docs/port/logs/u1_5/unity-environment.json diff --git a/Assets/Luneraria/Core/Model/Vec2.cs b/Assets/Luneraria/Core/Model/Vec2.cs index 56faf4b..1d4a0fd 100644 --- a/Assets/Luneraria/Core/Model/Vec2.cs +++ b/Assets/Luneraria/Core/Model/Vec2.cs @@ -6,8 +6,10 @@ namespace Luneraria.Core /// Ground vector (x, z) with 32-bit components and plain single-precision arithmetic, like /// Godot's Vector2 (tools/godot-golden/float_probe.gd: 4000 of 4000 samples identical). /// Scalars of the rules are doubles; they are rounded to float where they meet a vector. - /// Matching Godot's arithmetic keeps the golden comparisons tight; the product requirement - /// is determinism on one machine, not identical bits across platforms (docs/port/U1-RULES.md). + /// Every intermediate result is rounded to float explicitly: ECMA-335 (I.12.1.3) allows a + /// runtime to keep float intermediates at higher precision, and Unity's Mono does. Without + /// the casts `x * x + y * y` differs in the last bit between Mono and .NET/IL2CPP/Godot, + /// and the combat dynamics amplify that into different decisions (docs/port/U1.5-UNITY.md). /// [Serializable] public struct Vec2 : IEquatable @@ -32,58 +34,56 @@ namespace Luneraria.Core Y = (float)y; } - public static Vec2 operator +(Vec2 a, Vec2 b) { return new Vec2(a.X + b.X, a.Y + b.Y); } - public static Vec2 operator -(Vec2 a, Vec2 b) { return new Vec2(a.X - b.X, a.Y - b.Y); } + public static Vec2 operator +(Vec2 a, Vec2 b) { return new Vec2((float)(a.X + b.X), (float)(a.Y + b.Y)); } + public static Vec2 operator -(Vec2 a, Vec2 b) { return new Vec2((float)(a.X - b.X), (float)(a.Y - b.Y)); } public static Vec2 operator -(Vec2 a) { return new Vec2(-a.X, -a.Y); } public static Vec2 operator *(Vec2 a, double s) { float f = (float)s; - return new Vec2(a.X * f, a.Y * f); + return new Vec2((float)(a.X * f), (float)(a.Y * f)); } public static Vec2 operator *(double s, Vec2 a) { float f = (float)s; - return new Vec2(f * a.X, f * a.Y); + return new Vec2((float)(f * a.X), (float)(f * a.Y)); } public static Vec2 operator /(Vec2 a, double s) { float f = (float)s; - return new Vec2(a.X / f, a.Y / f); + return new Vec2((float)(a.X / f), (float)(a.Y / f)); } public static bool operator ==(Vec2 a, Vec2 b) { return a.X == b.X && a.Y == b.Y; } public static bool operator !=(Vec2 a, Vec2 b) { return !(a == b); } - public float Length() { return MathF.Sqrt(X * X + Y * Y); } + public float Length() { return (float)MathF.Sqrt(LengthSquared()); } - public float LengthSquared() { return X * X + Y * Y; } + public float LengthSquared() { return (float)((float)(X * X) + (float)(Y * Y)); } public float DistanceTo(Vec2 p) { - float dx = X - p.X; - float dy = Y - p.Y; - return MathF.Sqrt(dx * dx + dy * dy); + return (float)MathF.Sqrt(DistanceSquaredTo(p)); } public float DistanceSquaredTo(Vec2 p) { - float dx = X - p.X; - float dy = Y - p.Y; - return dx * dx + dy * dy; + float dx = (float)(X - p.X); + float dy = (float)(Y - p.Y); + return (float)((float)(dx * dx) + (float)(dy * dy)); } - public float Dot(Vec2 p) { return X * p.X + Y * p.Y; } + public float Dot(Vec2 p) { return (float)((float)(X * p.X) + (float)(Y * p.Y)); } public Vec2 Normalized() { - float l = X * X + Y * Y; + float l = LengthSquared(); if (l != 0f) { - l = MathF.Sqrt(l); - return new Vec2(X / l, Y / l); + l = (float)MathF.Sqrt(l); + return new Vec2((float)(X / l), (float)(Y / l)); } return this; } @@ -92,7 +92,7 @@ namespace Luneraria.Core public Vec2 Lerp(Vec2 to, double weight) { float w = (float)weight; - return new Vec2(X + (to.X - X) * w, Y + (to.Y - Y) * w); + return new Vec2((float)(X + (float)((float)(to.X - X) * w)), (float)(Y + (float)((float)(to.Y - Y) * w))); } public Vec2 LimitLength(double length) @@ -102,10 +102,10 @@ namespace Luneraria.Core Vec2 v = this; if (l > 0f && limit < l) { - v.X /= l; - v.Y /= l; - v.X *= limit; - v.Y *= limit; + v.X = (float)(v.X / l); + v.Y = (float)(v.Y / l); + v.X = (float)(v.X * limit); + v.Y = (float)(v.Y * limit); } return v; } @@ -113,9 +113,9 @@ namespace Luneraria.Core public Vec2 Rotated(double angle) { float a = (float)angle; - float sine = MathF.Sin(a); - float cosi = MathF.Cos(a); - return new Vec2(X * cosi - Y * sine, X * sine + Y * cosi); + float sine = (float)MathF.Sin(a); + float cosi = (float)MathF.Cos(a); + return new Vec2((float)((float)(X * cosi) - (float)(Y * sine)), (float)((float)(X * sine) + (float)(Y * cosi))); } public bool Equals(Vec2 other) { return X.Equals(other.X) && Y.Equals(other.Y); } diff --git a/Assets/Luneraria/Tests/Core/Unity/CoreIntegrationTests.cs b/Assets/Luneraria/Tests/Core/Unity/CoreIntegrationTests.cs new file mode 100644 index 0000000..2c25db4 --- /dev/null +++ b/Assets/Luneraria/Tests/Core/Unity/CoreIntegrationTests.cs @@ -0,0 +1,31 @@ +using System.IO; +using System.Linq; +using NUnit.Framework; +using UnityEngine; + +namespace Luneraria.Core.Tests +{ + /// + /// Unity-specific integration checks (U1.5), in addition to the unchanged U1 suites of + /// CoreSuiteTests: the core assembly stays free of engine references, and the core's float + /// arithmetic in Unity's Mono is bit-identical to Godot's (docs/port/U1.5-UNITY.md). + /// + public sealed class CoreIntegrationTests + { + [Test] + public void CoreHasNoEngineReferences() + { + string[] references = typeof(LuneMatch).Assembly.GetReferencedAssemblies().Select(a => a.Name).ToArray(); + Assert.IsFalse(references.Any(name => name.StartsWith("UnityEngine") || name.StartsWith("UnityEditor") || name.StartsWith("Unity.")), "Luneraria.Core references " + string.Join(", ", references)); + } + + [Test] + public void CoreFloatArithmeticMatchesGodot() + { + FloatProbe.Result result = FloatProbe.Run(Path.Combine(Application.dataPath, "Luneraria/Tests/Core/Golden")); + string report = string.Join(", ", result.Identical.Select(pair => pair.Key + " " + pair.Value + "/" + result.Rows)); + Debug.Log("float probe: " + report); + Assert.AreEqual(0, result.Mismatches, "Vec2/GdMath results differ from Godot: " + report); + } + } +} diff --git a/Assets/Luneraria/Tests/Core/Unity/CoreIntegrationTests.cs.meta b/Assets/Luneraria/Tests/Core/Unity/CoreIntegrationTests.cs.meta new file mode 100644 index 0000000..f36032c --- /dev/null +++ b/Assets/Luneraria/Tests/Core/Unity/CoreIntegrationTests.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 6721ef90a635849c3ba49077a1289874 \ No newline at end of file diff --git a/README.md b/README.md index 3384835..9df02ae 100644 --- a/README.md +++ b/README.md @@ -9,16 +9,26 @@ Drei-Lane-Autobattler 3 gegen 3 (Mischung aus Dota 2 und Legion TD 2), portiert ## Aufbau - `Assets/Luneraria/Core/`: Regelkern ohne Unity-Abhängigkeit (`Luneraria.Core`). -- `Assets/Luneraria/Tests/Core/`: Prüfsuiten mit Golden-Referenzen aus Godot; laufen im Unity Test Runner und mit dem .NET SDK. -- `tools/`: .NET-Projekte, Godot-Referenzwerkzeuge, Portwerkzeuge. +- `Assets/Luneraria/Tests/Core/`: Prüfsuiten mit Golden-Referenzen aus Godot, Diagnose (Float-Probe, Schritt-Trace, Benchmark); laufen im Unity Test Runner und mit dem .NET SDK. +- `tools/`: .NET-Projekte (Ausgabe nach `tools/out/`), Godot-Referenzwerkzeuge, Portwerkzeuge. - `docs/port/`: Stand, Entscheidungen und QA je Meilenstein ([PORT-STATUS.md](docs/port/PORT-STATUS.md)). ## Befehle +Kerntests mit dem .NET SDK (Protokoll nach `$LUNE_TEST_REPORT`, Standard `tools/out/`): + ```bash sh tools/run_core_tests.sh ``` +Dieselben Tests im Unity Test Runner: + +```bash +/Applications/Unity/Hub/Editor/6000.3.20f1/Unity.app/Contents/MacOS/Unity -batchmode -projectPath "$PWD" -runTests -testPlatform EditMode -testResults "$PWD/tools/out/editmode-results.xml" -logFile - +``` + +Leistung (.NET bzw. Editor über `-executeMethod Luneraria.Core.Tests.CoreBatch.Benchmark`): + ```bash dotnet run -c Release --project tools/dotnet/Luneraria.Core.Bench ``` diff --git a/docs/port/KNOWN-BEHAVIOR-DIFFERENCES.md b/docs/port/KNOWN-BEHAVIOR-DIFFERENCES.md new file mode 100644 index 0000000..a996d25 --- /dev/null +++ b/docs/port/KNOWN-BEHAVIOR-DIFFERENCES.md @@ -0,0 +1,49 @@ +# Bekannte Verhaltensunterschiede + +Diese Datei führt jedes bekannte Verhalten des Unity-Ports, das von der Godot-Referenz (`luneraria/g3`, `1b540f2`) abweicht oder bewusst gleich falsch bleibt. Jeder Eintrag nennt Ursache, Wirkung, Nachweis und Status. Neue Befunde kommen hier hinein, bevor sie behoben werden. + +## Legacy defects retained for parity + +Diese Fehler der Referenz sind im Port absichtlich nachgebildet. Die Golden-Parität bleibt so erhalten, und Bots und Partien verhalten sich fachlich wie in Godot. Jeder Fehler wird später in einem eigenen Schritt behoben, mit neuer Golden-Referenz und eigenem Commit. + +### R1 · Bot vergisst die Bossjagd nach einer Champion-Buchung + +| | | +| --- | --- | +| Ort in der Referenz | `saumwacht-godot/scripts/luneraria/core/lune_bot.gd`, `_plan_boss` (Zeile 150) | +| Ort im Port | `Assets/Luneraria/Core/Simulation/LuneBot.cs`, `PlanBoss` (Kommentar R1) | +| Ursache | Das Bot-Gedächtnis wird von zwei Stellen befüllt. Die Champion-Planung legt einen Eintrag nur mit `champion_round` an. `_plan_boss` liest danach `memory.bar` und `memory.sent`, die fehlen. GDScript meldet `Invalid access to property or key 'bar'` und bricht die Funktion ab. | +| Wirkung | Ein Bot, der vor seiner ersten Bossplanung einen Champion gebucht hat, schickt in dieser Partie nie eine Expedition in die Grotte. Das betrifft vor allem das Profil `bossjagd`. Die Bossjagd der Bots ist dadurch seltener, als die Profile vorsehen. | +| Nachweis | Beim Erzeugen der Golden-Referenzen trat der Fehler in vier Szenarien auf. Der Port liefert in allen 19 Szenarien dieselben Bossentscheidungen (Golden-Bereich „Boss“: 1.559 Werte, 0 Abweichungen). | +| Status | bewusst übernommen (U1, U1.5). Nicht korrigieren, solange U1 als Paritätsreferenz dient. | +| Korrektur später | In einer eigenen Bot- oder Balance-Phase: fehlende Felder mit Standardwerten anlegen (`bar` = 1,35, `sent` = 0), neue Golden-Referenz erzeugen und den Unterschied zu dieser Referenz dokumentieren. | + +## Laufzeitunterschiede + +### F1 · Mono rechnet float32-Zwischenwerte doppelt genau (behoben in U1.5) + +| | | +| --- | --- | +| Ursache | Unitys Mono 6.13 hält float32-Zwischenwerte doppelt genau (erlaubt nach ECMA-335 I.12.1.3). .NET 8 und Godot runden nach jeder Operation. | +| Wirkung ohne Korrektur | Positionen weichen nach 10–60 s um bis zu 0,22 mm ab, die Blickrichtung um bis zu 1,6·10⁻³ rad. In `mirror_match` verliert eine Einheit in Schritt 599 ihr Ziel, und der Kampf dauert 4,8 s länger. Die Abweichung beeinflusst also Entscheidungen. | +| Maßnahme | `Vec2` rundet jeden Zwischenwert explizit auf float32. Keine Regeländerung; .NET-Ergebnisse sind byteidentisch zu vorher. | +| Status | behoben. Im Unity-Editor (Release und Debug) sind alle Golden-Werte bitgleich. `CoreIntegrationTests.CoreFloatArithmeticMatchesGodot` sichert das ab. | +| Offen | IL2CPP sowie Player unter Windows 11 und Fedora 44 sind noch nicht gemessen (Build-Module fehlen). | + +Details: [U1.5-UNITY.md](U1.5-UNITY.md#float-befund). + +## Darstellungswerte + +| Wert | Warum kein Regelunterschied | +| --- | --- | +| Blickrichtung (`LuneUnit.Facing`) | Der Kern schreibt sie nur (Bewegungsrichtung ab 1 mm Schritt); keine Regel liest sie. Die Darstellung darf sie glätten. | + +## Technische Repräsentation + +Klassen statt Dictionaries, stabile Einfügesortierung, 32-Bit-`int`, der ungenutzte Zufallsgenerator entfällt. Keiner dieser Unterschiede ändert Ergebnisse. Die Liste steht in [U1-RULES.md](U1-RULES.md#bewusst-andere-technische-repräsentation). + +## Noch nicht gemessen + +- IL2CPP-Player (alle Plattformen). +- Mono-Player unter Windows 11 (x64) und Fedora 44 (x86_64). +- Läufe auf x86-64-Prozessoren. Bisher lief alles auf Apple M5 (ARM64). diff --git a/docs/port/PORT-QA.md b/docs/port/PORT-QA.md index 0f4699a..5de853b 100644 --- a/docs/port/PORT-QA.md +++ b/docs/port/PORT-QA.md @@ -11,9 +11,25 @@ Je Meilenstein: Tests, Bild, Leistung, bekannte Unterschiede, Commit. | Bild | `screenshots/u1/replay-start_three_lanes-t10.png`, `replay-boss_cycle-marching.png`, `replay-grotto_contested-t15.png`: Godot-Positionen (Punkte) und C#-Positionen (Ringe) decken sich (31/31, 35/35, 15/15) | | Leistung (Mac M5) | Simulationsschritt bei 120 Einheiten: Godot 1,587 ms, C#/.NET 8 0,259 ms; Bot-Partien pro Runde: 648 ms zu 36 ms (`logs/u1/sim-bench-*.json`) | | Bekannte Unterschiede | technische Repräsentation (Klassen statt Dictionaries, stabile Sortierung, 32-Bit-int, kein ungenutzter Zufallsgenerator); Referenzfehler R1 bewusst übernommen | -| Offen | Lauf im Unity-Editor (Lizenz fehlt), Mono-Leistung, Kerntests auf Windows 11 und Fedora 44 | -| Commit | Zweig `luneraria/u1` | +| Offen | erledigt in U1.5: Lauf im Unity-Editor, Mono-Leistung. Weiter offen: Kerntests auf Windows 11 und Fedora 44 | +| Commit | Zweig `luneraria/u1` (`4d6ed17`) | + +Hinweis: Die Projektdateien des .NET-Harness (`tools/dotnet/*/*.csproj`) fehlten im U1-Commit, weil `.gitignore` alle `*.csproj` ausschloss. Sie sind seit U1.5 eingecheckt. + +## U1.5 · Unity-Integrationsprüfung + +| Punkt | Ergebnis | +| --- | --- | +| Umgebung | Unity 6000.3.20f1 (6.3 LTS), Personal-Lizenz, URP 17.3.0, Editor-Mono 6.13.0, Standalone-Backend Mono, API-Level .NET Standard 2.1 | +| Tests | Unity Test Runner (EditMode): 402/402 in Release und Debug, dazu 2 Integrationstests; .NET 8 weiter 402/402. Protokolle `logs/u1_5/editmode-results-*.xml`, `core-tests-*.json` | +| Golden in Unity | 51.221 Werte, Abweichung 0 in allen Kategorien und Bereichen (Gold, HP, Positionen, Türme, Ancients, Wellen, Helden, Boss, Gewinner, …) | +| Float-Befund | Mono rechnet float32-Zwischenwerte doppelt genau. Das beeinflusste eine Zielentscheidung (`mirror_match` Runde 7: +4,8 s Kampfdauer). Behoben durch explizite Rundung in `Vec2`; .NET-Ergebnisse byteidentisch zu vorher | +| Bild | keines; U1.5 hat keine sichtbare Szene. Nachweis sind der Schritt-Trace (`tools/port/trace_diff.py`) und die Float-Probe (`logs/u1_5/float-probe-*.json`) | +| Leistung (Editor-Benchmark) | Schritt mit 120 Einheiten: Editor Release 0,51 ms, Debug 1,00 ms; .NET 8 0,24–0,26 ms; Godot 1,60 ms (`logs/u1_5/sim-bench-*.json`) | +| Bekannte Unterschiede | R1 bewusst übernommen; F1 (Mono-Float) behoben ([KNOWN-BEHAVIOR-DIFFERENCES.md](KNOWN-BEHAVIOR-DIFFERENCES.md)) | +| Offen | IL2CPP- und Player-Läufe unter Windows 11 und Fedora 44 (Build-Module fehlen, Installation nach Freigabe) | +| Commit | Zweig `luneraria/u1.5` | ## U2 bis U5 -Noch nicht begonnen. +U2 ist der nächste Schritt; U3 bis U5 sind noch nicht begonnen. diff --git a/docs/port/PORT-STATUS.md b/docs/port/PORT-STATUS.md index 96d0a68..09e3942 100644 --- a/docs/port/PORT-STATUS.md +++ b/docs/port/PORT-STATUS.md @@ -8,23 +8,27 @@ Zielplattformen sind Fedora 44 und Windows 11; macOS ist die Entwicklungsplattfo | Meilenstein | Zustand | Nachweis | | --- | --- | --- | -| U1 · C#-Simulation | **fertig außerhalb von Unity**: 402/402 Prüfungen, Golden-Parität mit Godot; Unity-Editor-Lauf offen (Lizenz) | [U1-RULES.md](U1-RULES.md) | -| U2 · Unity-Spielwelt | nicht begonnen; Voraussetzung: U1 grün und Unity-Lizenz aktiv | [U2-WORLD.md](U2-WORLD.md) | +| U1 · C#-Simulation | **fertig** (Referenzmeilenstein, `luneraria/u1` = `4d6ed17`): 402/402 Prüfungen, Golden-Parität mit Godot | [U1-RULES.md](U1-RULES.md) | +| U1.5 · Unity-Integrationsprüfung | **fertig**: Kern im Unity-Editor 402/402 (Release und Debug), Golden bitgleich nach expliziter float32-Rundung in `Vec2`, Editor-Benchmark | [U1.5-UNITY.md](U1.5-UNITY.md) | +| U2 · Unity-Spielwelt | nächster Schritt | [U2-WORLD.md](U2-WORLD.md) | | U3 · G3-HUD portieren | nicht begonnen | [U3-UI.md](U3-UI.md) | | U4 · Netzwerk | nicht begonnen; M4-Umfang ist erfasst | [U4-NETWORK.md](U4-NETWORK.md) | | U5 · Visuelle Neuausrichtung (Vertical Slice Mid-Lane) | nicht begonnen | [U5-VISUAL.md](U5-VISUAL.md) | QA je Meilenstein: [PORT-QA.md](PORT-QA.md). -## Blocker +Bekannte Verhaltensunterschiede, auch die bewusst übernommenen Referenzfehler: [KNOWN-BEHAVIOR-DIFFERENCES.md](KNOWN-BEHAVIOR-DIFFERENCES.md). -- **Unity-Lizenz:** Unity startet im Batch-Modus, meldet aber „No valid Unity Editor license found“. Nötig ist eine Anmeldung im Unity Hub mit aktivierter Personal-Lizenz; das kann nur der Projektinhaber erledigen. -- Bis dahin gibt es keine `ProjectSettings/` und keine `.meta`-Dateien; Unity erzeugt sie beim ersten Öffnen. +## Offene Voraussetzungen + +- **Build-Module für die Zielplattformen fehlen:** Windows Build Support (Mono) und Linux Build Support (Mono), optional Linux IL2CPP und Linux Dedicated Server. Installation nur nach Freigabe (Details in U1.5-UNITY.md). +- Kerntests und Player auf Windows 11 und Fedora 44 stehen deshalb aus. ## Entscheidungen - **Regelkern ohne Unity** (`Luneraria.Core`, `noEngineReferences`). Unity wird Adapter und Präsentation. -- **Verhaltensgleichheit statt Bitgleichheit.** Verglichen wird mit dokumentierten Toleranzen; gemessen wurde auf dem Mac trotzdem Abweichung 0. +- **Verhaltensgleichheit statt Bitgleichheit.** Verglichen wird mit dokumentierten Toleranzen; gemessen wurde auf dem Mac trotzdem Abweichung 0, in .NET 8 und im Unity-Editor. +- **Explizite float32-Rundung in `Vec2` (U1.5).** Unitys Mono rechnet float32-Zwischenwerte doppelt genau. Das änderte in einem Szenario eine Zielentscheidung, deshalb ist eine Toleranz hier nicht begründbar. Die Casts erzwingen Godots float32-Semantik auf jeder Laufzeit; Regeln und .NET-Ergebnisse bleiben unverändert. - **Tabellen** einmal aus Godot erzeugt (`tools/port/gen_content.py`); danach ist `ContentData.cs` die gepflegte Quelle. - **Referenzfehler R1** im Bot-Gedächtnis ist bewusst übernommen (Parität) und für eine spätere Bot-Phase vorgemerkt. - **Geplante Helden** (Lucy, Ghaliga, Siegfried, Feena) stehen im Datenmodell, sind aber inaktiv, bis eine Heldenphase ihre Regeln umsetzt. @@ -32,8 +36,9 @@ QA je Meilenstein: [PORT-QA.md](PORT-QA.md). ## Arbeitsweise -- Ein Zweig je Meilenstein: `luneraria/u1`, `luneraria/u2`, … Nach jedem Meilenstein kommen Tests, Bild, Leistung, bekannte Unterschiede und ein Commit. +- Ein Zweig je Meilenstein: `luneraria/u1`, `luneraria/u1.5`, `luneraria/u2`, … Nach jedem Meilenstein kommen Tests, Bild, Leistung, bekannte Unterschiede und ein Commit. - Werkzeuge: - - `tools/run_core_tests.sh` (Kern). + - `tools/run_core_tests.sh` (Kern mit .NET). + - Unity Test Runner (EditMode) und `CoreBatch` im Batchmodus (Kern im Editor). - `tools/godot-golden/run_godot.sh` (Godot-Referenz). - - `tools/dotnet/Luneraria.Core.Bench` (Leistung). + - `tools/dotnet/Luneraria.Core.Bench` und `CoreBatch.Benchmark` (Leistung). diff --git a/docs/port/U1-RULES.md b/docs/port/U1-RULES.md index 3136291..f8f0d4f 100644 --- a/docs/port/U1-RULES.md +++ b/docs/port/U1-RULES.md @@ -72,6 +72,8 @@ Bitgleichheit ist **kein Ziel**. Verglichen wird fachlich: Auf anderen Laufzeiten (Unity-Mono, IL2CPP, Windows x64, Linux) sind winzige Rundungsunterschiede möglich; dafür sind die Toleranzen da. Weil der Host autoritativ rechnet, müssen Clients die Simulation nicht selbst nachrechnen. +**Nachtrag U1.5:** Unitys Mono rechnet float32-Zwischenwerte doppelt genau. Das änderte in einem Szenario eine Zielentscheidung, deshalb rundet `Vec2` seitdem jeden Zwischenwert explizit auf float32. Die .NET-Ergebnisse blieben byteidentisch; im Unity-Editor ist die Abweichung jetzt überall 0. Details: [U1.5-UNITY.md](U1.5-UNITY.md#float-befund). + ## Bewusst andere technische Repräsentation | Godot | C# | Warum gleichwertig | @@ -133,7 +135,7 @@ Bildnachweis: `docs/port/screenshots/u1/replay-*.png` zeigt die Karte mit den Go sh tools/run_core_tests.sh ``` -Mit `REGENERATE_GOLDEN=1` werden vorher die Godot-Referenzen neu erzeugt. Das braucht `../saumwacht-godot`; das Referenz-Repository bleibt unverändert. Protokoll: `docs/port/logs/u1/core-tests.json`. Im Unity Test Runner laufen dieselben Suiten über `Luneraria.Core.Tests` (EditMode), sobald die Lizenz aktiv ist. +Mit `REGENERATE_GOLDEN=1` werden vorher die Godot-Referenzen neu erzeugt. Das braucht `../saumwacht-godot`; das Referenz-Repository bleibt unverändert. Protokoll: `docs/port/logs/u1/core-tests.json`. Seit U1.5 schreibt das Skript sein Protokoll nach `$LUNE_TEST_REPORT` (Standard `tools/out/`). Im Unity Test Runner laufen dieselben Suiten über `Luneraria.Core.Tests` (EditMode); Ergebnis in U1.5-UNITY.md. ## Leistung @@ -145,7 +147,7 @@ Gleiche Aufstellung wie die Godot-Benchmark-Probe: Runde 13, sechs Armeen am Ver | Bot-Partien Seeds 1–3 (32 Runden) | 20,7 s | 1,17 s | 17,8 × | Rohdaten: `docs/port/logs/u1/sim-bench-{godot,csharp}.json`. -- Unitys Editor läuft mit Mono; die Messung dort folgt mit U2. +- Unitys Editor läuft mit Mono. Die Messung dort steht in [U1.5-UNITY.md](U1.5-UNITY.md#leistung): 0,51 ms je Schritt im Release-Modus. - Der Faktor gegenüber Godot ist ein Anhaltspunkt, keine Zusage für den Player. ## Geplante Helden @@ -165,6 +167,6 @@ Für die Umsetzung sind neue Regeln nötig: ## Offene Punkte -- Unity-Lizenz aktivieren, dann Kern und Tests im Editor (Mono) laufen lassen und die Mono-Leistung messen. +- ~~Unity-Lizenz aktivieren, dann Kern und Tests im Editor (Mono) laufen lassen und die Mono-Leistung messen.~~ Erledigt in U1.5. - Kerntests auf Windows 11 und Fedora 44 ausführen (Zielplattformen). Der Kern ist plattformneutral, der Lauf dort steht aber aus. - R1 später korrigieren (siehe oben). diff --git a/docs/port/U1.5-UNITY.md b/docs/port/U1.5-UNITY.md new file mode 100644 index 0000000..aed8ef5 --- /dev/null +++ b/docs/port/U1.5-UNITY.md @@ -0,0 +1,240 @@ +# U1.5 · Unity-Integrationsprüfung + +Stand: 30. September 2026. Ergebnis: **grün**. Der Regelkern aus U1 läuft im Unity-Editor mit allen 402 Prüfungen. Die Golden-Szenarien sind bitgleich zur Godot-Referenz. Dafür war eine rein numerische Korrektur in `Vec2` nötig; Regeln und R1 sind unverändert (Abschnitt „Float-Befund“). + +## Umgebung + +| Punkt | Wert | +| --- | --- | +| Unity | 6000.3.20f1 (Revision c9ba695d4f07) = Unity 6.3 LTS, Apple-Silicon-Editor | +| Lizenz | Unity Personal (Typ „Assigned“, unbefristet; laut Editor-Log). `HasPro` = false | +| URP | 17.3.0; Core RP 17.3.0, Shader Graph 17.3.0 | +| Editor-Laufzeit | Mono 6.13.0 (explicit/dc7ab1aa) | +| Scripting Backend (Standalone) | Mono (`Mono2x`); IL2CPP ist nicht eingestellt | +| API Compatibility Level | .NET Standard 2.1. Unity-Enum-Wert 6; `ToString()` zeigt den Altnamen `NET_Standard_2_0`, der kompilierte Kern referenziert `netstandard 2.1.0.0` | +| Sprachstand | C# 9 (Unity-Standard; der .NET-Harness baut mit derselben Stufe) | +| Code Optimization | Batchmodus: Release. Interaktiver Editor: Debug. Beide Modi sind geprüft (`-debugCodeOptimization`) | +| Test Framework | com.unity.test-framework 1.6.0 (NUnit-Engine 3.5) | +| Farbraum, Render-Pipeline-Asset | Gamma, keines zugewiesen. Beides ist Teil von U2B (Linear, URP-Asset) | +| Build-Module | nur macOS. Windows und Linux fehlen (Abschnitt „Fehlende Build-Module“) | +| Rechner | Mac mit Apple M5, macOS 27.0 | + +Rohdaten: `logs/u1_5/unity-environment.json`. + +## Projektstruktur + +| Pfad | Inhalt | +| --- | --- | +| `Assets/Luneraria/Core/` | Regelkern, Assembly `Luneraria.Core` mit `noEngineReferences` | +| `Assets/Luneraria/Tests/Core/` | Suiten, Golden-Dateien, `Diagnostics/` (Float-Probe, Schritt-Trace, Benchmark), `Unity/` (Test-Runner-Adapter, Batch-Einstiege). Assembly `Luneraria.Core.Tests`, nur Editor | +| `Assets/UniversalRenderPipelineGlobalSettings.asset`, `Assets/DefaultVolumeProfile.asset` | von URP beim ersten Öffnen angelegt; U2B räumt sie mit dem URP-Asset nach `Assets/Luneraria/Settings/` | +| `ProjectSettings/`, `Packages/packages-lock.json` | von Unity 6.3 erzeugt und eingecheckt | +| `tools/dotnet/` | .NET-Harness. Baut nach `tools/out/`, weil Unity `Temp/` beim Beenden löscht | + +Alle Dateien unter `Assets/` haben `.meta`-Dateien; die GUIDs sind eingecheckt. + +## Pakete + +Unity hat beim ersten Öffnen (ohne `ProjectSettings/`) die Vorlagenpakete eines neuen Projekts in das Manifest geschrieben. Das Manifest ist wieder auf den geplanten Umfang zurückgeführt. + +Behalten sind: +- URP und Shader Graph. +- Input System, uGUI, Recorder (zieht Timeline nach) und Newtonsoft JSON. +- Test Framework. +- Die IDE-Integrationen für Rider und Visual Studio/VS Code (nur Editor). +- Die Engine-Module aus U1. + +Entfernt sind: +- Purchasing und Analytics (samt Unity Services). +- Collab-Proxy (Unity Version Control), Multiplayer Center. +- XR Legacy Input Helpers, 2D Sprite/Tilemap, AI Navigation. +- 25 zusätzliche Moduleinträge. + +Gründe: +- Keine Telemetrie und keine In-App-Käufe. +- Versionsverwaltung ist Git. +- Die Bewegung gehört dem Kern: Ein NavMesh in der Szene würde Regeln in die Darstellung ziehen. + +Beim Entfernen wurde nichts heruntergeladen. Beim ersten Öffnen hatte Unity die Vorlagenpakete aus der Registry geholt (`upm.log`). + +## Kern ohne Engine-Abhängigkeit + +- `Luneraria.Core.asmdef` setzt `noEngineReferences: true`: Jede Verwendung von `UnityEngine` wäre ein Kompilierfehler. +- Die in Unity kompilierte Assembly referenziert nur `netstandard 2.1.0.0`. Der Test `CoreIntegrationTests.CoreHasNoEngineReferences` prüft das bei jedem Lauf. +- Der Kern kompiliert in Unity ohne Warnung. + +## Tests im Unity Test Runner + +Die U1-Suiten laufen unverändert als EditMode-Tests (`CoreSuiteTests`), mit denselben Prüfungen und Werten. Die Golden-Referenzen wurden nicht neu erzeugt. + +| Suite | Prüfungen | Editor, Release | Editor, Debug | .NET 8 | +| --- | ---: | --- | --- | --- | +| `content` | 75 | grün, 0,01 s | grün, 0,01 s | grün | +| `core` | 91 | grün, 5,3 s | grün, 11,9 s | grün | +| `m2` | 73 | grün, 3,4 s | grün, 6,2 s | grün | +| `golden` | 157 | grün, 13,4 s | grün, 24,5 s | grün | +| `determinism` | 6 | grün, 13,4 s | grün, 24,8 s | grün | +| **gesamt** | **402** | **402/402** | **402/402** | **402/402** | + +Dazu kommen zwei Unity-spezifische Integrationstests (`CoreIntegrationTests`), beide grün: +- **Keine Engine-Referenzen:** Der Kern referenziert keine Unity-Assembly. +- **Float-Arithmetik wie Godot:** Alle `Vec2`-Operationen stimmen in 4.000 von 4.000 Stichproben mit Godot überein. + +Protokolle: +- `logs/u1_5/editmode-results-{release,debug}.xml` (NUnit). +- `logs/u1_5/core-tests-unity-editor-{release,debug}.json` und `core-tests-dotnet8.json`: gleiches Format, mit den Golden-Notizen. + +## Golden-Vergleich in Unity + +Verglichen werden 19 Szenarien, 120 Snapshots und 51.221 Werte (Umfang siehe U1-RULES.md). Neu ist die Auswertung je Fachbereich; sie ist reiner Bericht, Prüfungen und Toleranzen sind unverändert. + +| Bereich | Werte | Abweichungen im Editor | größte Zahlenabweichung | +| --- | ---: | ---: | ---: | +| Gold (Konten, Entwurf, Einnahmen, Ausgaben, Gewinne, Einkommen) | 3.584 | 0 | 0 | +| HP (Einheiten, Schilde) | 3.420 | 0 | 0 | +| Positionen und Lanefortschritt | 3.420 | 0 | 0 | +| Türme (Gebäude-HP, verlorene Gebäude) | 1.994 | 0 | 0 | +| Ancients (HP, zerstörte Kerne) | 246 | 0 | 0 | +| Wellen (Zusammensetzung, Werte, Kills je Lane, Truppenzahl) | 731 | 0 | 0 | +| Helden (Stufe, Erfahrung, Talente, Items) | 1.512 | 0 | 0 | +| Boss (Zustand, Anspruch, Log, Grotte, Boss-HP) | 1.559 | 0 | 0 | +| Gewinner, Phase, Endgrund | 695 | 0 | 0 | +| Ziele | 1.140 | 0 | 0 | +| Blickrichtung | 1.140 | 0 | 0 | +| Käufe, Armeen, Buchungen | 9.311 | 0 | 0 | +| Ereignisse | 6.937 | 0 | 0 | +| übrige Einheitenfelder, Kampf, Befehle, Runden, Ankündigungen | 15.532 | 0 | 0 | + +Das gilt für Release und Debug. Die Summe der Tabelle ist 51.221. + +## Float-Befund + +### Erster Lauf: nicht bitgleich + +Der erste Lauf im Editor fand in 5 von 157 Golden-Prüfungen Unterschiede (`logs/u1_5/editmode-results-before-fix.xml`): + +| Snapshot | Unterschied | +| --- | --- | +| `start_three_lanes/t10`, `lane_both_lost/t35`, `base_tower_attack/t60`, `high_wave_21/t20` | Blickrichtung bis 1,6·10⁻³ rad (Toleranz 10⁻⁴) | +| `mirror_match/round-7` | Kampfdauer 46,10 s statt 41,30 s (922 statt 826 Schritte) | + +Weitere Messwerte: +- Positionen wichen bis 0,22 mm ab (Toleranz 1 mm). +- Die berechnete Lanegeometrie wich um 2,4·10⁻⁷ m ab. +- Gold, HP, Türme, Ancients, Helden, Boss und Gewinner waren in allen Snapshots gleich. + +### Ursache + +Unitys Mono 6.13 wertet float32-Ausdrücke mit Zwischenwerten doppelter Genauigkeit aus und rundet erst beim Speichern. ECMA-335 (Partition I, 12.1.3) erlaubt das. .NET 8 und Godot runden nach jeder Operation. + +Nachweis mit `FloatProbe`: Er rechnet die 4.000 Eingaben von `tools/godot-golden/float_probe.gd` mit den Kernfunktionen und vergleicht die Bits mit Godot. + +| Laufzeit | `x*x + y*y` wie … | `a + (b − a)·w` wie … | +| --- | --- | --- | +| .NET 8 | float32 je Operation: 4.000/4.000 | float32 je Operation: 4.000/4.000 | +| Unity-Mono | doppelt genauer Zwischenwert: 4.000/4.000 (float32 nur 3.049, genau dort, wo beide Varianten zusammenfallen) | doppelt genauer Zwischenwert: 4.000/4.000 (float32 nur 2.620) | + +Kernoperationen vor der Korrektur, identisch mit Godot: + +| Operation | Treffer | +| --- | ---: | +| `Length` | 3.520/4.000 | +| `LengthSquared` | 3.049 | +| `DistanceTo` | 3.260 | +| `DistanceSquaredTo` | 2.578 | +| `Dot` | 2.560 | +| `Normalized` | 3.524 | +| `Lerp` | 1.786 | +| `LimitLength` | 3.530 | +| Einzeloperationen (`*`, `/`, `atan2`) | 4.000 | + +Die Abweichung liegt bei 1–2 ulp. Bei Auslöschung nahe null sind es bis 1.709 ulp, absolut aber winzig. + +Rohdaten: `logs/u1_5/float-probe-*.json`. + +### Wirkung auf Entscheidungen + +Ja, die Unterschiede ändern Entscheidungen. `DivergenceTrace` protokolliert `mirror_match` Runde 7 in beiden Laufzeiten Schritt für Schritt; `tools/port/trace_diff.py` vergleicht: + +- **Ab Schritt 0:** Die Positionsbits unterscheiden sich um höchstens 1,9 µm, bis Schritt 450 um höchstens 8 µm. +- **Ab etwa Schritt 470:** Die gegenseitige Verdrängung der Einheiten auf der oberen Lane verstärkt den Unterschied auf 3 mm (Schritt 475), 19 mm (500) und 17 cm (600). +- **Schritt 599 (t = 29,95 s):** Einheit 54 (Truppe Blau, obere Lane) behält in der Referenz Ziel 37 und hat in Unity keines. Der Kampf endet 4,8 s später. Beute, Gebäude, Erfahrung und Sieger dieser Runde blieben zufällig gleich. + +Die Blickrichtung wird vom Kern nur geschrieben, nie gelesen; sie ist ein reiner Darstellungswert. Ihre Abweichung folgt aus den Sub-Millimeter-Positionen, weil die Richtung schon ab 1 mm Bewegung neu gesetzt wird. + +### Bewertung und Maßnahme + +**Eine Toleranz ist hier fachlich nicht begründbar.** Die Kampfdynamik verstärkt Unterschiede im letzten Bit bis zu anderen Zielentscheidungen. Eine größere Toleranz würde das nur verdecken. Für die Blickrichtung allein wäre eine weitere Toleranz vertretbar; sie ist aber nicht nötig. + +**Maßnahme:** `Vec2` rundet jeden Zwischenwert explizit auf float32 (`(float)` → `conv.r4`); ECMA-335 garantiert diese Rundung. +- Die Änderung betrifft nur `Assets/Luneraria/Core/Model/Vec2.cs`. Alle anderen Regelgrößen sind double, und float-Ergebnisse werden dort exakt erweitert. +- Keine Regel ändert sich, R1 bleibt. +- Das ist keine Anpassung an .NET: Maßstab ist Godots Vector2 mit float32-Arithmetik. Die Casts erzwingen genau diese Semantik auf jeder ECMA-Laufzeit: Mono im Editor und im Player, IL2CPP, .NET. +- Doppelte Rundung ist dabei unschädlich. Bei +, −, ×, ÷ und √ hat double (53 Bit) mindestens 2·24 + 2 Bit, also ist jede über double gerundete Operation exakt das float32-Ergebnis (Figueroa, 1995). + +**Nachweis:** + +| Prüfung | Ergebnis | +| --- | --- | +| .NET 8 vorher/nachher | 402/402 und Golden-Abweichung 0 unverändert; Trace `mirror_match` Runde 7 byteidentisch (SHA-256 gleich) | +| Unity-Editor nachher, Release und Debug | 402/402, Golden-Abweichung 0 in allen Kategorien und Bereichen; Lanegeometrie 0 m | +| Float-Probe nachher | alle Kernoperationen 4.000/4.000 in .NET, Editor-Release und Editor-Debug | +| Trace nachher | Unity = .NET: 826 Schritte, kein abweichendes Bit | +| Absicherung | `CoreIntegrationTests.CoreFloatArithmeticMatchesGodot` im Test Runner | + +Die Laufzeit selbst rechnet weiter doppelt genau; die Probe zeigt das in `evaluation_matches`. Der Kern ist davon jetzt unabhängig. + +**Offen:** IL2CPP sowie Player unter Windows 11 und Fedora 44 sind nicht gemessen, weil die Build-Module fehlen. Erwartet wird Gleichheit, weil die Rundung jetzt explizit ist. Der Nachweis steht aus. + +## Leistung + +Das ist ein Editor-Benchmark, kein Player-Wert: Mono-JIT im Editor, gemessen im Batchmodus. Fixture wie in U1: Runde 13, sechs Armeen am Versorgungslimit, 120 bewegliche Einheiten; dazu die Bot-Partien der Seeds 1–3 (32 Runden). Alle Läufe fanden nacheinander auf demselben Mac statt. + +| Laufzeit | Simulationsschritt (Median 5 × 200) | Bot-Partien Seeds 1–3 | +| --- | ---: | ---: | +| Godot 4.7.2, GDScript | 1,600 ms | 21,0 s | +| Unity-Editor, Mono, Debug | 1,004 ms | 11,0 s | +| **Unity-Editor, Mono, Release** | **0,514–0,518 ms** | **6,76–6,82 s** | +| .NET 8, Release | 0,24–0,26 ms | 1,20–1,55 s | + +Einordnung: +- Im Editor ist der Kern 3,1-mal so schnell wie Godot und halb so schnell wie .NET 8. +- Ein Schritt deckt 50 ms Spielzeit ab. 0,52 ms sind damit rund 1 % eines Kerns. +- Der größere Abstand bei den Bot-Partien kommt aus der Bot-Planung mit vielen Zuweisungen (Mono-GC). +- Player-Werte (Mono/IL2CPP) folgen, sobald die Build-Module installiert sind. + +Rohdaten: `logs/u1_5/sim-bench-*.json`. + +## Fehlende Build-Module + +Im Editor 6000.3.20f1 ist nur `MacStandaloneSupport` installiert. Für die Zielplattformen fehlen die folgenden Unity-Hub-Module. Sie werden erst nach Freigabe installiert: + +| Modul (Unity Hub) | Hub-ID | wofür | +| --- | --- | --- | +| Windows Build Support (Mono) | `windows-mono` | Windows-11-Player (x64) vom Mac aus | +| Linux Build Support (Mono) | `linux-mono` | Fedora-44-Player (x86_64) vom Mac aus | +| Linux Build Support (IL2CPP) | `linux-il2cpp` | optional: IL2CPP-Player für Linux; braucht zusätzlich die Linux-Toolchain aus dem Package Manager (Download) | +| Linux Dedicated Server Build Support | `linux-server` | optional, erst für U4: dedizierter Host ohne Grafik | + +Windows-IL2CPP lässt sich nur auf einem Windows-Rechner bauen, nicht vom Mac aus. + +Hinweis zu den Testrechnern: Die vorhandenen Parallels-VMs sind ARM. Windows 11 ARM führt x64-Player per Emulation aus. Fedora ARM kann einen x86_64-Linux-Player nicht direkt starten; dafür braucht es ein x86_64-System. + +## Befehle + +```bash +UNITY=/Applications/Unity/Hub/Editor/6000.3.20f1/Unity.app/Contents/MacOS/Unity +``` + +```bash +LUNE_TEST_REPORT="$PWD/docs/port/logs/u1_5/core-tests-unity-editor-release.json" "$UNITY" -batchmode -projectPath "$PWD" -runTests -testPlatform EditMode -testResults "$PWD/docs/port/logs/u1_5/editmode-results-release.xml" -logFile - +``` + +```bash +"$UNITY" -batchmode -projectPath "$PWD" -executeMethod Luneraria.Core.Tests.CoreBatch.Benchmark -logFile - +``` + +Weitere Einstiege: +- `CoreBatch.FloatProbe`, `CoreBatch.EnvironmentReport`, `CoreBatch.TraceMirrorMatch`. +- Mit `-debugCodeOptimization` läuft der Editor im Debug-Modus. +- .NET: `sh tools/run_core_tests.sh`, `--float-probe `, `--trace-mirror `; Vergleich mit `python3 tools/port/trace_diff.py `. diff --git a/docs/port/logs/u1_5/core-tests-dotnet8.json b/docs/port/logs/u1_5/core-tests-dotnet8.json new file mode 100644 index 0000000..4031bc7 --- /dev/null +++ b/docs/port/logs/u1_5/core-tests-dotnet8.json @@ -0,0 +1,12 @@ +{ + "suites": [ + {"suite": "content", "checks": 75, "failures": 0, "seconds": 0.02, "failed": [], "notes": ["map geometry: largest deviation lanes 0.00E+000 m, buildings 0.00E+000 m"]}, + {"suite": "core", "checks": 91, "failures": 0, "seconds": 1.70, "failed": [], "notes": ["full match seed 2 -> Rot after 9 rounds"]}, + {"suite": "m2", "checks": 73, "failures": 0, "seconds": 0.39, "failed": [], "notes": []}, + {"suite": "golden", "checks": 157, "failures": 0, "seconds": 1.49, "failed": [], "notes": ["golden values compared: 51221", "largest deviation angle: 0.00E+000 over 1140 values (tolerance 1E-004)", "largest deviation effect: 0.00E+000 over 264 values (tolerance 1E-006)", "largest deviation hp: 0.00E+000 over 5679 values (tolerance 1E-002)", "largest deviation other: 0.00E+000 over 34 values (tolerance 1E-006)", "largest deviation position: 0.00E+000 over 3420 values (tolerance 1E-003)", "largest deviation time: 0.00E+000 over 780 values (tolerance 1E-006)", "domain ancients: 246 values, 0 differences, largest numeric deviation 0.00E+000", "domain announcements: 44 values, 0 differences, largest numeric deviation 0.00E+000", "domain boss: 1559 values, 0 differences, largest numeric deviation 0.00E+000", "domain combat: 676 values, 0 differences, largest numeric deviation 0.00E+000", "domain commands: 410 values, 0 differences, largest numeric deviation 0.00E+000", "domain events: 6937 values, 0 differences, largest numeric deviation 0.00E+000", "domain facing: 1140 values, 0 differences, largest numeric deviation 0.00E+000", "domain gold: 3584 values, 0 differences, largest numeric deviation 0.00E+000", "domain heroes: 1512 values, 0 differences, largest numeric deviation 0.00E+000", "domain hp: 3420 values, 0 differences, largest numeric deviation 0.00E+000", "domain other: 119 values, 0 differences, largest numeric deviation 0.00E+000", "domain positions: 3420 values, 0 differences, largest numeric deviation 0.00E+000", "domain purchases: 9311 values, 0 differences, largest numeric deviation 0.00E+000", "domain rounds: 119 values, 0 differences, largest numeric deviation 0.00E+000", "domain targets: 1140 values, 0 differences, largest numeric deviation 0.00E+000", "domain towers: 1994 values, 0 differences, largest numeric deviation 0.00E+000", "domain units: 14164 values, 0 differences, largest numeric deviation 0.00E+000", "domain waves: 731 values, 0 differences, largest numeric deviation 0.00E+000", "domain winner: 695 values, 0 differences, largest numeric deviation 0.00E+000"]}, + {"suite": "determinism", "checks": 6, "failures": 0, "seconds": 1.51, "failed": [], "notes": []} + ], + "runtime": ".NET 8.0.27", + "label": ".NET 8.0.27, Release", + "os": "Darwin 27.0.0 Darwin Kernel Version 27.0.0: Tue Aug 11 21:02:59 PDT 2026; root:xnu-13432.1.9~1/RELEASE_ARM64_T8142 Arm64" +} diff --git a/docs/port/logs/u1_5/core-tests-unity-editor-debug.json b/docs/port/logs/u1_5/core-tests-unity-editor-debug.json new file mode 100644 index 0000000..907578e --- /dev/null +++ b/docs/port/logs/u1_5/core-tests-unity-editor-debug.json @@ -0,0 +1,12 @@ +{ + "suites": [ + {"suite": "content", "checks": 75, "failures": 0, "seconds": 0.01, "failed": [], "notes": ["map geometry: largest deviation lanes 0.00E+000 m, buildings 0.00E+000 m"]}, + {"suite": "core", "checks": 91, "failures": 0, "seconds": 11.91, "failed": [], "notes": ["full match seed 2 -> Rot after 9 rounds"]}, + {"suite": "determinism", "checks": 6, "failures": 0, "seconds": 24.76, "failed": [], "notes": []}, + {"suite": "golden", "checks": 157, "failures": 0, "seconds": 24.49, "failed": [], "notes": ["golden values compared: 51221", "largest deviation angle: 0.00E+000 over 1140 values (tolerance 1E-004)", "largest deviation effect: 0.00E+000 over 264 values (tolerance 1E-006)", "largest deviation hp: 0.00E+000 over 5679 values (tolerance 1E-002)", "largest deviation other: 0.00E+000 over 34 values (tolerance 1E-006)", "largest deviation position: 0.00E+000 over 3420 values (tolerance 1E-003)", "largest deviation time: 0.00E+000 over 780 values (tolerance 1E-006)", "domain ancients: 246 values, 0 differences, largest numeric deviation 0.00E+000", "domain announcements: 44 values, 0 differences, largest numeric deviation 0.00E+000", "domain boss: 1559 values, 0 differences, largest numeric deviation 0.00E+000", "domain combat: 676 values, 0 differences, largest numeric deviation 0.00E+000", "domain commands: 410 values, 0 differences, largest numeric deviation 0.00E+000", "domain events: 6937 values, 0 differences, largest numeric deviation 0.00E+000", "domain facing: 1140 values, 0 differences, largest numeric deviation 0.00E+000", "domain gold: 3584 values, 0 differences, largest numeric deviation 0.00E+000", "domain heroes: 1512 values, 0 differences, largest numeric deviation 0.00E+000", "domain hp: 3420 values, 0 differences, largest numeric deviation 0.00E+000", "domain other: 119 values, 0 differences, largest numeric deviation 0.00E+000", "domain positions: 3420 values, 0 differences, largest numeric deviation 0.00E+000", "domain purchases: 9311 values, 0 differences, largest numeric deviation 0.00E+000", "domain rounds: 119 values, 0 differences, largest numeric deviation 0.00E+000", "domain targets: 1140 values, 0 differences, largest numeric deviation 0.00E+000", "domain towers: 1994 values, 0 differences, largest numeric deviation 0.00E+000", "domain units: 14164 values, 0 differences, largest numeric deviation 0.00E+000", "domain waves: 731 values, 0 differences, largest numeric deviation 0.00E+000", "domain winner: 695 values, 0 differences, largest numeric deviation 0.00E+000"]}, + {"suite": "m2", "checks": 73, "failures": 0, "seconds": 6.17, "failed": [], "notes": []} + ], + "runtime": "Mono 6.13.0 (explicit/dc7ab1aa)", + "label": "Unity 6000.3.20f1 editor, Mono 6.13.0 (explicit/dc7ab1aa), code optimization Debug", + "os": "Unix 27.0.0.0 Arm64" +} diff --git a/docs/port/logs/u1_5/core-tests-unity-editor-release.json b/docs/port/logs/u1_5/core-tests-unity-editor-release.json new file mode 100644 index 0000000..10e4087 --- /dev/null +++ b/docs/port/logs/u1_5/core-tests-unity-editor-release.json @@ -0,0 +1,12 @@ +{ + "suites": [ + {"suite": "content", "checks": 75, "failures": 0, "seconds": 0.01, "failed": [], "notes": ["map geometry: largest deviation lanes 0.00E+000 m, buildings 0.00E+000 m"]}, + {"suite": "core", "checks": 91, "failures": 0, "seconds": 5.28, "failed": [], "notes": ["full match seed 2 -> Rot after 9 rounds"]}, + {"suite": "determinism", "checks": 6, "failures": 0, "seconds": 13.44, "failed": [], "notes": []}, + {"suite": "golden", "checks": 157, "failures": 0, "seconds": 13.37, "failed": [], "notes": ["golden values compared: 51221", "largest deviation angle: 0.00E+000 over 1140 values (tolerance 1E-004)", "largest deviation effect: 0.00E+000 over 264 values (tolerance 1E-006)", "largest deviation hp: 0.00E+000 over 5679 values (tolerance 1E-002)", "largest deviation other: 0.00E+000 over 34 values (tolerance 1E-006)", "largest deviation position: 0.00E+000 over 3420 values (tolerance 1E-003)", "largest deviation time: 0.00E+000 over 780 values (tolerance 1E-006)", "domain ancients: 246 values, 0 differences, largest numeric deviation 0.00E+000", "domain announcements: 44 values, 0 differences, largest numeric deviation 0.00E+000", "domain boss: 1559 values, 0 differences, largest numeric deviation 0.00E+000", "domain combat: 676 values, 0 differences, largest numeric deviation 0.00E+000", "domain commands: 410 values, 0 differences, largest numeric deviation 0.00E+000", "domain events: 6937 values, 0 differences, largest numeric deviation 0.00E+000", "domain facing: 1140 values, 0 differences, largest numeric deviation 0.00E+000", "domain gold: 3584 values, 0 differences, largest numeric deviation 0.00E+000", "domain heroes: 1512 values, 0 differences, largest numeric deviation 0.00E+000", "domain hp: 3420 values, 0 differences, largest numeric deviation 0.00E+000", "domain other: 119 values, 0 differences, largest numeric deviation 0.00E+000", "domain positions: 3420 values, 0 differences, largest numeric deviation 0.00E+000", "domain purchases: 9311 values, 0 differences, largest numeric deviation 0.00E+000", "domain rounds: 119 values, 0 differences, largest numeric deviation 0.00E+000", "domain targets: 1140 values, 0 differences, largest numeric deviation 0.00E+000", "domain towers: 1994 values, 0 differences, largest numeric deviation 0.00E+000", "domain units: 14164 values, 0 differences, largest numeric deviation 0.00E+000", "domain waves: 731 values, 0 differences, largest numeric deviation 0.00E+000", "domain winner: 695 values, 0 differences, largest numeric deviation 0.00E+000"]}, + {"suite": "m2", "checks": 73, "failures": 0, "seconds": 3.36, "failed": [], "notes": []} + ], + "runtime": "Mono 6.13.0 (explicit/dc7ab1aa)", + "label": "Unity 6000.3.20f1 editor, Mono 6.13.0 (explicit/dc7ab1aa), code optimization Release", + "os": "Unix 27.0.0.0 Arm64" +} diff --git a/docs/port/logs/u1_5/editmode-results-before-fix.xml b/docs/port/logs/u1_5/editmode-results-before-fix.xml new file mode 100644 index 0000000..fefeafb --- /dev/null +++ b/docs/port/logs/u1_5/editmode-results-before-fix.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Rot after 9 rounds +LUNE_U1_PASS core: 91 checks +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/port/logs/u1_5/editmode-results-debug.xml b/docs/port/logs/u1_5/editmode-results-debug.xml new file mode 100644 index 0000000..c9449d4 --- /dev/null +++ b/docs/port/logs/u1_5/editmode-results-debug.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Rot after 9 rounds +LUNE_U1_PASS core: 91 checks +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/port/logs/u1_5/editmode-results-release.xml b/docs/port/logs/u1_5/editmode-results-release.xml new file mode 100644 index 0000000..a0ba912 --- /dev/null +++ b/docs/port/logs/u1_5/editmode-results-release.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Rot after 9 rounds +LUNE_U1_PASS core: 91 checks +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/port/logs/u1_5/float-probe-dotnet8.json b/docs/port/logs/u1_5/float-probe-dotnet8.json new file mode 100644 index 0000000..99ec0be --- /dev/null +++ b/docs/port/logs/u1_5/float-probe-dotnet8.json @@ -0,0 +1,42 @@ +{ + "runtime": ".NET 8.0.27", + "label": ".NET 8.0.27, Release", + "rows": 4000, + "identical_to_godot": { + "length": 4000, + "length_squared": 4000, + "distance_to": 4000, + "distance_squared_to": 4000, + "dot": 4000, + "normalized": 4000, + "lerp": 4000, + "limit_length": 4000, + "multiply": 4000, + "divide": 4000, + "sub_normalized": 4000, + "atan2": 4000 + }, + "worst_ulps": { + "length": 0, + "length_squared": 0, + "distance_to": 0, + "distance_squared_to": 0, + "dot": 0, + "normalized": 0, + "lerp": 0, + "limit_length": 0, + "multiply": 0, + "divide": 0, + "sub_normalized": 0 + }, + "evaluation_samples": 4000, + "evaluation_matches": { + "sum_of_squares_strict_float32": 4000, + "sum_of_squares_double_intermediate": 3049, + "sum_of_squares_fused": 3368, + "sum_of_squares_candidates_differ": 951, + "lerp_strict_float32": 4000, + "lerp_double_intermediate": 2620, + "lerp_candidates_differ": 1380 + } +} diff --git a/docs/port/logs/u1_5/float-probe-unity-debug.json b/docs/port/logs/u1_5/float-probe-unity-debug.json new file mode 100644 index 0000000..0520211 --- /dev/null +++ b/docs/port/logs/u1_5/float-probe-unity-debug.json @@ -0,0 +1,42 @@ +{ + "runtime": "Mono 6.13.0 (explicit/dc7ab1aa)", + "label": "Unity 6000.3.20f1 editor, Mono 6.13.0 (explicit/dc7ab1aa), code optimization Debug", + "rows": 4000, + "identical_to_godot": { + "length": 4000, + "length_squared": 4000, + "distance_to": 4000, + "distance_squared_to": 4000, + "dot": 4000, + "normalized": 4000, + "lerp": 4000, + "limit_length": 4000, + "multiply": 4000, + "divide": 4000, + "sub_normalized": 4000, + "atan2": 4000 + }, + "worst_ulps": { + "length": 0, + "length_squared": 0, + "distance_to": 0, + "distance_squared_to": 0, + "dot": 0, + "normalized": 0, + "lerp": 0, + "limit_length": 0, + "multiply": 0, + "divide": 0, + "sub_normalized": 0 + }, + "evaluation_samples": 4000, + "evaluation_matches": { + "sum_of_squares_strict_float32": 3049, + "sum_of_squares_double_intermediate": 4000, + "sum_of_squares_fused": 3503, + "sum_of_squares_candidates_differ": 951, + "lerp_strict_float32": 2620, + "lerp_double_intermediate": 4000, + "lerp_candidates_differ": 1380 + } +} diff --git a/docs/port/logs/u1_5/float-probe-unity-release-before-fix.json b/docs/port/logs/u1_5/float-probe-unity-release-before-fix.json new file mode 100644 index 0000000..decae68 --- /dev/null +++ b/docs/port/logs/u1_5/float-probe-unity-release-before-fix.json @@ -0,0 +1,42 @@ +{ + "runtime": "Mono 6.13.0 (explicit/dc7ab1aa)", + "label": "Unity 6000.3.20f1 editor, Mono 6.13.0 (explicit/dc7ab1aa), code optimization Release", + "rows": 4000, + "identical_to_godot": { + "length": 3520, + "length_squared": 3049, + "distance_to": 3260, + "distance_squared_to": 2578, + "dot": 2560, + "normalized": 3524, + "lerp": 1786, + "limit_length": 3530, + "multiply": 4000, + "divide": 4000, + "sub_normalized": 3564, + "atan2": 4000 + }, + "worst_ulps": { + "length": 1, + "length_squared": 1, + "distance_to": 2, + "distance_squared_to": 2, + "dot": 1177, + "normalized": 2, + "lerp": 1709, + "limit_length": 2, + "multiply": 0, + "divide": 0, + "sub_normalized": 2 + }, + "evaluation_samples": 4000, + "evaluation_matches": { + "sum_of_squares_strict_float32": 3049, + "sum_of_squares_double_intermediate": 4000, + "sum_of_squares_fused": 3503, + "sum_of_squares_candidates_differ": 951, + "lerp_strict_float32": 2620, + "lerp_double_intermediate": 4000, + "lerp_candidates_differ": 1380 + } +} diff --git a/docs/port/logs/u1_5/float-probe-unity-release.json b/docs/port/logs/u1_5/float-probe-unity-release.json new file mode 100644 index 0000000..f102ae4 --- /dev/null +++ b/docs/port/logs/u1_5/float-probe-unity-release.json @@ -0,0 +1,42 @@ +{ + "runtime": "Mono 6.13.0 (explicit/dc7ab1aa)", + "label": "Unity 6000.3.20f1 editor, Mono 6.13.0 (explicit/dc7ab1aa), code optimization Release", + "rows": 4000, + "identical_to_godot": { + "length": 4000, + "length_squared": 4000, + "distance_to": 4000, + "distance_squared_to": 4000, + "dot": 4000, + "normalized": 4000, + "lerp": 4000, + "limit_length": 4000, + "multiply": 4000, + "divide": 4000, + "sub_normalized": 4000, + "atan2": 4000 + }, + "worst_ulps": { + "length": 0, + "length_squared": 0, + "distance_to": 0, + "distance_squared_to": 0, + "dot": 0, + "normalized": 0, + "lerp": 0, + "limit_length": 0, + "multiply": 0, + "divide": 0, + "sub_normalized": 0 + }, + "evaluation_samples": 4000, + "evaluation_matches": { + "sum_of_squares_strict_float32": 3049, + "sum_of_squares_double_intermediate": 4000, + "sum_of_squares_fused": 3503, + "sum_of_squares_candidates_differ": 951, + "lerp_strict_float32": 2620, + "lerp_double_intermediate": 4000, + "lerp_candidates_differ": 1380 + } +} diff --git a/docs/port/logs/u1_5/sim-bench-dotnet8.json b/docs/port/logs/u1_5/sim-bench-dotnet8.json new file mode 100644 index 0000000..4487630 --- /dev/null +++ b/docs/port/logs/u1_5/sim-bench-dotnet8.json @@ -0,0 +1,11 @@ +{ + "runtime": ".NET 8.0.27", + "label": ".NET 8.0.27, Release build", + "cpu": "Arm64", + "mobile_units_at_start": 120, + "sim_step_ms_median": 0.2627, + "step_blocks_ms": [0.2405, 0.2436, 0.2627, 0.2650, 0.2718], + "bot_matches_seeds_1_2_3_ms": 1511.7, + "bot_match_rounds": 32, + "ms_per_round": 47.24 +} diff --git a/docs/port/logs/u1_5/sim-bench-godot.json b/docs/port/logs/u1_5/sim-bench-godot.json new file mode 100644 index 0000000..b046b95 --- /dev/null +++ b/docs/port/logs/u1_5/sim-bench-godot.json @@ -0,0 +1,15 @@ +{ + "bot_match_rounds": 32, + "bot_matches_seeds_1_2_3_ms": 21005.8, + "engine": "4.7.2-stable (official)", + "mobile_units_at_start": 120, + "ms_per_round": 656.43125, + "sim_step_ms_median": 1.6, + "step_blocks_ms": [ + 1.598385, + 1.5995, + 1.6, + 1.605535, + 1.614205 + ] +} \ No newline at end of file diff --git a/docs/port/logs/u1_5/sim-bench-unity-editor-debug.json b/docs/port/logs/u1_5/sim-bench-unity-editor-debug.json new file mode 100644 index 0000000..7e661b4 --- /dev/null +++ b/docs/port/logs/u1_5/sim-bench-unity-editor-debug.json @@ -0,0 +1,11 @@ +{ + "runtime": "Mono 6.13.0 (explicit/dc7ab1aa)", + "label": "Unity 6000.3.20f1 editor, Mono 6.13.0 (explicit/dc7ab1aa), code optimization Debug", + "cpu": "Arm64", + "mobile_units_at_start": 120, + "sim_step_ms_median": 1.0038, + "step_blocks_ms": [0.7783, 0.9861, 1.0038, 1.0177, 1.2192], + "bot_matches_seeds_1_2_3_ms": 10959.3, + "bot_match_rounds": 32, + "ms_per_round": 342.48 +} diff --git a/docs/port/logs/u1_5/sim-bench-unity-editor-release.json b/docs/port/logs/u1_5/sim-bench-unity-editor-release.json new file mode 100644 index 0000000..c30963f --- /dev/null +++ b/docs/port/logs/u1_5/sim-bench-unity-editor-release.json @@ -0,0 +1,11 @@ +{ + "runtime": "Mono 6.13.0 (explicit/dc7ab1aa)", + "label": "Unity 6000.3.20f1 editor, Mono 6.13.0 (explicit/dc7ab1aa), code optimization Release", + "cpu": "Arm64", + "mobile_units_at_start": 120, + "sim_step_ms_median": 0.5137, + "step_blocks_ms": [0.3574, 0.4181, 0.5137, 0.5505, 1.5007], + "bot_matches_seeds_1_2_3_ms": 6755.1, + "bot_match_rounds": 32, + "ms_per_round": 211.10 +} diff --git a/docs/port/logs/u1_5/unity-environment.json b/docs/port/logs/u1_5/unity-environment.json new file mode 100644 index 0000000..dbbfcb0 --- /dev/null +++ b/docs/port/logs/u1_5/unity-environment.json @@ -0,0 +1,15 @@ +{ + "unity_version": "6000.3.20f1 (c9ba695d4f07)", + "license_has_pro": "False", + "urp_version": "17.3.0", + "editor_runtime": "Mono 6.13.0 (explicit/dc7ab1aa)", + "editor_code_optimization": "Release", + "standalone_scripting_backend": "Mono2x", + "standalone_api_compatibility": "NET_Standard_2_0", + "color_space": "Gamma", + "render_pipeline_asset": "none", + "build_support_macos": "True", + "build_support_windows64": "False", + "build_support_linux64": "False", + "core_assembly_references": "netstandard 2.1.0.0" +}