champions-of-luneraria/Assets/Luneraria/Core/Model/GdMath.cs
Martin Stickelbroeck 4d6ed17dca U1: Regelkern in C#, verhaltensgleich zur Godot-Referenz
Vollständiger, engineunabhängiger Regelkern (Luneraria.Core, ohne
UnityEngine): Partie mit sechs Plätzen und zwei Teams, private Planung mit
atomarer Übernahme, Gold, Käufe, Aufwertungen, Formation, drei Lanes mit
zwei Laneturmen und zwei Basistürmen je Team, Ancients, unbegrenzte Wellen,
Helden mit Stufen, Items und Talenten, Angriffschampions, Bossgrotte mit
Anspruch, Entsendung, Kopfgeld und dreirundiger Sperre, Bots und
Siegbedingung (nur Ancient-Fall). Serverautoritative Befehlsschnittstelle.

- Deterministisch: fester Takt, geordnete Maps, fortlaufende IDs, kein
  Zufall, keine Uhr, keine Threads
- Tabellen einmal aus Godot erzeugt (tools/port/gen_content.py)
- Golden-Referenzen aus Godot (tools/godot-golden): 19 Szenarien,
  120 Snapshots, 51.221 Werte, Abweichung 0 (Toleranzen dokumentiert)
- Tests: content 75, core 91, m2 73, golden 157, determinism 6 = 402/402
- Leistung (M5): Simulationsschritt 0,259 ms statt 1,587 ms in GDScript
- Referenzfehler R1 im Bot-Gedächtnis bewusst übernommen und dokumentiert
- Geplante Helden (Lucy, Ghaliga, Siegfried, Feena) im Datenmodell, inaktiv
- Doku unter docs/port (U1-RULES, PORT-STATUS, PORT-QA, U2 bis U5)

Unity-Editor-Lauf offen: Die Unity-Lizenz ist noch nicht aktiviert; gebaut
und getestet mit dem .NET 8 SDK auf Unitys Sprachstand (C# 9, .NET
Standard 2.1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 14:31:18 +02:00

83 lines
2.7 KiB
C#

using System;
using System.Collections.Generic;
namespace Luneraria.Core
{
/// <summary>
/// Scalar helpers with the exact semantics of the GDScript built-ins the rules used
/// (clampf, maxf, minf, roundf, floori, ceili, is_equal_approx). Doubles throughout.
/// </summary>
public static class GdMath
{
public const double CmpEpsilon = 0.00001;
public static double Clamp(double value, double low, double high)
{
return value < low ? low : (value > high ? high : value);
}
public static int Clamp(int value, int low, int high)
{
return value < low ? low : (value > high ? high : value);
}
/// <summary>Godot's MAX macro: (a &gt; b) ? a : b.</summary>
public static double Max(double a, double b)
{
return a > b ? a : b;
}
/// <summary>Godot's MIN macro: (a &lt; b) ? a : b.</summary>
public static double Min(double a, double b)
{
return a < b ? a : b;
}
public static int Max(int a, int b) { return a > b ? a : b; }
public static int Min(int a, int b) { return a < b ? a : b; }
public static int FloorI(double value) { return (int)Math.Floor(value); }
public static int CeilI(double value) { return (int)Math.Ceiling(value); }
/// <summary>roundf: half away from zero (C round), not banker's rounding.</summary>
public static double Round(double value)
{
return Math.Round(value, MidpointRounding.AwayFromZero);
}
public static bool IsEqualApprox(double a, double b)
{
if (a == b)
return true;
double tolerance = CmpEpsilon * Math.Abs(a);
if (tolerance < CmpEpsilon)
tolerance = CmpEpsilon;
return Math.Abs(a - b) < tolerance;
}
/// <summary>GDScript atan2(y, x) on doubles.</summary>
public static double Atan2(double y, double x) { return Math.Atan2(y, x); }
/// <summary>
/// Stable insertion sort with a "less than" predicate. Every comparator of the rules is
/// a strict order on distinct unit ids or sequence numbers, so the result is unique;
/// stability keeps it deterministic even for near ties.
/// </summary>
public static void SortStable<T>(List<T> list, Func<T, T, bool> less)
{
for (int i = 1; i < list.Count; i++)
{
T item = list[i];
int j = i - 1;
while (j >= 0 && less(item, list[j]))
{
list[j + 1] = list[j];
j--;
}
list[j + 1] = item;
}
}
}
}