diff --git a/.gitignore b/.gitignore
index d96badf..e75b34a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,9 +8,10 @@
/[Uu]ser[Ss]ettings/
/[Mm]emoryCaptures/
/[Rr]ecordings/
-*.csproj
-*.sln
-*.slnx
+# IDE projects that Unity generates in the project root (tools/dotnet/*/*.csproj are sources)
+/*.csproj
+/*.sln
+/*.slnx
*.suo
*.user
*.pidb
diff --git a/Assets/DefaultVolumeProfile.asset b/Assets/DefaultVolumeProfile.asset
new file mode 100644
index 0000000..5393bfe
--- /dev/null
+++ b/Assets/DefaultVolumeProfile.asset
@@ -0,0 +1,15 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: d7fd9488000d3734a9e00ee676215985, type: 3}
+ m_Name: DefaultVolumeProfile
+ m_EditorClassIdentifier: Unity.RenderPipelines.Core.Runtime::UnityEngine.Rendering.VolumeProfile
+ components: []
diff --git a/Assets/DefaultVolumeProfile.asset.meta b/Assets/DefaultVolumeProfile.asset.meta
new file mode 100644
index 0000000..94f355a
--- /dev/null
+++ b/Assets/DefaultVolumeProfile.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 6a697dcb8a0174239a6b4f5cf34e06f3
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Luneraria.meta b/Assets/Luneraria.meta
new file mode 100644
index 0000000..bcf9eed
--- /dev/null
+++ b/Assets/Luneraria.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 0e6d8499dbaf24b81b6ad837fdd97658
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Luneraria/Core.meta b/Assets/Luneraria/Core.meta
new file mode 100644
index 0000000..b3aac0d
--- /dev/null
+++ b/Assets/Luneraria/Core.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 78305943818f84edabafec322efe1fe6
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Luneraria/Core/Content.meta b/Assets/Luneraria/Core/Content.meta
new file mode 100644
index 0000000..6b7d37f
--- /dev/null
+++ b/Assets/Luneraria/Core/Content.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: ead6ce2b7436340ca9e68ba84201c3e2
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Luneraria/Core/Content/ContentData.cs.meta b/Assets/Luneraria/Core/Content/ContentData.cs.meta
new file mode 100644
index 0000000..6e7ad0f
--- /dev/null
+++ b/Assets/Luneraria/Core/Content/ContentData.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 328555984a481452089b9d3edb228624
\ No newline at end of file
diff --git a/Assets/Luneraria/Core/Content/DataCatalog.cs.meta b/Assets/Luneraria/Core/Content/DataCatalog.cs.meta
new file mode 100644
index 0000000..15f5250
--- /dev/null
+++ b/Assets/Luneraria/Core/Content/DataCatalog.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 908d82286fb264c028c17d79a57496a0
\ No newline at end of file
diff --git a/Assets/Luneraria/Core/Content/Definitions.cs.meta b/Assets/Luneraria/Core/Content/Definitions.cs.meta
new file mode 100644
index 0000000..a5e26b6
--- /dev/null
+++ b/Assets/Luneraria/Core/Content/Definitions.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: adf467f403c0447f493cde3b3d06c592
\ No newline at end of file
diff --git a/Assets/Luneraria/Core/Content/LuneBalance.cs.meta b/Assets/Luneraria/Core/Content/LuneBalance.cs.meta
new file mode 100644
index 0000000..1b7ce16
--- /dev/null
+++ b/Assets/Luneraria/Core/Content/LuneBalance.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 4eabf29eecb184c5fac328a428a9f5f9
\ No newline at end of file
diff --git a/Assets/Luneraria/Core/Content/LuneContent.cs.meta b/Assets/Luneraria/Core/Content/LuneContent.cs.meta
new file mode 100644
index 0000000..eb669c2
--- /dev/null
+++ b/Assets/Luneraria/Core/Content/LuneContent.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: f4ce13de7838f481a85413ac397ab77a
\ No newline at end of file
diff --git a/Assets/Luneraria/Core/Content/PlannedHeroes.cs.meta b/Assets/Luneraria/Core/Content/PlannedHeroes.cs.meta
new file mode 100644
index 0000000..5ac76e2
--- /dev/null
+++ b/Assets/Luneraria/Core/Content/PlannedHeroes.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 494a8b8f3589145b6b4865a06f2d820f
\ No newline at end of file
diff --git a/Assets/Luneraria/Core/Luneraria.Core.asmdef.meta b/Assets/Luneraria/Core/Luneraria.Core.asmdef.meta
new file mode 100644
index 0000000..408382e
--- /dev/null
+++ b/Assets/Luneraria/Core/Luneraria.Core.asmdef.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: a1963e13e4b2d498880517ad9c8cb0d4
+AssemblyDefinitionImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Luneraria/Core/Model.meta b/Assets/Luneraria/Core/Model.meta
new file mode 100644
index 0000000..582deb5
--- /dev/null
+++ b/Assets/Luneraria/Core/Model.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 54a47352f21234f5289742a42955b423
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Luneraria/Core/Model/GdMath.cs.meta b/Assets/Luneraria/Core/Model/GdMath.cs.meta
new file mode 100644
index 0000000..02e8ae4
--- /dev/null
+++ b/Assets/Luneraria/Core/Model/GdMath.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: e0fb56a7322c5472482706dd9223cf59
\ No newline at end of file
diff --git a/Assets/Luneraria/Core/Model/OrderedMap.cs.meta b/Assets/Luneraria/Core/Model/OrderedMap.cs.meta
new file mode 100644
index 0000000..8890be8
--- /dev/null
+++ b/Assets/Luneraria/Core/Model/OrderedMap.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 01f9ba65d813741128d484a918918119
\ No newline at end of file
diff --git a/Assets/Luneraria/Core/Model/Vec2.cs.meta b/Assets/Luneraria/Core/Model/Vec2.cs.meta
new file mode 100644
index 0000000..14a66aa
--- /dev/null
+++ b/Assets/Luneraria/Core/Model/Vec2.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 0fb490510bfcb424aa04edaa7727c169
\ No newline at end of file
diff --git a/Assets/Luneraria/Core/Simulation.meta b/Assets/Luneraria/Core/Simulation.meta
new file mode 100644
index 0000000..b8f10d4
--- /dev/null
+++ b/Assets/Luneraria/Core/Simulation.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d3272a7bb47bc4100993e32779e701a6
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Luneraria/Core/Simulation/LuneBot.cs.meta b/Assets/Luneraria/Core/Simulation/LuneBot.cs.meta
new file mode 100644
index 0000000..7686d9d
--- /dev/null
+++ b/Assets/Luneraria/Core/Simulation/LuneBot.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: f7089c45fb5d44cc8a9275b41dbc81fa
\ No newline at end of file
diff --git a/Assets/Luneraria/Core/Simulation/LuneCombat.cs.meta b/Assets/Luneraria/Core/Simulation/LuneCombat.cs.meta
new file mode 100644
index 0000000..ccffb6a
--- /dev/null
+++ b/Assets/Luneraria/Core/Simulation/LuneCombat.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: e976218cfa7d145d499cea25fb7743c8
\ No newline at end of file
diff --git a/Assets/Luneraria/Core/Simulation/LuneMap.cs.meta b/Assets/Luneraria/Core/Simulation/LuneMap.cs.meta
new file mode 100644
index 0000000..fe81343
--- /dev/null
+++ b/Assets/Luneraria/Core/Simulation/LuneMap.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 72579d5206af44de0952c6795daa0f30
\ No newline at end of file
diff --git a/Assets/Luneraria/Core/Simulation/LuneMatch.cs.meta b/Assets/Luneraria/Core/Simulation/LuneMatch.cs.meta
new file mode 100644
index 0000000..b0deafc
--- /dev/null
+++ b/Assets/Luneraria/Core/Simulation/LuneMatch.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 0df2fde8515b847ac9061c49a3833673
\ No newline at end of file
diff --git a/Assets/Luneraria/Core/Simulation/LuneUnit.cs.meta b/Assets/Luneraria/Core/Simulation/LuneUnit.cs.meta
new file mode 100644
index 0000000..d830ccd
--- /dev/null
+++ b/Assets/Luneraria/Core/Simulation/LuneUnit.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: f6ef4a8c1e43c4168ba865a111175643
\ No newline at end of file
diff --git a/Assets/Luneraria/Core/Simulation/MatchModel.cs.meta b/Assets/Luneraria/Core/Simulation/MatchModel.cs.meta
new file mode 100644
index 0000000..3f30798
--- /dev/null
+++ b/Assets/Luneraria/Core/Simulation/MatchModel.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 5e825dabacf0048aaa43963436a358c7
\ No newline at end of file
diff --git a/Assets/Luneraria/Tests.meta b/Assets/Luneraria/Tests.meta
new file mode 100644
index 0000000..6667d3b
--- /dev/null
+++ b/Assets/Luneraria/Tests.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: bdb7d0e2deed24189a8cea4318bb3669
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Luneraria/Tests/Core.meta b/Assets/Luneraria/Tests/Core.meta
new file mode 100644
index 0000000..c3629e6
--- /dev/null
+++ b/Assets/Luneraria/Tests/Core.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: efd02dc831c894b3291786d9e1eb7c31
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Luneraria/Tests/Core/CheckSuite.cs.meta b/Assets/Luneraria/Tests/Core/CheckSuite.cs.meta
new file mode 100644
index 0000000..b81aebc
--- /dev/null
+++ b/Assets/Luneraria/Tests/Core/CheckSuite.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: c49dd358c53a24d02a8fe32174de6483
\ No newline at end of file
diff --git a/Assets/Luneraria/Tests/Core/ContentSuite.cs.meta b/Assets/Luneraria/Tests/Core/ContentSuite.cs.meta
new file mode 100644
index 0000000..1ebf057
--- /dev/null
+++ b/Assets/Luneraria/Tests/Core/ContentSuite.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: bad9dbdfd44444b849155de8e602259a
\ No newline at end of file
diff --git a/Assets/Luneraria/Tests/Core/CoreRulesSuite.cs.meta b/Assets/Luneraria/Tests/Core/CoreRulesSuite.cs.meta
new file mode 100644
index 0000000..63380d1
--- /dev/null
+++ b/Assets/Luneraria/Tests/Core/CoreRulesSuite.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: b02c8152124054bbc96f124c3d140c92
\ No newline at end of file
diff --git a/Assets/Luneraria/Tests/Core/DeterminismSuite.cs.meta b/Assets/Luneraria/Tests/Core/DeterminismSuite.cs.meta
new file mode 100644
index 0000000..8e67883
--- /dev/null
+++ b/Assets/Luneraria/Tests/Core/DeterminismSuite.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 1e085e5e04b944c1eb4ea7a10eede0e1
\ No newline at end of file
diff --git a/Assets/Luneraria/Tests/Core/Diagnostics.meta b/Assets/Luneraria/Tests/Core/Diagnostics.meta
new file mode 100644
index 0000000..2929e2f
--- /dev/null
+++ b/Assets/Luneraria/Tests/Core/Diagnostics.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 8e7cd6f0975494c94895a2fb75c870b2
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Luneraria/Tests/Core/Diagnostics/CoreBenchmark.cs b/Assets/Luneraria/Tests/Core/Diagnostics/CoreBenchmark.cs
new file mode 100644
index 0000000..6ae3936
--- /dev/null
+++ b/Assets/Luneraria/Tests/Core/Diagnostics/CoreBenchmark.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+
+namespace Luneraria.Core.Tests
+{
+ ///
+ /// Simulation cost of the core, identical in every runtime (.NET harness
+ /// tools/dotnet/Luneraria.Core.Bench, Unity editor via CoreBatch.Benchmark): one combat step
+ /// at representative maximum occupation (round 13, six armies at the supply cap, the fixture
+ /// of the Godot probe tools/godot-golden/sim_bench.gd) and complete bot matches of seeds 1-3.
+ ///
+ public static class CoreBenchmark
+ {
+ public static string Run(string runtime, string label)
+ {
+ int units = Fixture(out LuneMatch probe);
+ for (int i = 0; i < 50; i++)
+ probe.StepCombat();
+ var samples = new List();
+ for (int block = 0; block < 5; block++)
+ {
+ Fixture(out LuneMatch fresh);
+ var clock = Stopwatch.StartNew();
+ for (int i = 0; i < 200; i++)
+ fresh.StepCombat();
+ clock.Stop();
+ samples.Add(clock.Elapsed.TotalMilliseconds / 200.0);
+ }
+ samples.Sort();
+ double stepMs = samples[samples.Count / 2];
+ var matchClock = Stopwatch.StartNew();
+ int rounds = 0;
+ foreach (int seed in new[] { 1, 2, 3 })
+ {
+ var match = new LuneMatch();
+ match.Setup(new MatchOptions { Seed = seed, HumanSlot = -1 });
+ while (match.Phase != Phase.Over && match.RoundNumber <= 60)
+ {
+ for (int slot = 0; slot < 6; slot++)
+ LuneBot.Plan(match, slot);
+ match.BeginCombat();
+ match.FinishCombat();
+ rounds++;
+ }
+ }
+ matchClock.Stop();
+ return string.Format(CultureInfo.InvariantCulture,
+ "{{\n \"runtime\": \"{0}\",\n \"label\": \"{1}\",\n \"cpu\": \"{2}\",\n \"mobile_units_at_start\": {3},\n \"sim_step_ms_median\": {4:0.0000},\n \"step_blocks_ms\": [{5}],\n \"bot_matches_seeds_1_2_3_ms\": {6:0.0},\n \"bot_match_rounds\": {7},\n \"ms_per_round\": {8:0.00}\n}}\n",
+ runtime, label, System.Runtime.InteropServices.RuntimeInformation.OSArchitecture,
+ units, stepMs, string.Join(", ", samples.ConvertAll(s => s.ToString("0.0000", CultureInfo.InvariantCulture))),
+ matchClock.Elapsed.TotalMilliseconds, rounds, matchClock.Elapsed.TotalMilliseconds / rounds);
+ }
+
+ static int Fixture(out LuneMatch match)
+ {
+ match = new LuneMatch();
+ match.Setup(new MatchOptions { Seed = 5, HumanSlot = -1 });
+ match.RoundNumber = 13;
+ for (int slot = 0; slot < 6; slot++)
+ {
+ match.Players[slot].Draft.Gold = 100000;
+ LuneBot.Plan(match, slot, "ausgewogen");
+ }
+ match.BeginCombat();
+ return match.Combat.MobileCount();
+ }
+ }
+}
diff --git a/Assets/Luneraria/Tests/Core/Diagnostics/CoreBenchmark.cs.meta b/Assets/Luneraria/Tests/Core/Diagnostics/CoreBenchmark.cs.meta
new file mode 100644
index 0000000..957d2b9
--- /dev/null
+++ b/Assets/Luneraria/Tests/Core/Diagnostics/CoreBenchmark.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 17ab9a74fa7954e03a18622404a4450d
\ No newline at end of file
diff --git a/Assets/Luneraria/Tests/Core/Diagnostics/DivergenceTrace.cs b/Assets/Luneraria/Tests/Core/Diagnostics/DivergenceTrace.cs
new file mode 100644
index 0000000..254e39e
--- /dev/null
+++ b/Assets/Luneraria/Tests/Core/Diagnostics/DivergenceTrace.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Globalization;
+using System.IO;
+using System.Text;
+
+namespace Luneraria.Core.Tests
+{
+ ///
+ /// 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.
+ ///
+ public static class DivergenceTrace
+ {
+ /// Plays rounds 1..round of a bot match; writes the trace of the last round.
+ 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(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));
+ }
+ }
+ }
+}
diff --git a/Assets/Luneraria/Tests/Core/Diagnostics/DivergenceTrace.cs.meta b/Assets/Luneraria/Tests/Core/Diagnostics/DivergenceTrace.cs.meta
new file mode 100644
index 0000000..f46f58d
--- /dev/null
+++ b/Assets/Luneraria/Tests/Core/Diagnostics/DivergenceTrace.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 76014535682464a2487fb6762d09c83e
\ No newline at end of file
diff --git a/Assets/Luneraria/Tests/Core/Diagnostics/FloatProbe.cs b/Assets/Luneraria/Tests/Core/Diagnostics/FloatProbe.cs
new file mode 100644
index 0000000..bb369cd
--- /dev/null
+++ b/Assets/Luneraria/Tests/Core/Diagnostics/FloatProbe.cs
@@ -0,0 +1,213 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Text;
+
+namespace Luneraria.Core.Tests
+{
+ ///
+ /// Float diagnosis for a runtime (docs/port/U1.5-UNITY.md): evaluates the core's own Vec2 and
+ /// GdMath operations on the inputs of tools/godot-golden/float_probe.gd and compares the raw
+ /// bits with Godot's results (Golden/float_probe.json). A second part classifies how the
+ /// runtime evaluates float32 expressions: strictly per operation, with double-precision
+ /// intermediates (allowed by ECMA-335 I.12.1.3) or with fused multiply-add.
+ ///
+ public static class FloatProbe
+ {
+ public sealed class Result
+ {
+ public int Rows;
+ public readonly OrderedMap Identical = new OrderedMap();
+ public readonly OrderedMap WorstUlps = new OrderedMap();
+ public readonly OrderedMap Evaluation = new OrderedMap();
+ public int EvaluationSamples;
+
+ public int Mismatches
+ {
+ get
+ {
+ int total = 0;
+ foreach (KeyValuePair pair in Identical)
+ total += Rows - pair.Value;
+ return total;
+ }
+ }
+
+ public string ToJson(string runtime, string label)
+ {
+ var text = new StringBuilder();
+ text.Append("{\n");
+ text.AppendFormat(" \"runtime\": \"{0}\",\n \"label\": \"{1}\",\n \"rows\": {2},\n", runtime, label, Rows);
+ text.Append(" \"identical_to_godot\": {");
+ bool first = true;
+ foreach (KeyValuePair pair in Identical)
+ {
+ text.AppendFormat(CultureInfo.InvariantCulture, "{0}\n \"{1}\": {2}", first ? "" : ",", pair.Key, pair.Value);
+ first = false;
+ }
+ text.Append("\n },\n \"worst_ulps\": {");
+ first = true;
+ foreach (KeyValuePair pair in WorstUlps)
+ {
+ text.AppendFormat(CultureInfo.InvariantCulture, "{0}\n \"{1}\": {2}", first ? "" : ",", pair.Key, pair.Value);
+ first = false;
+ }
+ text.AppendFormat("\n }},\n \"evaluation_samples\": {0},\n \"evaluation_matches\": {{", EvaluationSamples);
+ first = true;
+ foreach (KeyValuePair pair in Evaluation)
+ {
+ text.AppendFormat(CultureInfo.InvariantCulture, "{0}\n \"{1}\": {2}", first ? "" : ",", pair.Key, pair.Value);
+ first = false;
+ }
+ text.Append("\n }\n}\n");
+ return text.ToString();
+ }
+ }
+
+ public static Result Run(string goldenDirectory)
+ {
+ var result = new Result();
+ var probe = MiniJson.Obj(MiniJson.Parse(File.ReadAllText(Path.Combine(goldenDirectory, "float_probe.json"))));
+ foreach (object item in MiniJson.Arr(probe["rows"]))
+ {
+ List