Update timeline designator usage
Timelines are now identified by strings and come first in timeline-turn tuples.
This commit is contained in:
parent
3d48a7d6f6
commit
9696919773
92
MDATC.html
92
MDATC.html
|
@ -236,17 +236,13 @@
|
|||
<a name="2.B"><h3>2.B. ORDER NOTATION</h3></a>
|
||||
<p>The order notation in this document is as in DATC, with the following additions for multiversal time travel.</p>
|
||||
<ul>
|
||||
<li>A season within a particular timeline is designated in the format X:Y, where X is the turn (starting from 0 and advancing with each movement phase) and Y is the timeline number (starting from 0 and advancing with each timeline fork).</li>
|
||||
<li>Adjudication is implied to be done between successive seasons. For example, if orders are listed for 0:0 and then for 1:0, it is implied that the orders for 0:0 were adjudicated.</li>
|
||||
<li>Units are designated by unit type, province, and season, e.g. "A Munich 1:0". A destination for a move order or support-to-move order is designated by province and season, e.g. "Munich 1:0".
|
||||
<li>Timelines are designated by letters, e.g. "a", "b". Turns are designated by numbers, e.g. 0, 1, 2. A board in a timeline X at turn N is designated XN. A location LOC on that board is designated X-LOC@N.</li>
|
||||
<li>In examples that cover multiple turns, orders are given in sets. Each order set is adjudicated before moving on to the next set.</li>
|
||||
<li>Units are fully designated by unit type and multiversal location, e.g. "A b-Munich@3". Destinations for move orders or support-to-move orders are fully designated by multiversal location, e.g. <code>a-Munich@1</code>. Where orders are not fully designated, the full designations are implied according to these rules:</li>
|
||||
<ul>
|
||||
<li>If season of the ordered unit is not specified, the season is the season to which the orders are being given.</li>
|
||||
<li>If the season of a unit supported to hold is not specified, the season is the same season as the supporting unit.</li>
|
||||
<li>If the season of the destination of a move order or the season of the destination of a supported move order is not specified, the season is the season of the moving unit.</li>
|
||||
<li>For example:
|
||||
<pre>Germany 2:0
|
||||
A Munich supports A Munich 1:1 - Tyrolia</pre>
|
||||
The order here is for Army Munich in 2:0. The move being supported is for Army Munich in 1:1 to move to Tyrolia in 1:1.</li>
|
||||
<li>If only the timeline of a location is specified, the turn is the latest turn in that timeline. E.g. if timeline "a" is at turn 2, <code>a-Munich</code> is interpreted as <code>a-Munich@2</code>.</li>
|
||||
<li>If the timeline or turn are unspecified for the target of a move or support-hold order, the timeline and turn are those of the ordered unit. E.g. if timeline "b" is at turn 1, <code>A b-Tyrolia - Munich</code> is interpreted as <code>b-Tyrolia@1 - b-Munich@1</code>.</li>
|
||||
<li>If only the province is specified for the target of a support-move order, the timeline and turn are those of the supported unit. E.g. if timeline "a" is at turn 2 and "b" at turn 1, <code>A a-Munich supports A b-Tyrolia - Munich</code> is interpreted as <code>A a-Munich@2 supports A b-Tyrolia@1 - b-Munich@1</code>.</li>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
|
@ -258,13 +254,15 @@ The order here is for Army Munich in 2:0. The move being supported is for Army M
|
|||
<summary><h4><a href="#3.A.1">3.A.1</a>. TEST CASE, MOVE INTO OWN PAST FORKS TIMELINE</h4></summary>
|
||||
<p>A unit that moves into its own immediate past causes the timeline to fork.</p>
|
||||
<pre>
|
||||
Germany 0:0
|
||||
A Munich hold
|
||||
Germany:
|
||||
A a-Munich hold
|
||||
|
||||
Germany 1:0
|
||||
A Munich - Tyrolia 0:0
|
||||
---
|
||||
|
||||
Germany:
|
||||
A a-Munich - a-Tyrolia@0
|
||||
</pre>
|
||||
<p>A Munich 1:0 moves to Tyrolia 0:0. The main timeline advances to 2:0 with an empty board. A forked timeline advances to 1:1 with armies in Munich and Tyrolia.</p>
|
||||
<p>A a-Munich@1 moves to a-Tyrolia@0. The main timeline advances to a2 with an empty board. A forked timeline advances to b1 with armies in Munich and Tyrolia.</p>
|
||||
<div class="figures">
|
||||
<canvas id="canvas-3-A-1-before" width="0" height="0"></canvas>
|
||||
<script>
|
||||
|
@ -311,19 +309,21 @@ The order here is for Army Munich in 2:0. The move being supported is for Army M
|
|||
<summary><h4><a href="#3.A.2">3.A.2</a>. TEST CASE, SUPPORT TO REPELLED PAST MOVE FORKS TIMELINE</h4></summary>
|
||||
<p>A unit that supports a move that previously failed in the past, such that it now succeeds, causes the timeline to fork.</p>
|
||||
<pre>
|
||||
Austria 0:0
|
||||
Austria:
|
||||
A Tyrolia hold
|
||||
|
||||
Germany 0:0
|
||||
Germany:
|
||||
A Munich - Tyrolia
|
||||
|
||||
Austria 1:0
|
||||
---
|
||||
|
||||
Austria:
|
||||
A Tyrolia hold
|
||||
|
||||
Germany 1:0
|
||||
A Munich supports A Munich 0:0 - Tyrolia 0:0
|
||||
Germany:
|
||||
A Munich supports A a-Munich@0 - Tyrolia
|
||||
</pre>
|
||||
<p>With the support from A Munich 1:0, A Munich 0:0 dislodges A Tyrolia 0:0. A forked timeline advances to 1:1 where A Tyrolia 0:0 has been dislodged. The main timeline advances to 2:0 where A Munich and A Tyrolia are in their initial positions.</p>
|
||||
<p>With the support from A a-Munich@1, A a-Munich@0 dislodges A a-Tyrolia@0. A forked timeline advances to b1 where A Tyrolia has been dislodged. The main timeline advances to a2 where A Munich and A Tyrolia are in their initial positions.</p>
|
||||
<div class="figures">
|
||||
<canvas id="canvas-3-A-2-before" width="0" height="0"></canvas>
|
||||
<script>
|
||||
|
@ -379,19 +379,21 @@ The order here is for Army Munich in 2:0. The move being supported is for Army M
|
|||
<summary><h4><a href="#3.A.3">3.A.3</a>. TEST CASE, FAILED MOVE DOES NOT FORK TIMELINE</h4></summary>
|
||||
<p>A unit that attempts to move into the past, but is repelled, does not cause the timeline to fork.</p>
|
||||
<pre>
|
||||
Austria 0:0
|
||||
Austria:
|
||||
A Tyrolia hold
|
||||
|
||||
Germany 0:0
|
||||
Germany:
|
||||
A Munich hold
|
||||
|
||||
Austria 1:0
|
||||
---
|
||||
|
||||
Austria:
|
||||
A Tyrolia hold
|
||||
|
||||
Germany 1:0
|
||||
A Munich - Tyrolia 0:0
|
||||
Germany:
|
||||
A Munich - a-Tyrolia@0
|
||||
</pre>
|
||||
<p>The move by A Munich 1:0 fails. The main timeline advances to 2:0 with both armies in their initial positions. No alternate timeline is created.</p>
|
||||
<p>The move by A a-Munich@1 fails. The main timeline advances to a2 with both armies in their initial positions. No alternate timeline is created.</p>
|
||||
<div class="figures">
|
||||
<canvas id="canvas-3-A-3-before" width="0" height="0"></canvas>
|
||||
<script>
|
||||
|
@ -441,15 +443,17 @@ The order here is for Army Munich in 2:0. The move being supported is for Army M
|
|||
<summary><h4><a href="#3.A.4">3.A.4</a>. TEST CASE, SUPERFLUOUS SUPPORT DOES NOT FORK TIMELINE</h4></summary>
|
||||
<p>A unit that supports a move that succeeded in the past and still succeeds after the additional future support does not cause the timeline to fork.</p>
|
||||
<pre>
|
||||
Germany 0:0
|
||||
Germany:
|
||||
A Munich - Tyrolia
|
||||
A Bohemia hold
|
||||
|
||||
Germany 1:0
|
||||
---
|
||||
|
||||
Germany:
|
||||
A Tyrolia hold
|
||||
A Bohemia supports A Munich 0:0 - Tyrolia
|
||||
A Bohemia supports A a-Munich@0 - Tyrolia
|
||||
</pre>
|
||||
<p>Both units in 1:0 continue to 2:0. No alternate timeline is created.</p>
|
||||
<p>Both units in a1 continue to a2. No alternate timeline is created.</p>
|
||||
<div class="figures">
|
||||
<canvas id="canvas-3-A-4-before" width="0" height="0"></canvas>
|
||||
<script>
|
||||
|
@ -501,15 +505,15 @@ The order here is for Army Munich in 2:0. The move being supported is for Army M
|
|||
<summary><h4><a href="#3.A.5">3.A.5</a>. TEST CASE, CROSS-TIMELINE SUPPORT DOES NOT FORK HEAD</h4></summary>
|
||||
<p>In this test case, a unit elsewhere on the map moves into the past to cause a timeline fork. Once there are two parallel timelines, a support from one to the head of the other should not cause any forking, since timeline forks only occur when the past changes, not the present.</p>
|
||||
<pre>
|
||||
Austria
|
||||
A Tyrolia 2:0 hold
|
||||
A Tyrolia 1:1 hold
|
||||
Austria:
|
||||
A a-Tyrolia hold
|
||||
A b-Tyrolia hold
|
||||
|
||||
Germany
|
||||
A Munich 2:0 - Tyrolia
|
||||
A Munich 1:1 supports A Munich 2:0 - Tyrolia
|
||||
A a-Munich - Tyrolia
|
||||
A b-Munich supports A a-Munich - Tyrolia
|
||||
</pre>
|
||||
<p>A Munich 2:0 dislodges A Tyrolia 2:0. No alternate timeline is created.</p>
|
||||
<p>A a-Munich dislodges A a-Tyrolia. No alternate timeline is created.</p>
|
||||
<div class="figures">
|
||||
<canvas id="canvas-3-A-5-before" width="0" height="0"></canvas>
|
||||
<script>
|
||||
|
@ -567,19 +571,11 @@ The order here is for Army Munich in 2:0. The move being supported is for Army M
|
|||
<p>Following <a href="#3.A.5">3.A.5</a>, a cross-timeline support that previously succeeded is cut.</p>
|
||||
<pre>
|
||||
Germany
|
||||
A Munich 2:0 - Tyrolia
|
||||
A Munich 1:1 supports A Munich 2:0 - Tyrolia
|
||||
A a-Tyrolia holds
|
||||
A b-Munich holds
|
||||
|
||||
Austria
|
||||
A Tyrolia 2:0 holds
|
||||
A Tyrolia 1:1 holds
|
||||
|
||||
Germany
|
||||
A Tyrolia 3:0 holds
|
||||
A Munich 2:1 holds
|
||||
|
||||
Austria
|
||||
A Tyrolia 2:1 - Munich 1:1
|
||||
A b-Tyrolia@2 - b-Munich@1
|
||||
</pre>
|
||||
<p>Cutting the support does not change the past or cause a timeline fork.</p>
|
||||
<div class="figures">
|
||||
|
|
|
@ -27,12 +27,12 @@ public class Season
|
|||
/// <summary>
|
||||
/// The timeline to which this season belongs.
|
||||
/// </summary>
|
||||
public int Timeline { get; }
|
||||
public string Timeline { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The season's spatial location as a turn-timeline tuple.
|
||||
/// The season's spatial location as a timeline-turn tuple.
|
||||
/// </summary>
|
||||
public (int Turn, int Timeline) Coord => (this.Turn, this.Timeline);
|
||||
public (string Timeline, int Turn) Coord => (this.Timeline, this.Turn);
|
||||
|
||||
/// <summary>
|
||||
/// The shared timeline number generator.
|
||||
|
@ -45,18 +45,15 @@ public class Season
|
|||
public IEnumerable<Season> Futures => this.FutureList;
|
||||
private List<Season> FutureList { get; }
|
||||
|
||||
private Season(Season? past, int turn, int timeline, TimelineFactory factory)
|
||||
private Season(Season? past, int turn, string timeline, TimelineFactory factory)
|
||||
{
|
||||
this.Past = past;
|
||||
this.Turn = turn;
|
||||
this.Timeline = timeline;
|
||||
this.Timelines = factory;
|
||||
this.FutureList = new();
|
||||
this.FutureList = [];
|
||||
|
||||
if (past != null)
|
||||
{
|
||||
past.FutureList.Add(this);
|
||||
}
|
||||
past?.FutureList.Add(this);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
@ -73,7 +70,7 @@ public class Season
|
|||
return new Season(
|
||||
past: null,
|
||||
turn: FIRST_TURN,
|
||||
timeline: TimelineFactory.StringToInt(factory.NextTimeline()),
|
||||
timeline: factory.NextTimeline(),
|
||||
factory: factory);
|
||||
}
|
||||
|
||||
|
@ -81,13 +78,13 @@ public class Season
|
|||
/// Create a season immediately after this one in the same timeline.
|
||||
/// </summary>
|
||||
public Season MakeNext()
|
||||
=> new Season(this, this.Turn + 1, this.Timeline, this.Timelines);
|
||||
=> new(this, Turn + 1, Timeline, Timelines);
|
||||
|
||||
/// <summary>
|
||||
/// Create a season immediately after this one in a new timeline.
|
||||
/// </summary>
|
||||
public Season MakeFork()
|
||||
=> new Season(this, this.Turn + 1, TimelineFactory.StringToInt(Timelines.NextTimeline()), this.Timelines);
|
||||
=> new(this, Turn + 1, Timelines.NextTimeline(), Timelines);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the first season in this season's timeline. The first season is the
|
||||
|
@ -127,7 +124,7 @@ public class Season
|
|||
/// </summary>
|
||||
public IEnumerable<Season> GetAdjacentSeasons()
|
||||
{
|
||||
List<Season> adjacents = new();
|
||||
List<Season> adjacents = [];
|
||||
|
||||
// The immediate past and all immediate futures are adjacent.
|
||||
if (this.Past != null) adjacents.Add(this.Past);
|
||||
|
|
|
@ -207,29 +207,27 @@ public class World
|
|||
/// <summary>
|
||||
/// Get a season by coordinate. Throws if the season is not found.
|
||||
/// </summary>
|
||||
public Season GetSeason(int turn, int timeline)
|
||||
public Season GetSeason(string timeline, int turn)
|
||||
{
|
||||
Season? foundSeason = this.Seasons.SingleOrDefault(
|
||||
s => s!.Turn == turn && s.Timeline == timeline,
|
||||
null);
|
||||
if (foundSeason == null) throw new KeyNotFoundException(
|
||||
$"Season {turn}:{timeline} not found");
|
||||
null)
|
||||
?? throw new KeyNotFoundException($"Season {timeline}@{turn} not found");
|
||||
return foundSeason;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a unit in a province. Throws if there are duplicate units.
|
||||
/// </summary>
|
||||
public Unit GetUnitAt(string provinceName, (int turn, int timeline)? seasonCoord = null)
|
||||
public Unit GetUnitAt(string provinceName, (string timeline, int turn)? seasonCoord = null)
|
||||
{
|
||||
Province province = Map.GetProvince(provinceName);
|
||||
seasonCoord ??= (this.RootSeason.Turn, this.RootSeason.Timeline);
|
||||
Season season = GetSeason(seasonCoord.Value.turn, seasonCoord.Value.timeline);
|
||||
seasonCoord ??= (this.RootSeason.Timeline, this.RootSeason.Turn);
|
||||
Season season = GetSeason(seasonCoord.Value.timeline, seasonCoord.Value.turn);
|
||||
Unit? foundUnit = this.Units.SingleOrDefault(
|
||||
u => u!.Province == province && u.Season == season,
|
||||
null);
|
||||
if (foundUnit == null) throw new KeyNotFoundException(
|
||||
$"Unit at {province} at {season} not found");
|
||||
null)
|
||||
?? throw new KeyNotFoundException($"Unit at {province} at {season} not found");
|
||||
return foundUnit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,12 +14,12 @@ public class TimeTravelTest
|
|||
TestCaseBuilder setup = new(World.WithStandardMap(), MovementPhaseAdjudicator.Instance);
|
||||
|
||||
// Hold to move into the future, then move back into the past.
|
||||
setup[(0, 0)]
|
||||
setup[("a", 0)]
|
||||
.GetReference(out Season s0)
|
||||
["Germany"]
|
||||
.Army("Mun").Holds().GetReference(out var mun0)
|
||||
.Execute()
|
||||
[(1, 0)]
|
||||
[("a", 1)]
|
||||
.GetReference(out Season s1)
|
||||
["Germany"]
|
||||
.Army("Mun").MovesTo("Tyr", season: s0).GetReference(out var mun1);
|
||||
|
@ -41,15 +41,15 @@ public class TimeTravelTest
|
|||
Is.EqualTo(1),
|
||||
"Failed to fork timeline when unit moved in");
|
||||
|
||||
// Confirm that there is a unit in Tyr 1:1 originating from Mun 1:0
|
||||
Season fork = world.GetSeason(1, 1);
|
||||
// Confirm that there is a unit in Tyr b1 originating from Mun a1
|
||||
Season fork = world.GetSeason("b", 1);
|
||||
Unit originalUnit = world.GetUnitAt("Mun", s0.Coord);
|
||||
Unit aMun0 = world.GetUnitAt("Mun", s1.Coord);
|
||||
Unit aTyr = world.GetUnitAt("Tyr", fork.Coord);
|
||||
Assert.That(aTyr.Past, Is.EqualTo(mun1.Order.Unit));
|
||||
Assert.That(aTyr.Past?.Past, Is.EqualTo(mun0.Order.Unit));
|
||||
|
||||
// Confirm that there is a unit in Mun 1:1 originating from Mun 0:0
|
||||
// Confirm that there is a unit in Mun b1 originating from Mun a0
|
||||
Unit aMun1 = world.GetUnitAt("Mun", fork.Coord);
|
||||
Assert.That(aMun1.Past, Is.EqualTo(originalUnit));
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ public class TimeTravelTest
|
|||
TestCaseBuilder setup = new(World.WithStandardMap(), MovementPhaseAdjudicator.Instance);
|
||||
|
||||
// Fail to dislodge on the first turn, then support the move so it succeeds.
|
||||
setup[(0, 0)]
|
||||
setup[("a", 0)]
|
||||
.GetReference(out Season s0)
|
||||
["Germany"]
|
||||
.Army("Mun").MovesTo("Tyr").GetReference(out var mun0)
|
||||
|
@ -75,7 +75,7 @@ public class TimeTravelTest
|
|||
Assert.That(tyr0, Is.NotDislodged);
|
||||
setup.UpdateWorld();
|
||||
|
||||
setup[(1, 0)]
|
||||
setup[("a", 1)]
|
||||
["Germany"]
|
||||
.Army("Mun").Supports.Army("Mun", season: s0).MoveTo("Tyr").GetReference(out var mun1)
|
||||
["Austria"]
|
||||
|
@ -91,16 +91,16 @@ public class TimeTravelTest
|
|||
|
||||
// Confirm that an alternate future is created.
|
||||
World world = setup.UpdateWorld();
|
||||
Season fork = world.GetSeason(1, 1);
|
||||
Season fork = world.GetSeason("b", 1);
|
||||
Unit tyr1 = world.GetUnitAt("Tyr", fork.Coord);
|
||||
Assert.That(
|
||||
tyr1.Past,
|
||||
Is.EqualTo(mun0.Order.Unit),
|
||||
"Expected A Mun 0:0 to advance to Tyr 1:1");
|
||||
"Expected A Mun a0 to advance to Tyr b1");
|
||||
Assert.That(
|
||||
world.RetreatingUnits.Count,
|
||||
Is.EqualTo(1),
|
||||
"Expected A Tyr 0:0 to be in retreat");
|
||||
"Expected A Tyr a0 to be in retreat");
|
||||
Assert.That(world.RetreatingUnits.First().Unit, Is.EqualTo(tyr0.Order.Unit));
|
||||
}
|
||||
|
||||
|
@ -110,14 +110,14 @@ public class TimeTravelTest
|
|||
TestCaseBuilder setup = new(World.WithStandardMap(), MovementPhaseAdjudicator.Instance);
|
||||
|
||||
// Hold to create a future, then attempt to attack in the past.
|
||||
setup[(0, 0)]
|
||||
setup[("a", 0)]
|
||||
.GetReference(out Season s0)
|
||||
["Germany"]
|
||||
.Army("Mun").Holds()
|
||||
["Austria"]
|
||||
.Army("Tyr").Holds().GetReference(out var tyr0)
|
||||
.Execute()
|
||||
[(1, 0)]
|
||||
[("a", 1)]
|
||||
.GetReference(out Season s1)
|
||||
["Germany"]
|
||||
.Army("Mun").MovesTo("Tyr", season: s0).GetReference(out var mun1)
|
||||
|
@ -128,7 +128,7 @@ public class TimeTravelTest
|
|||
Assert.That(mun1, Is.Valid);
|
||||
setup.AdjudicateOrders();
|
||||
Assert.That(mun1, Is.Repelled);
|
||||
// The order to Tyr 0:0 should have been pulled into the adjudication set, so the reference
|
||||
// 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);
|
||||
|
||||
|
@ -140,7 +140,7 @@ public class TimeTravelTest
|
|||
Is.EqualTo(1),
|
||||
"A failed move incorrectly forked the timeline");
|
||||
Assert.That(s1.Futures.Count(), Is.EqualTo(1));
|
||||
Season s2 = world.GetSeason(s1.Turn + 1, s1.Timeline);
|
||||
Season s2 = world.GetSeason(s1.Timeline, s1.Turn + 1);
|
||||
Assert.That(s2.Futures.Count(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
|
@ -150,7 +150,7 @@ public class TimeTravelTest
|
|||
TestCaseBuilder setup = new(World.WithStandardMap(), MovementPhaseAdjudicator.Instance);
|
||||
|
||||
// Move, then support the past move even though it succeeded already.
|
||||
setup[(0, 0)]
|
||||
setup[("a", 0)]
|
||||
.GetReference(out Season s0)
|
||||
["Germany"]
|
||||
.Army("Mun").MovesTo("Tyr").GetReference(out var mun0)
|
||||
|
@ -162,7 +162,7 @@ public class TimeTravelTest
|
|||
Assert.That(mun0, Is.Victorious);
|
||||
setup.UpdateWorld();
|
||||
|
||||
setup[(1, 0)]
|
||||
setup[("a", 1)]
|
||||
.GetReference(out Season s1)
|
||||
["Germany"]
|
||||
.Army("Tyr").Holds()
|
||||
|
@ -182,7 +182,7 @@ public class TimeTravelTest
|
|||
Is.EqualTo(1),
|
||||
"A superfluous support incorrectly forked the timeline");
|
||||
Assert.That(s1.Futures.Count(), Is.EqualTo(1));
|
||||
Season s2 = world.GetSeason(s1.Turn + 1, s1.Timeline);
|
||||
Season s2 = world.GetSeason(s1.Timeline, s1.Turn + 1);
|
||||
Assert.That(s2.Futures.Count(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
|
@ -192,51 +192,51 @@ public class TimeTravelTest
|
|||
TestCaseBuilder setup = new(World.WithStandardMap(), MovementPhaseAdjudicator.Instance);
|
||||
|
||||
// London creates two timelines by moving into the past.
|
||||
setup[(0, 0)]
|
||||
.GetReference(out var s0_0)
|
||||
setup[("a", 0)]
|
||||
.GetReference(out var a0)
|
||||
["England"].Army("Lon").Holds()
|
||||
["Austria"].Army("Tyr").Holds()
|
||||
["Germany"].Army("Mun").Holds()
|
||||
.Execute()
|
||||
[(1, 0)]
|
||||
["England"].Army("Lon").MovesTo("Yor", s0_0)
|
||||
[("a", 1)]
|
||||
["England"].Army("Lon").MovesTo("Yor", a0)
|
||||
.Execute()
|
||||
// Head seasons: 2:0 1:1
|
||||
// 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.
|
||||
[(2, 0)]
|
||||
.GetReference(out var s2_0)
|
||||
["Germany"].Army("Mun").MovesTo("Tyr").GetReference(out var mun2_0)
|
||||
["Austria"].Army("Tyr").Holds().GetReference(out var tyr2_0)
|
||||
[(1, 1)]
|
||||
.GetReference(out var s1_1)
|
||||
["Germany"].Army("Mun").Supports.Army("Mun", s2_0).MoveTo("Tyr").GetReference(out var mun1_1)
|
||||
[("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 2:0 succeeds.
|
||||
// The attack against Tyr a2 succeeds.
|
||||
setup.ValidateOrders();
|
||||
Assert.That(mun2_0, Is.Valid);
|
||||
Assert.That(tyr2_0, Is.Valid);
|
||||
Assert.That(mun1_1, Is.Valid);
|
||||
Assert.That(mun_a2, Is.Valid);
|
||||
Assert.That(tyr_a2, Is.Valid);
|
||||
Assert.That(mun_b1, Is.Valid);
|
||||
setup.AdjudicateOrders();
|
||||
Assert.That(mun2_0, Is.Victorious);
|
||||
Assert.That(tyr2_0, Is.Dislodged);
|
||||
Assert.That(mun1_1, Is.NotCut);
|
||||
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(
|
||||
s2_0.Futures.Count(),
|
||||
a2.Futures.Count(),
|
||||
Is.EqualTo(1),
|
||||
"A cross-timeline support incorrectly forked the head of the timeline");
|
||||
Assert.That(
|
||||
s1_1.Futures.Count(),
|
||||
b1.Futures.Count(),
|
||||
Is.EqualTo(1),
|
||||
"A cross-timeline support incorrectly forked the head of the timeline");
|
||||
Season s3_0 = world.GetSeason(s2_0.Turn + 1, s2_0.Timeline);
|
||||
Assert.That(s3_0.Futures.Count(), Is.EqualTo(0));
|
||||
Season s2_1 = world.GetSeason(s1_1.Turn + 1, s1_1.Timeline);
|
||||
Assert.That(s2_1.Futures.Count(), Is.EqualTo(0));
|
||||
Season a3 = world.GetSeason(a2.Timeline, a2.Turn + 1);
|
||||
Assert.That(a3.Futures.Count(), Is.EqualTo(0));
|
||||
Season b2 = world.GetSeason(b1.Timeline, b1.Turn + 1);
|
||||
Assert.That(b2.Futures.Count(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -245,63 +245,63 @@ public class TimeTravelTest
|
|||
TestCaseBuilder setup = new(World.WithStandardMap(), MovementPhaseAdjudicator.Instance);
|
||||
|
||||
// As above, only now London creates three timelines.
|
||||
setup[(0, 0)]
|
||||
.GetReference(out var s0_0)
|
||||
setup[("a", 0)]
|
||||
.GetReference(out var a0)
|
||||
["England"].Army("Lon").Holds()
|
||||
["Austria"].Army("Boh").Holds()
|
||||
["Germany"].Army("Mun").Holds()
|
||||
.Execute()
|
||||
[(1, 0)]
|
||||
["England"].Army("Lon").MovesTo("Yor", s0_0)
|
||||
[("a", 1)]
|
||||
["England"].Army("Lon").MovesTo("Yor", a0)
|
||||
.Execute()
|
||||
// Head seasons: 2:0 1:1
|
||||
[(2, 0)]
|
||||
[(1, 1)]
|
||||
["England"].Army("Yor").MovesTo("Edi", s0_0)
|
||||
// Head seasons: a2, b1
|
||||
[("a", 2)]
|
||||
[("b", 1)]
|
||||
["England"].Army("Yor").MovesTo("Edi", a0)
|
||||
.Execute()
|
||||
// Head seasons: 3:0 2:1 1:2
|
||||
// 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 3:0 by supporting the move from 2:1.
|
||||
[(3, 0)]
|
||||
.GetReference(out var s3_0)
|
||||
// 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()
|
||||
[(2, 1)]
|
||||
.GetReference(out Season s2_1)
|
||||
["Germany"].Army("Mun").Supports.Army("Mun", s3_0).MoveTo("Tyr").GetReference(out var mun2_1)
|
||||
[("b", 2)]
|
||||
.GetReference(out Season b2)
|
||||
["Germany"].Army("Mun").Supports.Army("Mun", a3).MoveTo("Tyr").GetReference(out var mun_b2)
|
||||
["Austria"].Army("Tyr").Holds()
|
||||
[(1, 2)]
|
||||
[("c", 1)]
|
||||
["Germany"].Army("Mun").Holds()
|
||||
["Austria"].Army("Tyr").Holds()
|
||||
.Execute()
|
||||
// Head seasons: 4:0 3:1 2:2
|
||||
// Now Austria cuts the support in 2:1 by attacking from 2:2.
|
||||
[(4, 0)]
|
||||
// Head seasons: a4, b3, c2
|
||||
// Now Austria cuts the support in b2 by attacking from c2.
|
||||
[("a", 4)]
|
||||
["Germany"].Army("Tyr").Holds()
|
||||
[(3, 1)]
|
||||
[("b", 3)]
|
||||
["Germany"].Army("Mun").Holds()
|
||||
["Austria"].Army("Tyr").Holds()
|
||||
[(2, 2)]
|
||||
[("c", 2)]
|
||||
["Germany"].Army("Mun").Holds()
|
||||
["Austria"].Army("Tyr").MovesTo("Mun", s2_1).GetReference(out var tyr2_2);
|
||||
["Austria"].Army("Tyr").MovesTo("Mun", b2).GetReference(out var tyr_c2);
|
||||
|
||||
// The attack on Mun 2:1 is repelled, but the support is cut.
|
||||
// The attack on Mun b2 is repelled, but the support is cut.
|
||||
setup.ValidateOrders();
|
||||
Assert.That(tyr2_2, Is.Valid);
|
||||
Assert.That(tyr_c2, Is.Valid);
|
||||
setup.AdjudicateOrders();
|
||||
Assert.That(tyr2_2, Is.Repelled);
|
||||
Assert.That(mun2_1, Is.NotDislodged);
|
||||
Assert.That(mun2_1, Is.Cut);
|
||||
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(
|
||||
s3_0.Futures.Count(),
|
||||
a3.Futures.Count(),
|
||||
Is.EqualTo(1),
|
||||
"A cross-timeline support cut incorrectly forked the timeline");
|
||||
Assert.That(
|
||||
s2_1.Futures.Count(),
|
||||
b2.Futures.Count(),
|
||||
Is.EqualTo(1),
|
||||
"A cross-timeline support cut incorrectly forked the timeline");
|
||||
}
|
||||
|
|
|
@ -186,7 +186,7 @@ public class MovementAdjudicatorTest
|
|||
public void Update_DoubleHold()
|
||||
{
|
||||
TestCaseBuilder setup = new(World.WithStandardMap(), MovementPhaseAdjudicator.Instance);
|
||||
setup[(0, 0)]
|
||||
setup[("a", 0)]
|
||||
.GetReference(out Season s1)
|
||||
["Germany"]
|
||||
.Army("Mun").Holds().GetReference(out var mun1);
|
||||
|
@ -199,7 +199,7 @@ public class MovementAdjudicatorTest
|
|||
World updated = setup.UpdateWorld();
|
||||
|
||||
// Confirm the future was created
|
||||
Season s2 = updated.GetSeason(1, 0);
|
||||
Season s2 = updated.GetSeason("a", 1);
|
||||
Assert.That(s2.Past, Is.EqualTo(s1));
|
||||
Assert.That(s2.Futures, Is.Empty);
|
||||
Assert.That(s2.Timeline, Is.EqualTo(s1.Timeline));
|
||||
|
@ -212,7 +212,7 @@ public class MovementAdjudicatorTest
|
|||
Assert.That(u2.Past, Is.EqualTo(mun1.Order.Unit));
|
||||
Assert.That(u2.Season, Is.EqualTo(s2));
|
||||
|
||||
setup[(1, 0)]
|
||||
setup[("a", 1)]
|
||||
["Germany"]
|
||||
.Army("Mun").Holds().GetReference(out var mun2);
|
||||
|
||||
|
@ -227,7 +227,7 @@ public class MovementAdjudicatorTest
|
|||
|
||||
// Update the world again
|
||||
updated = setup.UpdateWorld();
|
||||
Season s3 = updated.GetSeason(s2.Turn + 1, s2.Timeline);
|
||||
Season s3 = updated.GetSeason(s2.Timeline, s2.Turn + 1);
|
||||
Unit u3 = updated.GetUnitAt("Mun", s3.Coord);
|
||||
Assert.That(u3.Past, Is.EqualTo(mun2.Order.Unit));
|
||||
}
|
||||
|
@ -236,7 +236,7 @@ public class MovementAdjudicatorTest
|
|||
public void Update_DoubleMove()
|
||||
{
|
||||
TestCaseBuilder setup = new(World.WithStandardMap(), MovementPhaseAdjudicator.Instance);
|
||||
setup[(0, 0)]
|
||||
setup[("a", 0)]
|
||||
.GetReference(out Season s1)
|
||||
["Germany"]
|
||||
.Army("Mun").MovesTo("Tyr").GetReference(out var mun1);
|
||||
|
@ -249,7 +249,7 @@ public class MovementAdjudicatorTest
|
|||
World updated = setup.UpdateWorld();
|
||||
|
||||
// Confirm the future was created
|
||||
Season s2 = updated.GetSeason(s1.Turn + 1, s1.Timeline);
|
||||
Season s2 = updated.GetSeason(s1.Timeline, s1.Turn + 1);
|
||||
Assert.That(s2.Past, Is.EqualTo(s1));
|
||||
Assert.That(s2.Futures, Is.Empty);
|
||||
Assert.That(s2.Timeline, Is.EqualTo(s1.Timeline));
|
||||
|
@ -262,7 +262,7 @@ public class MovementAdjudicatorTest
|
|||
Assert.That(u2.Past, Is.EqualTo(mun1.Order.Unit));
|
||||
Assert.That(u2.Season, Is.EqualTo(s2));
|
||||
|
||||
setup[(1, 0)]
|
||||
setup[("a", 1)]
|
||||
["Germany"]
|
||||
.Army("Tyr").MovesTo("Mun").GetReference(out var tyr2);
|
||||
|
||||
|
@ -277,7 +277,7 @@ public class MovementAdjudicatorTest
|
|||
|
||||
// Update the world again
|
||||
updated = setup.UpdateWorld();
|
||||
Season s3 = updated.GetSeason(s2.Turn + 1, s2.Timeline);
|
||||
Season s3 = updated.GetSeason(s2.Timeline, s2.Turn + 1);
|
||||
Unit u3 = updated.GetUnitAt("Mun", s3.Coord);
|
||||
Assert.That(u3.Past, Is.EqualTo(u2));
|
||||
}
|
||||
|
|
|
@ -18,14 +18,14 @@ public class SeasonTests
|
|||
Season c1 = a1.MakeFork();
|
||||
Season d1 = a2.MakeFork();
|
||||
|
||||
Assert.That(a0.Timeline, Is.EqualTo(0), "Unexpected trunk timeline number");
|
||||
Assert.That(a1.Timeline, Is.EqualTo(0), "Unexpected trunk timeline number");
|
||||
Assert.That(a2.Timeline, Is.EqualTo(0), "Unexpected trunk timeline number");
|
||||
Assert.That(a3.Timeline, Is.EqualTo(0), "Unexpected trunk timeline number");
|
||||
Assert.That(b1.Timeline, Is.EqualTo(1), "Unexpected first alt number");
|
||||
Assert.That(b2.Timeline, Is.EqualTo(1), "Unexpected first alt number");
|
||||
Assert.That(c1.Timeline, Is.EqualTo(2), "Unexpected second alt number");
|
||||
Assert.That(d1.Timeline, Is.EqualTo(3), "Unexpected third alt number");
|
||||
Assert.That(a0.Timeline, Is.EqualTo("a"), "Unexpected trunk timeline");
|
||||
Assert.That(a1.Timeline, Is.EqualTo("a"), "Unexpected trunk timeline");
|
||||
Assert.That(a2.Timeline, Is.EqualTo("a"), "Unexpected trunk timeline");
|
||||
Assert.That(a3.Timeline, Is.EqualTo("a"), "Unexpected trunk timeline");
|
||||
Assert.That(b1.Timeline, Is.EqualTo("b"), "Unexpected first alt");
|
||||
Assert.That(b2.Timeline, Is.EqualTo("b"), "Unexpected first alt");
|
||||
Assert.That(c1.Timeline, Is.EqualTo("c"), "Unexpected second alt");
|
||||
Assert.That(d1.Timeline, Is.EqualTo("d"), "Unexpected third alt");
|
||||
|
||||
Assert.That(a0.Turn, Is.EqualTo(Season.FIRST_TURN + 0), "Unexpected first turn number");
|
||||
Assert.That(a1.Turn, Is.EqualTo(Season.FIRST_TURN + 1), "Unexpected next turn number");
|
||||
|
@ -57,9 +57,9 @@ public class SeasonTests
|
|||
Season s4 = s2.MakeFork();
|
||||
World updated = world.Update(seasons: world.Seasons.Append(s2).Append(s3).Append(s4));
|
||||
|
||||
Assert.That(updated.GetSeason(Season.FIRST_TURN, 0), Is.EqualTo(updated.RootSeason));
|
||||
Assert.That(updated.GetSeason(Season.FIRST_TURN + 1, 0), Is.EqualTo(s2));
|
||||
Assert.That(updated.GetSeason(Season.FIRST_TURN + 2, 0), Is.EqualTo(s3));
|
||||
Assert.That(updated.GetSeason(Season.FIRST_TURN + 2, 1), Is.EqualTo(s4));
|
||||
Assert.That(updated.GetSeason("a", Season.FIRST_TURN), Is.EqualTo(updated.RootSeason));
|
||||
Assert.That(updated.GetSeason("a", Season.FIRST_TURN + 1), Is.EqualTo(s2));
|
||||
Assert.That(updated.GetSeason("a", Season.FIRST_TURN + 2), Is.EqualTo(s3));
|
||||
Assert.That(updated.GetSeason("b", Season.FIRST_TURN + 2), Is.EqualTo(s4));
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ public class TestCaseBuilder
|
|||
/// <summary>
|
||||
/// Choose a new season to define orders for.
|
||||
/// </summary>
|
||||
public ISeasonContext this[(int turn, int timeline) seasonCoord] { get; }
|
||||
public ISeasonContext this[(string timeline, int turn) seasonCoord] { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the context for defining the orders for a power.
|
||||
|
@ -40,7 +40,7 @@ public class TestCaseBuilder
|
|||
/// <summary>
|
||||
/// Choose a new season to define orders for.
|
||||
/// </summary>
|
||||
public ISeasonContext this[(int turn, int timeline) seasonCoord] { get; }
|
||||
public ISeasonContext this[(string timeline, int turn) seasonCoord] { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the context for defining the orders for another power.
|
||||
|
@ -188,7 +188,7 @@ public class TestCaseBuilder
|
|||
/// <summary>
|
||||
/// Choose a new season to define orders for.
|
||||
/// </summary>
|
||||
public ISeasonContext this[(int turn, int timeline) seasonCoord] { get; }
|
||||
public ISeasonContext this[(string timeline, int turn) seasonCoord] { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the context for defining the orders for another power.
|
||||
|
@ -234,13 +234,13 @@ public class TestCaseBuilder
|
|||
/// <summary>
|
||||
/// Get the context for defining the orders for a power. Defaults to the root season.
|
||||
/// </summary>
|
||||
public IPowerContext this[string powerName] => this[(0, 0)][powerName];
|
||||
public IPowerContext this[string powerName] => this[("a", 0)][powerName];
|
||||
|
||||
/// <summary>
|
||||
/// Get the context for defining the orders for a season.
|
||||
/// </summary>
|
||||
public ISeasonContext this[(int turn, int timeline) seasonCoord]
|
||||
=> new SeasonContext(this, this.World.GetSeason(seasonCoord.turn, seasonCoord.timeline));
|
||||
public ISeasonContext this[(string timeline, int turn) seasonCoord]
|
||||
=> new SeasonContext(this, this.World.GetSeason(seasonCoord.timeline, seasonCoord.turn));
|
||||
|
||||
/// <summary>
|
||||
/// Get a unit matching a description. If no such unit exists, one is created and added to the
|
||||
|
@ -327,8 +327,8 @@ public class TestCaseBuilder
|
|||
this.Season = season;
|
||||
}
|
||||
|
||||
public ISeasonContext this[(int turn, int timeline) seasonCoord]
|
||||
=> this.Builder[(seasonCoord.turn, seasonCoord.timeline)];
|
||||
public ISeasonContext this[(string timeline, int turn) seasonCoord]
|
||||
=> this.Builder[(seasonCoord.timeline, seasonCoord.turn)];
|
||||
|
||||
public IPowerContext this[string powerName]
|
||||
=> new PowerContext(this, this.Builder.World.Map.GetPower(powerName));
|
||||
|
@ -353,7 +353,7 @@ public class TestCaseBuilder
|
|||
this.Power = Power;
|
||||
}
|
||||
|
||||
public ISeasonContext this[(int turn, int timeline) seasonCoord]
|
||||
public ISeasonContext this[(string timeline, int turn) seasonCoord]
|
||||
=> this.SeasonContext[seasonCoord];
|
||||
|
||||
public IPowerContext this[string powerName]
|
||||
|
@ -623,7 +623,7 @@ public class TestCaseBuilder
|
|||
return this.Builder;
|
||||
}
|
||||
|
||||
public ISeasonContext this[(int turn, int timeline) seasonCoord]
|
||||
public ISeasonContext this[(string timeline, int turn) seasonCoord]
|
||||
=> this.SeasonContext[seasonCoord];
|
||||
|
||||
public IPowerContext this[string powerName]
|
||||
|
|
|
@ -14,7 +14,7 @@ class TestCaseBuilderTest
|
|||
{
|
||||
TestCaseBuilder setup = new(World.WithStandardMap());
|
||||
|
||||
Assert.That(setup.World.Powers.Count(), Is.EqualTo(7), "Unexpected power count");
|
||||
Assert.That(setup.World.Powers.Count, Is.EqualTo(7), "Unexpected power count");
|
||||
Assert.That(setup.World.Units, Is.Empty, "Expected no units to be created yet");
|
||||
|
||||
setup
|
||||
|
@ -49,7 +49,7 @@ class TestCaseBuilderTest
|
|||
{
|
||||
TestCaseBuilder setup = new(World.WithStandardMap());
|
||||
|
||||
Assert.That(setup.World.Powers.Count(), Is.EqualTo(7), "Unexpected power count");
|
||||
Assert.That(setup.World.Powers.Count, Is.EqualTo(7), "Unexpected power count");
|
||||
Assert.That(setup.World.Units, Is.Empty, "Expected no units to be created yet");
|
||||
Assert.That(setup.Orders, Is.Empty, "Expected no orders to be created yet");
|
||||
|
||||
|
|
Loading…
Reference in New Issue