champions-of-luneraria/Assets/Luneraria/Core/Model/OrderedMap.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

97 lines
3.1 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
namespace Luneraria.Core
{
/// <summary>
/// Map with a defined iteration order: insertion order; removing a key keeps the order of
/// the others and a key added again goes to the end. The rules iterate buildings, effects
/// and damage-over-time entries; with this map that order never depends on hashing or on
/// the history of a Dictionary (docs/port/U1-RULES.md, determinism).
/// </summary>
public sealed class OrderedMap<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
{
readonly Dictionary<TKey, int> _index = new Dictionary<TKey, int>();
readonly List<TKey> _keys = new List<TKey>();
readonly List<TValue> _values = new List<TValue>();
public int Count { get { return _keys.Count; } }
public IReadOnlyList<TKey> Keys { get { return _keys; } }
public IReadOnlyList<TValue> Values { get { return _values; } }
public bool ContainsKey(TKey key) { return _index.ContainsKey(key); }
public TValue this[TKey key]
{
get { return _values[_index[key]]; }
set
{
int at;
if (_index.TryGetValue(key, out at))
{
_values[at] = value;
return;
}
_index[key] = _keys.Count;
_keys.Add(key);
_values.Add(value);
}
}
public bool TryGetValue(TKey key, out TValue value)
{
int at;
if (_index.TryGetValue(key, out at))
{
value = _values[at];
return true;
}
value = default(TValue);
return false;
}
public TValue GetValueOrDefault(TKey key, TValue fallback)
{
int at;
return _index.TryGetValue(key, out at) ? _values[at] : fallback;
}
public bool Remove(TKey key)
{
int at;
if (!_index.TryGetValue(key, out at))
return false;
_keys.RemoveAt(at);
_values.RemoveAt(at);
_index.Remove(key);
for (int i = at; i < _keys.Count; i++)
_index[_keys[i]] = i;
return true;
}
public void Clear()
{
_index.Clear();
_keys.Clear();
_values.Clear();
}
/// <summary>Copy in the same order; values are copied with the given function.</summary>
public OrderedMap<TKey, TValue> Clone(Func<TValue, TValue> copy = null)
{
var result = new OrderedMap<TKey, TValue>();
for (int i = 0; i < _keys.Count; i++)
result[_keys[i]] = copy != null ? copy(_values[i]) : _values[i];
return result;
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
for (int i = 0; i < _keys.Count; i++)
yield return new KeyValuePair<TKey, TValue>(_keys[i], _values[i]);
}
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
}