309 lines
12 KiB
C#
309 lines
12 KiB
C#
using MultiversalDiplomacy.Adjudicate;
|
|
using MultiversalDiplomacy.Adjudicate.Decision;
|
|
using MultiversalDiplomacy.Model;
|
|
|
|
using NUnit.Framework;
|
|
|
|
namespace MultiversalDiplomacyTests;
|
|
|
|
public class TimeTravelTest
|
|
{
|
|
[Test]
|
|
public void MDATC_3_A_1_MoveIntoOwnPastForksTimeline()
|
|
{
|
|
TestCaseBuilder setup = new(World.WithStandardMap(), MovementPhaseAdjudicator.Instance);
|
|
|
|
// Hold to move into the future, then move back into the past.
|
|
setup[("a", 0)]
|
|
.GetReference(out Season s0)
|
|
["Germany"]
|
|
.Army("Mun").Holds().GetReference(out var mun0)
|
|
.Execute()
|
|
[("a", 1)]
|
|
.GetReference(out Season s1)
|
|
["Germany"]
|
|
.Army("Mun").MovesTo("Tyr", season: s0).GetReference(out var mun1);
|
|
|
|
setup.ValidateOrders();
|
|
Assert.That(mun1, Is.Valid);
|
|
setup.AdjudicateOrders();
|
|
Assert.That(mun1, Is.Victorious);
|
|
|
|
World world = setup.UpdateWorld();
|
|
|
|
// Confirm that there are now four seasons: three in the main timeline and one in a fork.
|
|
Assert.That(
|
|
world.Seasons.Values.Where(s => s.Timeline == s0.Timeline).Count(),
|
|
Is.EqualTo(3),
|
|
"Failed to advance main timeline after last unit left");
|
|
Assert.That(
|
|
world.Seasons.Values.Where(s => s.Timeline != s0.Timeline).Count(),
|
|
Is.EqualTo(1),
|
|
"Failed to fork timeline when unit moved in");
|
|
|
|
// Confirm that there is a unit in Tyr b1 originating from Mun a1
|
|
Season fork = world.Seasons["b1"];
|
|
Unit originalUnit = world.GetUnitAt("Mun", s0);
|
|
Unit aMun0 = world.GetUnitAt("Mun", s1);
|
|
Unit aTyr = world.GetUnitAt("Tyr", fork);
|
|
Assert.That(aTyr.Past, Is.EqualTo(mun1.Order.Unit.Designation));
|
|
Assert.That(world.GetUnitByDesignation(aTyr.Past!).Past, Is.EqualTo(mun0.Order.Unit.Designation));
|
|
|
|
// Confirm that there is a unit in Mun b1 originating from Mun a0
|
|
Unit aMun1 = world.GetUnitAt("Mun", fork);
|
|
Assert.That(aMun1.Past, Is.EqualTo(originalUnit.Designation));
|
|
}
|
|
|
|
[Test]
|
|
public void MDATC_3_A_2_SupportToRepelledPastMoveForksTimeline()
|
|
{
|
|
TestCaseBuilder setup = new(World.WithStandardMap(), MovementPhaseAdjudicator.Instance);
|
|
|
|
// Fail to dislodge on the first turn, then support the move so it succeeds.
|
|
setup[("a", 0)]
|
|
.GetReference(out Season s0)
|
|
["Germany"]
|
|
.Army("Mun").MovesTo("Tyr").GetReference(out var mun0)
|
|
["Austria"]
|
|
.Army("Tyr").Holds().GetReference(out var tyr0);
|
|
|
|
setup.ValidateOrders();
|
|
Assert.That(mun0, Is.Valid);
|
|
Assert.That(tyr0, Is.Valid);
|
|
setup.AdjudicateOrders();
|
|
Assert.That(mun0, Is.Repelled);
|
|
Assert.That(tyr0, Is.NotDislodged);
|
|
setup.UpdateWorld();
|
|
|
|
setup[("a", 1)]
|
|
["Germany"]
|
|
.Army("Mun").Supports.Army("Mun", season: s0).MoveTo("Tyr").GetReference(out var mun1)
|
|
["Austria"]
|
|
.Army("Tyr").Holds();
|
|
|
|
// Confirm that history is changed.
|
|
setup.ValidateOrders();
|
|
Assert.That(mun1, Is.Valid);
|
|
setup.AdjudicateOrders();
|
|
Assert.That(mun1, Is.NotCut);
|
|
Assert.That(mun0, Is.Victorious);
|
|
Assert.That(tyr0, Is.Dislodged);
|
|
|
|
// Confirm that an alternate future is created.
|
|
World world = setup.UpdateWorld();
|
|
Season fork = world.Seasons["b1"];
|
|
Unit tyr1 = world.GetUnitAt("Tyr", fork);
|
|
Assert.That(
|
|
tyr1.Past,
|
|
Is.EqualTo(mun0.Order.Unit.Designation),
|
|
"Expected A Mun a0 to advance to Tyr b1");
|
|
Assert.That(
|
|
world.RetreatingUnits.Count,
|
|
Is.EqualTo(1),
|
|
"Expected A Tyr a0 to be in retreat");
|
|
Assert.That(world.RetreatingUnits.First().Unit, Is.EqualTo(tyr0.Order.Unit));
|
|
}
|
|
|
|
[Test]
|
|
public void MDATC_3_A_3_FailedMoveDoesNotForkTimeline()
|
|
{
|
|
TestCaseBuilder setup = new(World.WithStandardMap(), MovementPhaseAdjudicator.Instance);
|
|
|
|
// Hold to create a future, then attempt to attack in the past.
|
|
setup[("a", 0)]
|
|
.GetReference(out Season s0)
|
|
["Germany"]
|
|
.Army("Mun").Holds()
|
|
["Austria"]
|
|
.Army("Tyr").Holds().GetReference(out var tyr0)
|
|
.Execute()
|
|
[("a", 1)]
|
|
.GetReference(out Season s1)
|
|
["Germany"]
|
|
.Army("Mun").MovesTo("Tyr", season: s0).GetReference(out var mun1)
|
|
["Austria"]
|
|
.Army("Tyr").Holds();
|
|
|
|
setup.ValidateOrders();
|
|
Assert.That(mun1, Is.Valid);
|
|
setup.AdjudicateOrders();
|
|
Assert.That(mun1, Is.Repelled);
|
|
// The order to Tyr a0 should have been pulled into the adjudication set, so the reference
|
|
// should not throw when accessing it.
|
|
Assert.That(tyr0, Is.NotDislodged);
|
|
|
|
// There should only be three seasons, all in the main timeline, since the move failed to
|
|
// change the past and therefore did not create a new timeline.
|
|
World world = setup.UpdateWorld();
|
|
Assert.That(
|
|
world.GetFutures(s0).Count(),
|
|
Is.EqualTo(1),
|
|
"A failed move incorrectly forked the timeline");
|
|
Assert.That(world.GetFutures(s1).Count(), Is.EqualTo(1));
|
|
Season s2 = world.GetSeason(s1.Timeline, s1.Turn + 1);
|
|
Assert.That(world.GetFutures(s2).Count(), Is.EqualTo(0));
|
|
}
|
|
|
|
[Test]
|
|
public void MDATC_3_A_4_SuperfluousSupportDoesNotForkTimeline()
|
|
{
|
|
TestCaseBuilder setup = new(World.WithStandardMap(), MovementPhaseAdjudicator.Instance);
|
|
|
|
// Move, then support the past move even though it succeeded already.
|
|
setup[("a", 0)]
|
|
.GetReference(out Season s0)
|
|
["Germany"]
|
|
.Army("Mun").MovesTo("Tyr").GetReference(out var mun0)
|
|
.Army("Boh").Holds();
|
|
|
|
setup.ValidateOrders();
|
|
Assert.That(mun0, Is.Valid);
|
|
setup.AdjudicateOrders();
|
|
Assert.That(mun0, Is.Victorious);
|
|
setup.UpdateWorld();
|
|
|
|
setup[("a", 1)]
|
|
.GetReference(out Season s1)
|
|
["Germany"]
|
|
.Army("Tyr").Holds()
|
|
.Army("Boh").Supports.Army("Mun", season: s0).MoveTo("Tyr").GetReference(out var boh1);
|
|
|
|
// The support does work and the move does succeed, but...
|
|
setup.ValidateOrders();
|
|
Assert.That(boh1, Is.Valid);
|
|
setup.AdjudicateOrders();
|
|
Assert.That(boh1, Is.NotCut);
|
|
Assert.That(mun0, Is.Victorious);
|
|
|
|
// ...since it succeeded anyway, no fork is created.
|
|
World world = setup.UpdateWorld();
|
|
Assert.That(
|
|
world.GetFutures(s0).Count(),
|
|
Is.EqualTo(1),
|
|
"A superfluous support incorrectly forked the timeline");
|
|
Assert.That(world.GetFutures(s1).Count(), Is.EqualTo(1));
|
|
Season s2 = world.GetSeason(s1.Timeline, s1.Turn + 1);
|
|
Assert.That(world.GetFutures(s2).Count(), Is.EqualTo(0));
|
|
}
|
|
|
|
[Test]
|
|
public void MDATC_3_A_5_CrossTimelineSupportDoesNotForkHead()
|
|
{
|
|
TestCaseBuilder setup = new(World.WithStandardMap(), MovementPhaseAdjudicator.Instance);
|
|
|
|
// London creates two timelines by moving into the past.
|
|
setup[("a", 0)]
|
|
.GetReference(out var a0)
|
|
["England"].Army("Lon").Holds()
|
|
["Austria"].Army("Tyr").Holds()
|
|
["Germany"].Army("Mun").Holds()
|
|
.Execute()
|
|
[("a", 1)]
|
|
["England"].Army("Lon").MovesTo("Yor", a0)
|
|
.Execute()
|
|
// Head seasons: a2 b1
|
|
// Both contain copies of the armies in Mun and Tyr.
|
|
// Now Germany dislodges Austria in Tyr by supporting the move across timelines.
|
|
[("a", 2)]
|
|
.GetReference(out var a2)
|
|
["Germany"].Army("Mun").MovesTo("Tyr").GetReference(out var mun_a2)
|
|
["Austria"].Army("Tyr").Holds().GetReference(out var tyr_a2)
|
|
[("b", 1)]
|
|
.GetReference(out var b1)
|
|
["Germany"].Army("Mun").Supports.Army("Mun", a2).MoveTo("Tyr").GetReference(out var mun_b1)
|
|
["Austria"].Army("Tyr").Holds();
|
|
|
|
// The attack against Tyr a2 succeeds.
|
|
setup.ValidateOrders();
|
|
Assert.That(mun_a2, Is.Valid);
|
|
Assert.That(tyr_a2, Is.Valid);
|
|
Assert.That(mun_b1, Is.Valid);
|
|
setup.AdjudicateOrders();
|
|
Assert.That(mun_a2, Is.Victorious);
|
|
Assert.That(tyr_a2, Is.Dislodged);
|
|
Assert.That(mun_b1, Is.NotCut);
|
|
|
|
// Since both seasons were at the head of their timelines, there should be no forking.
|
|
World world = setup.UpdateWorld();
|
|
Assert.That(
|
|
world.GetFutures(a2).Count(),
|
|
Is.EqualTo(1),
|
|
"A cross-timeline support incorrectly forked the head of the timeline");
|
|
Assert.That(
|
|
world.GetFutures(b1).Count(),
|
|
Is.EqualTo(1),
|
|
"A cross-timeline support incorrectly forked the head of the timeline");
|
|
Season a3 = world.GetSeason(a2.Timeline, a2.Turn + 1);
|
|
Assert.That(world.GetFutures(a3).Count(), Is.EqualTo(0));
|
|
Season b2 = world.GetSeason(b1.Timeline, b1.Turn + 1);
|
|
Assert.That(world.GetFutures(b2).Count(), Is.EqualTo(0));
|
|
}
|
|
|
|
[Test]
|
|
public void MDATC_3_A_6_CuttingCrossTimelineSupportDoesNotFork()
|
|
{
|
|
TestCaseBuilder setup = new(World.WithStandardMap(), MovementPhaseAdjudicator.Instance);
|
|
|
|
// As above, only now London creates three timelines.
|
|
setup[("a", 0)]
|
|
.GetReference(out var a0)
|
|
["England"].Army("Lon").Holds()
|
|
["Austria"].Army("Boh").Holds()
|
|
["Germany"].Army("Mun").Holds()
|
|
.Execute()
|
|
[("a", 1)]
|
|
["England"].Army("Lon").MovesTo("Yor", a0)
|
|
.Execute()
|
|
// Head seasons: a2, b1
|
|
[("a", 2)]
|
|
[("b", 1)]
|
|
["England"].Army("Yor").MovesTo("Edi", a0)
|
|
.Execute()
|
|
// Head seasons: a3, b2, c1
|
|
// All three of these contain copies of the armies in Mun and Tyr.
|
|
// As above, Germany dislodges Austria in Tyr a3 by supporting the move from b2.
|
|
[("a", 3)]
|
|
.GetReference(out var a3)
|
|
["Germany"].Army("Mun").MovesTo("Tyr")
|
|
["Austria"].Army("Tyr").Holds()
|
|
[("b", 2)]
|
|
.GetReference(out Season b2)
|
|
["Germany"].Army("Mun").Supports.Army("Mun", a3).MoveTo("Tyr").GetReference(out var mun_b2)
|
|
["Austria"].Army("Tyr").Holds()
|
|
[("c", 1)]
|
|
["Germany"].Army("Mun").Holds()
|
|
["Austria"].Army("Tyr").Holds()
|
|
.Execute()
|
|
// Head seasons: a4, b3, c2
|
|
// Now Austria cuts the support in b2 by attacking from c2.
|
|
[("a", 4)]
|
|
["Germany"].Army("Tyr").Holds()
|
|
[("b", 3)]
|
|
["Germany"].Army("Mun").Holds()
|
|
["Austria"].Army("Tyr").Holds()
|
|
[("c", 2)]
|
|
["Germany"].Army("Mun").Holds()
|
|
["Austria"].Army("Tyr").MovesTo("Mun", b2).GetReference(out var tyr_c2);
|
|
|
|
// The attack on Mun b2 is repelled, but the support is cut.
|
|
setup.ValidateOrders();
|
|
Assert.That(tyr_c2, Is.Valid);
|
|
setup.AdjudicateOrders();
|
|
Assert.That(tyr_c2, Is.Repelled);
|
|
Assert.That(mun_b2, Is.NotDislodged);
|
|
Assert.That(mun_b2, Is.Cut);
|
|
|
|
// Though the support was cut, the timeline doesn't fork because the outcome of a battle
|
|
// wasn't changed in this timeline.
|
|
World world = setup.UpdateWorld();
|
|
Assert.That(
|
|
world.GetFutures(a3).Count(),
|
|
Is.EqualTo(1),
|
|
"A cross-timeline support cut incorrectly forked the timeline");
|
|
Assert.That(
|
|
world.GetFutures(b2).Count(),
|
|
Is.EqualTo(1),
|
|
"A cross-timeline support cut incorrectly forked the timeline");
|
|
}
|
|
}
|