diff --git a/tests/Aetheria.Shared.Tests/ConsumableAiTests.cs b/tests/Aetheria.Shared.Tests/ConsumableAiTests.cs index de982bf86..6ad889fda 100644 --- a/tests/Aetheria.Shared.Tests/ConsumableAiTests.cs +++ b/tests/Aetheria.Shared.Tests/ConsumableAiTests.cs @@ -339,6 +339,20 @@ public sealed class ConsumableAiTests : IDisposable Assert.True(s.Zone.Agents.Last(a => ReferenceEquals(a.Ship, s.Agent)).Id > first, "a re-admitted pilot is a new id"); } + // Admitting a pilot takes nothing from the item stream: its patrol circuit is its own draw, so how pilots are chosen never + // moves what is minted next. Kills: the circuit drawn from ItemManager.Random. + [Fact] + public void AdmittingAPilotDrawsNothingFromTheItemStream() + { + var s = Build(carryOverdrive: true); + Assert.True(s.Zone.Remove(s.Agent)); + s.Agent.Deactivate(); + var items = s.Agent.ItemManager; + var before = items.Random; + s.Zone.Admit(s.Agent, piloted: true); + Assert.Equal(before.NextFloat(), items.Random.NextFloat()); + } + // The production path for the optimum: a ship the zone admits as piloted derives it from its gun's live stats // before it needs it, and re-derives it when its loadout changes. The gun's effective range is 500 and the // heavy gun's is 800, so the optimum moves with the weapons it has now. diff --git a/tests/Aetheria.Shared.Tests/FireControlPerWeaponTests.cs b/tests/Aetheria.Shared.Tests/FireControlPerWeaponTests.cs index 90e142b3c..bbd57c7ac 100644 --- a/tests/Aetheria.Shared.Tests/FireControlPerWeaponTests.cs +++ b/tests/Aetheria.Shared.Tests/FireControlPerWeaponTests.cs @@ -811,6 +811,13 @@ public sealed class FireControlPerWeaponTests : IDisposable var cells = (Optimum(id, 0) - offset - min) / step; Assert.True(MathF.Abs(cells - MathF.Round(cells)) < .01f, $"id {id}: optimum is {cells} steps from the pilot's own offset"); } + + // The other direction: sampling the optimum takes nothing from the item stream, so how a pilot fights never moves + // what is minted next. Kills: an AI draw from ItemManager.Random. + var items = r.Shooter.ItemManager; + var before = items.Random; + Optimum(5, 0); + Assert.Equal(before.NextFloat(), items.Random.NextFloat()); } // A round refused on a Range that fell between the trigger and the round is still free: the burst is paid diff --git a/tests/Aetheria.Shared.Tests/ScenarioTests.cs b/tests/Aetheria.Shared.Tests/ScenarioTests.cs index fe457bbd6..314132459 100644 --- a/tests/Aetheria.Shared.Tests/ScenarioTests.cs +++ b/tests/Aetheria.Shared.Tests/ScenarioTests.cs @@ -319,6 +319,23 @@ public sealed partial class RunStartTests Assert.Equal(Places(plain), Places(disturbed)); } + // Each pilot's circuit is keyed by its own id: three pilots admitted into one zone do not all patrol the same orbits. + // Kills: the circuit keyed by a constant. + [Fact] + public void EachPilotsPatrolCircuitIsItsOwn() + { + var (_, arena, _, failures) = Launch(new Scripted(false, stage => + { + stage.Player(stage.Bare("Djinni"), float2(0, 0)); + for (var i = 0; i < 3; i++) + stage.Place(stage.Generated("Longinus"), float2(300 + 40 * i, 0), stance: ScenarioStance.Hostile, piloted: true); + })); + Assert.True(failures.Count == 0, string.Join("; ", failures)); + var circuits = arena.Agents.Select(agent => string.Join(",", ((PatrolOrbitsTask) agent.Task).Circuit.Select(key => arena.Orbits.Keys.ToList().IndexOf(key)))).ToArray(); + Assert.Equal(3, circuits.Length); + Assert.True(circuits.Distinct().Count() > 1, "every pilot patrols the same orbits: " + string.Join(" | ", circuits)); + } + // A fit that states its quality mints every lot it builds at it, assembly and role fills alike; the same fit without one // draws them from the item stream, so some lot comes out otherwise. A scenario's checks rest on the first. [Fact]