Refactor season futures into World
This commit is contained in:
parent
752a898123
commit
87685ec744
|
@ -50,7 +50,7 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator
|
||||||
|
|
||||||
// Invalidate any order given to a unit in the past.
|
// Invalidate any order given to a unit in the past.
|
||||||
AdjudicatorHelpers.InvalidateIfNotMatching(
|
AdjudicatorHelpers.InvalidateIfNotMatching(
|
||||||
order => !order.Unit.Season.Futures.Any(),
|
order => !world.GetFutures(order.Unit.Season).Any(),
|
||||||
ValidationReason.IneligibleForOrder,
|
ValidationReason.IneligibleForOrder,
|
||||||
ref unitOrders,
|
ref unitOrders,
|
||||||
ref validationResults);
|
ref validationResults);
|
||||||
|
@ -255,7 +255,7 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator
|
||||||
|
|
||||||
// Finally, add implicit hold orders for units without legal orders.
|
// Finally, add implicit hold orders for units without legal orders.
|
||||||
List<Unit> allOrderableUnits = world.Units
|
List<Unit> allOrderableUnits = world.Units
|
||||||
.Where(unit => !unit.Season.Futures.Any())
|
.Where(unit => !world.GetFutures(unit.Season).Any())
|
||||||
.ToList();
|
.ToList();
|
||||||
HashSet<Unit> orderedUnits = validOrders.Select(order => order.Unit).ToHashSet();
|
HashSet<Unit> orderedUnits = validOrders.Select(order => order.Unit).ToHashSet();
|
||||||
List<Unit> unorderedUnits = allOrderableUnits
|
List<Unit> unorderedUnits = allOrderableUnits
|
||||||
|
@ -324,7 +324,7 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator
|
||||||
if (advanceTimeline.Outcome == true)
|
if (advanceTimeline.Outcome == true)
|
||||||
{
|
{
|
||||||
// A timeline that doesn't have a future yet simply continues. Otherwise, it forks.
|
// A timeline that doesn't have a future yet simply continues. Otherwise, it forks.
|
||||||
createdFutures[advanceTimeline.Season] = !advanceTimeline.Season.Futures.Any()
|
createdFutures[advanceTimeline.Season] = !world.GetFutures(advanceTimeline.Season).Any()
|
||||||
? advanceTimeline.Season.MakeNext()
|
? advanceTimeline.Season.MakeNext()
|
||||||
: advanceTimeline.Season.MakeFork();
|
: advanceTimeline.Season.MakeFork();
|
||||||
}
|
}
|
||||||
|
@ -486,7 +486,7 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator
|
||||||
bool progress = false;
|
bool progress = false;
|
||||||
|
|
||||||
// A season at the head of a timeline always advances.
|
// A season at the head of a timeline always advances.
|
||||||
if (!decision.Season.Futures.Any())
|
if (!world.GetFutures(decision.Season).Any())
|
||||||
{
|
{
|
||||||
progress |= LoggedUpdate(decision, true, depth, "A timeline head always advances");
|
progress |= LoggedUpdate(decision, true, depth, "A timeline head always advances");
|
||||||
return progress;
|
return progress;
|
||||||
|
|
|
@ -115,7 +115,7 @@ public static class PathFinder
|
||||||
|
|
||||||
// The immediate past and all immediate futures are adjacent.
|
// The immediate past and all immediate futures are adjacent.
|
||||||
if (season.Past != null) adjacents.Add(world.GetSeason(season.Past));
|
if (season.Past != null) adjacents.Add(world.GetSeason(season.Past));
|
||||||
adjacents.AddRange(season.Futures);
|
adjacents.AddRange(world.GetFutures(season));
|
||||||
|
|
||||||
// Find all adjacent timelines by finding all timelines that branched off of this season's
|
// Find all adjacent timelines by finding all timelines that branched off of this season's
|
||||||
// timeline, i.e. all futures of this season's past that have different timelines. Also
|
// timeline, i.e. all futures of this season's past that have different timelines. Also
|
||||||
|
@ -127,7 +127,7 @@ public static class PathFinder
|
||||||
current = world.GetSeason(current.Past))
|
current = world.GetSeason(current.Past))
|
||||||
{
|
{
|
||||||
adjacentTimelineRoots.AddRange(
|
adjacentTimelineRoots.AddRange(
|
||||||
current.Futures.Where(s => s.Timeline != current.Timeline));
|
world.GetFutures(current).Where(s => s.Timeline != current.Timeline));
|
||||||
}
|
}
|
||||||
|
|
||||||
// At the end of the for loop, if this season is part of the first timeline, then current
|
// At the end of the for loop, if this season is part of the first timeline, then current
|
||||||
|
@ -137,7 +137,8 @@ public static class PathFinder
|
||||||
// the first timeline by definition cannot have co-branches.
|
// the first timeline by definition cannot have co-branches.
|
||||||
if (current?.Past != null && world.GetSeason(current.Past) is Season past)
|
if (current?.Past != null && world.GetSeason(current.Past) is Season past)
|
||||||
{
|
{
|
||||||
IEnumerable<Season> cobranchRoots = past.Futures
|
IEnumerable<Season> cobranchRoots = world
|
||||||
|
.GetFutures(past)
|
||||||
.Where(s => s.Timeline != current.Timeline && s.Timeline != past.Timeline);
|
.Where(s => s.Timeline != current.Timeline && s.Timeline != past.Timeline);
|
||||||
adjacentTimelineRoots.AddRange(cobranchRoots);
|
adjacentTimelineRoots.AddRange(cobranchRoots);
|
||||||
}
|
}
|
||||||
|
@ -147,7 +148,7 @@ public static class PathFinder
|
||||||
{
|
{
|
||||||
for (Season? branchSeason = timelineRoot;
|
for (Season? branchSeason = timelineRoot;
|
||||||
branchSeason != null && branchSeason.Turn <= season.Turn + 1;
|
branchSeason != null && branchSeason.Turn <= season.Turn + 1;
|
||||||
branchSeason = branchSeason.Futures
|
branchSeason = world.GetFutures(branchSeason)
|
||||||
.FirstOrDefault(s => s!.Timeline == branchSeason.Timeline, null))
|
.FirstOrDefault(s => s!.Timeline == branchSeason.Timeline, null))
|
||||||
{
|
{
|
||||||
if (branchSeason.Turn >= season.Turn - 1) adjacents.Add(branchSeason);
|
if (branchSeason.Turn >= season.Turn - 1) adjacents.Add(branchSeason);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace MultiversalDiplomacy.Model;
|
namespace MultiversalDiplomacy.Model;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -30,36 +32,32 @@ public class Season
|
||||||
public string Timeline { get; }
|
public string Timeline { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The season's spatial location as a timeline-turn tuple.
|
/// The multiversal designation of this season.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
|
public string Designation => $"{this.Timeline}{this.Turn}";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The season's multiversal location as a timeline-turn tuple for convenience.
|
||||||
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
public (string Timeline, int Turn) Coord => (this.Timeline, this.Turn);
|
public (string Timeline, int Turn) Coord => (this.Timeline, this.Turn);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The shared timeline number generator.
|
/// The shared timeline number generator.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
private TimelineFactory Timelines { get; }
|
private TimelineFactory Timelines { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Future seasons created directly from this season.
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerable<Season> Futures => this.FutureList;
|
|
||||||
private List<Season> FutureList { get; }
|
|
||||||
|
|
||||||
private Season(Season? past, int turn, string timeline, TimelineFactory factory)
|
private Season(Season? past, int turn, string timeline, TimelineFactory factory)
|
||||||
{
|
{
|
||||||
this.Past = past?.ToString();
|
this.Past = past?.ToString();
|
||||||
this.Turn = turn;
|
this.Turn = turn;
|
||||||
this.Timeline = timeline;
|
this.Timeline = timeline;
|
||||||
this.Timelines = factory;
|
this.Timelines = factory;
|
||||||
this.FutureList = [];
|
|
||||||
|
|
||||||
past?.FutureList.Add(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString() => Designation;
|
||||||
{
|
|
||||||
return $"{this.Timeline}{this.Turn}";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a root season at the beginning of time.
|
/// Create a root season at the beginning of time.
|
||||||
|
|
|
@ -232,6 +232,21 @@ public class World
|
||||||
public Season GetSeason(string designation)
|
public Season GetSeason(string designation)
|
||||||
=> SeasonLookup[designation];
|
=> SeasonLookup[designation];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all seasons that are immediate futures of a season.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="present">A season designation.</param>
|
||||||
|
/// <returns>The immediate futures of the designated season.</returns>
|
||||||
|
public IEnumerable<Season> GetFutures(string present)
|
||||||
|
=> Seasons.Where(future => future.Past == present);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all seasons that are immediate futures of a season.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="present">A season.</param>
|
||||||
|
/// <returns>The immediate futures of the season.</returns>
|
||||||
|
public IEnumerable<Season> GetFutures(Season present) => GetFutures(present.Designation);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the first season in this season's timeline. The first season is the
|
/// Returns the first season in this season's timeline. The first season is the
|
||||||
/// root of the first timeline. The earliest season in each alternate timeline is
|
/// root of the first timeline. The earliest season in each alternate timeline is
|
||||||
|
|
|
@ -136,12 +136,12 @@ public class TimeTravelTest
|
||||||
// change the past and therefore did not create a new timeline.
|
// change the past and therefore did not create a new timeline.
|
||||||
World world = setup.UpdateWorld();
|
World world = setup.UpdateWorld();
|
||||||
Assert.That(
|
Assert.That(
|
||||||
s0.Futures.Count(),
|
world.GetFutures(s0).Count(),
|
||||||
Is.EqualTo(1),
|
Is.EqualTo(1),
|
||||||
"A failed move incorrectly forked the timeline");
|
"A failed move incorrectly forked the timeline");
|
||||||
Assert.That(s1.Futures.Count(), Is.EqualTo(1));
|
Assert.That(world.GetFutures(s1).Count(), Is.EqualTo(1));
|
||||||
Season s2 = world.GetSeason(s1.Timeline, s1.Turn + 1);
|
Season s2 = world.GetSeason(s1.Timeline, s1.Turn + 1);
|
||||||
Assert.That(s2.Futures.Count(), Is.EqualTo(0));
|
Assert.That(world.GetFutures(s2).Count(), Is.EqualTo(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -178,12 +178,12 @@ public class TimeTravelTest
|
||||||
// ...since it succeeded anyway, no fork is created.
|
// ...since it succeeded anyway, no fork is created.
|
||||||
World world = setup.UpdateWorld();
|
World world = setup.UpdateWorld();
|
||||||
Assert.That(
|
Assert.That(
|
||||||
s0.Futures.Count(),
|
world.GetFutures(s0).Count(),
|
||||||
Is.EqualTo(1),
|
Is.EqualTo(1),
|
||||||
"A superfluous support incorrectly forked the timeline");
|
"A superfluous support incorrectly forked the timeline");
|
||||||
Assert.That(s1.Futures.Count(), Is.EqualTo(1));
|
Assert.That(world.GetFutures(s1).Count(), Is.EqualTo(1));
|
||||||
Season s2 = world.GetSeason(s1.Timeline, s1.Turn + 1);
|
Season s2 = world.GetSeason(s1.Timeline, s1.Turn + 1);
|
||||||
Assert.That(s2.Futures.Count(), Is.EqualTo(0));
|
Assert.That(world.GetFutures(s2).Count(), Is.EqualTo(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -226,17 +226,17 @@ public class TimeTravelTest
|
||||||
// Since both seasons were at the head of their timelines, there should be no forking.
|
// Since both seasons were at the head of their timelines, there should be no forking.
|
||||||
World world = setup.UpdateWorld();
|
World world = setup.UpdateWorld();
|
||||||
Assert.That(
|
Assert.That(
|
||||||
a2.Futures.Count(),
|
world.GetFutures(a2).Count(),
|
||||||
Is.EqualTo(1),
|
Is.EqualTo(1),
|
||||||
"A cross-timeline support incorrectly forked the head of the timeline");
|
"A cross-timeline support incorrectly forked the head of the timeline");
|
||||||
Assert.That(
|
Assert.That(
|
||||||
b1.Futures.Count(),
|
world.GetFutures(b1).Count(),
|
||||||
Is.EqualTo(1),
|
Is.EqualTo(1),
|
||||||
"A cross-timeline support incorrectly forked the head of the timeline");
|
"A cross-timeline support incorrectly forked the head of the timeline");
|
||||||
Season a3 = world.GetSeason(a2.Timeline, a2.Turn + 1);
|
Season a3 = world.GetSeason(a2.Timeline, a2.Turn + 1);
|
||||||
Assert.That(a3.Futures.Count(), Is.EqualTo(0));
|
Assert.That(world.GetFutures(a3).Count(), Is.EqualTo(0));
|
||||||
Season b2 = world.GetSeason(b1.Timeline, b1.Turn + 1);
|
Season b2 = world.GetSeason(b1.Timeline, b1.Turn + 1);
|
||||||
Assert.That(b2.Futures.Count(), Is.EqualTo(0));
|
Assert.That(world.GetFutures(b2).Count(), Is.EqualTo(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -297,11 +297,11 @@ public class TimeTravelTest
|
||||||
// wasn't changed in this timeline.
|
// wasn't changed in this timeline.
|
||||||
World world = setup.UpdateWorld();
|
World world = setup.UpdateWorld();
|
||||||
Assert.That(
|
Assert.That(
|
||||||
a3.Futures.Count(),
|
world.GetFutures(a3).Count(),
|
||||||
Is.EqualTo(1),
|
Is.EqualTo(1),
|
||||||
"A cross-timeline support cut incorrectly forked the timeline");
|
"A cross-timeline support cut incorrectly forked the timeline");
|
||||||
Assert.That(
|
Assert.That(
|
||||||
b2.Futures.Count(),
|
world.GetFutures(b2).Count(),
|
||||||
Is.EqualTo(1),
|
Is.EqualTo(1),
|
||||||
"A cross-timeline support cut incorrectly forked the timeline");
|
"A cross-timeline support cut incorrectly forked the timeline");
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,7 +172,7 @@ public class MovementAdjudicatorTest
|
||||||
Assert.That(updated.Seasons.Count, Is.EqualTo(2));
|
Assert.That(updated.Seasons.Count, Is.EqualTo(2));
|
||||||
Season future = updated.Seasons.Single(s => s != updated.RootSeason);
|
Season future = updated.Seasons.Single(s => s != updated.RootSeason);
|
||||||
Assert.That(future.Past, Is.EqualTo(updated.RootSeason.ToString()));
|
Assert.That(future.Past, Is.EqualTo(updated.RootSeason.ToString()));
|
||||||
Assert.That(future.Futures, Is.Empty);
|
Assert.That(updated.GetFutures(future), Is.Empty);
|
||||||
Assert.That(future.Timeline, Is.EqualTo(updated.RootSeason.Timeline));
|
Assert.That(future.Timeline, Is.EqualTo(updated.RootSeason.Timeline));
|
||||||
Assert.That(future.Turn, Is.EqualTo(Season.FIRST_TURN + 1));
|
Assert.That(future.Turn, Is.EqualTo(Season.FIRST_TURN + 1));
|
||||||
|
|
||||||
|
@ -201,7 +201,7 @@ public class MovementAdjudicatorTest
|
||||||
// Confirm the future was created
|
// Confirm the future was created
|
||||||
Season s2 = updated.GetSeason("a1");
|
Season s2 = updated.GetSeason("a1");
|
||||||
Assert.That(s2.Past, Is.EqualTo(s1.ToString()));
|
Assert.That(s2.Past, Is.EqualTo(s1.ToString()));
|
||||||
Assert.That(s2.Futures, Is.Empty);
|
Assert.That(updated.GetFutures(s2), Is.Empty);
|
||||||
Assert.That(s2.Timeline, Is.EqualTo(s1.Timeline));
|
Assert.That(s2.Timeline, Is.EqualTo(s1.Timeline));
|
||||||
Assert.That(s2.Turn, Is.EqualTo(s1.Turn + 1));
|
Assert.That(s2.Turn, Is.EqualTo(s1.Turn + 1));
|
||||||
|
|
||||||
|
@ -251,7 +251,7 @@ public class MovementAdjudicatorTest
|
||||||
// Confirm the future was created
|
// Confirm the future was created
|
||||||
Season s2 = updated.GetSeason(s1.Timeline, s1.Turn + 1);
|
Season s2 = updated.GetSeason(s1.Timeline, s1.Turn + 1);
|
||||||
Assert.That(s2.Past, Is.EqualTo(s1.ToString()));
|
Assert.That(s2.Past, Is.EqualTo(s1.ToString()));
|
||||||
Assert.That(s2.Futures, Is.Empty);
|
Assert.That(updated.GetFutures(s2), Is.Empty);
|
||||||
Assert.That(s2.Timeline, Is.EqualTo(s1.Timeline));
|
Assert.That(s2.Timeline, Is.EqualTo(s1.Timeline));
|
||||||
Assert.That(s2.Turn, Is.EqualTo(s1.Turn + 1));
|
Assert.That(s2.Turn, Is.EqualTo(s1.Turn + 1));
|
||||||
|
|
||||||
|
|
|
@ -61,5 +61,10 @@ public class SeasonTests
|
||||||
Assert.That(world.InAdjacentTimeline(b3, a3), Is.True, "Expected alts to be adjacent to origin");
|
Assert.That(world.InAdjacentTimeline(b3, a3), Is.True, "Expected alts to be adjacent to origin");
|
||||||
Assert.That(world.InAdjacentTimeline(b3, c2), Is.True, "Expected alts with common origin to be adjacent");
|
Assert.That(world.InAdjacentTimeline(b3, c2), Is.True, "Expected alts with common origin to be adjacent");
|
||||||
Assert.That(world.InAdjacentTimeline(b3, d3), Is.False, "Expected alts from different origins not to be adjacent");
|
Assert.That(world.InAdjacentTimeline(b3, d3), Is.False, "Expected alts from different origins not to be adjacent");
|
||||||
|
|
||||||
|
Assert.That(world.GetFutures(a0), Is.EquivalentTo(new List<Season> { a1 }), "Unexpected futures");
|
||||||
|
Assert.That(world.GetFutures(a1), Is.EquivalentTo(new List<Season> { a2, b2, c2 }), "Unexpected futures");
|
||||||
|
Assert.That(world.GetFutures(a2), Is.EquivalentTo(new List<Season> { a3, d3 }), "Unexpected futures");
|
||||||
|
Assert.That(world.GetFutures(b2), Is.EquivalentTo(new List<Season> { b3 }), "Unexpected futures");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue