Refactor timelines and season creation logic into World
This commit is contained in:
parent
58f877425a
commit
345d54f960
|
@ -312,9 +312,9 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator
|
||||||
|
|
||||||
// All moves to a particular season in a single phase result in the same future. Keep a
|
// All moves to a particular season in a single phase result in the same future. Keep a
|
||||||
// record of when a future season has been created.
|
// record of when a future season has been created.
|
||||||
Dictionary<Season, Season> createdFutures = new();
|
Dictionary<Season, Season> createdFutures = [];
|
||||||
List<Unit> createdUnits = new();
|
List<Unit> createdUnits = [];
|
||||||
List<RetreatingUnit> retreats = new();
|
List<RetreatingUnit> retreats = [];
|
||||||
|
|
||||||
// Populate createdFutures with the timeline fork decisions
|
// Populate createdFutures with the timeline fork decisions
|
||||||
logger.Log(1, "Processing AdvanceTimeline decisions");
|
logger.Log(1, "Processing AdvanceTimeline decisions");
|
||||||
|
@ -324,9 +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] = !world.GetFutures(advanceTimeline.Season).Any()
|
createdFutures[advanceTimeline.Season] = world.ContinueOrFork(advanceTimeline.Season);
|
||||||
? advanceTimeline.Season.MakeNext()
|
|
||||||
: advanceTimeline.Season.MakeFork();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,4 +20,10 @@ public static class ModelExtensions
|
||||||
{
|
{
|
||||||
return $"{coord.season.Timeline}-{coord.province.Abbreviations[0]}@{coord.season.Turn}";
|
return $"{coord.season.Timeline}-{coord.province.Abbreviations[0]}@{coord.season.Turn}";
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public static World ContinueOrFork(this World world, Season season, out Season future)
|
||||||
|
{
|
||||||
|
future = world.ContinueOrFork(season);
|
||||||
|
return world.Update(seasons: world.Seasons.Append(future));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -43,44 +43,12 @@ public class Season
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public (string Timeline, int Turn) Coord => (this.Timeline, this.Turn);
|
public (string Timeline, int Turn) Coord => (this.Timeline, this.Turn);
|
||||||
|
|
||||||
/// <summary>
|
public Season(string? past, int turn, string timeline)
|
||||||
/// The shared timeline number generator.
|
|
||||||
/// </summary>
|
|
||||||
[JsonIgnore]
|
|
||||||
private TimelineFactory Timelines { get; }
|
|
||||||
|
|
||||||
private Season(string? past, int turn, string timeline, TimelineFactory factory)
|
|
||||||
{
|
{
|
||||||
this.Past = past;
|
this.Past = past;
|
||||||
this.Turn = turn;
|
this.Turn = turn;
|
||||||
this.Timeline = timeline;
|
this.Timeline = timeline;
|
||||||
this.Timelines = factory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString() => Designation;
|
public override string ToString() => Designation;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a root season at the beginning of time.
|
|
||||||
/// </summary>
|
|
||||||
public static Season MakeRoot()
|
|
||||||
{
|
|
||||||
TimelineFactory factory = new();
|
|
||||||
return new Season(
|
|
||||||
past: null,
|
|
||||||
turn: FIRST_TURN,
|
|
||||||
timeline: factory.NextTimeline(),
|
|
||||||
factory: factory);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a season immediately after this one in the same timeline.
|
|
||||||
/// </summary>
|
|
||||||
public Season MakeNext()
|
|
||||||
=> new(this.Designation, Turn + 1, Timeline, Timelines);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a season immediately after this one in a new timeline.
|
|
||||||
/// </summary>
|
|
||||||
public Season MakeFork()
|
|
||||||
=> new(this.Designation, Turn + 1, Timelines.NextTimeline(), Timelines);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ namespace MultiversalDiplomacy.Model;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A shared counter for handing out new timeline designations.
|
/// A shared counter for handing out new timeline designations.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class TimelineFactory
|
public class TimelineFactory
|
||||||
{
|
{
|
||||||
private static readonly char[] Letters = [
|
private static readonly char[] Letters = [
|
||||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
|
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
|
||||||
|
|
|
@ -63,6 +63,11 @@ public class World
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ReadOnlyDictionary<string, OrderHistory> OrderHistory { get; }
|
public ReadOnlyDictionary<string, OrderHistory> OrderHistory { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The shared timeline number generator.
|
||||||
|
/// </summary>
|
||||||
|
public TimelineFactory Timelines { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Immutable game options.
|
/// Immutable game options.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -77,6 +82,7 @@ public class World
|
||||||
ReadOnlyCollection<Unit> units,
|
ReadOnlyCollection<Unit> units,
|
||||||
ReadOnlyCollection<RetreatingUnit> retreatingUnits,
|
ReadOnlyCollection<RetreatingUnit> retreatingUnits,
|
||||||
ReadOnlyDictionary<string, OrderHistory> orderHistory,
|
ReadOnlyDictionary<string, OrderHistory> orderHistory,
|
||||||
|
TimelineFactory timelines,
|
||||||
Options options)
|
Options options)
|
||||||
{
|
{
|
||||||
this.Map = map;
|
this.Map = map;
|
||||||
|
@ -84,6 +90,7 @@ public class World
|
||||||
this.Units = units;
|
this.Units = units;
|
||||||
this.RetreatingUnits = retreatingUnits;
|
this.RetreatingUnits = retreatingUnits;
|
||||||
this.OrderHistory = orderHistory;
|
this.OrderHistory = orderHistory;
|
||||||
|
this.Timelines = timelines;
|
||||||
this.Options = options;
|
this.Options = options;
|
||||||
|
|
||||||
this.SeasonLookup = new(Seasons.ToDictionary(season => $"{season.Timeline}{season.Turn}"));
|
this.SeasonLookup = new(Seasons.ToDictionary(season => $"{season.Timeline}{season.Turn}"));
|
||||||
|
@ -105,6 +112,7 @@ public class World
|
||||||
units ?? previous.Units,
|
units ?? previous.Units,
|
||||||
retreatingUnits ?? previous.RetreatingUnits,
|
retreatingUnits ?? previous.RetreatingUnits,
|
||||||
orderHistory ?? previous.OrderHistory,
|
orderHistory ?? previous.OrderHistory,
|
||||||
|
previous.Timelines,
|
||||||
options ?? previous.Options)
|
options ?? previous.Options)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -114,12 +122,14 @@ public class World
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static World WithMap(Map map)
|
public static World WithMap(Map map)
|
||||||
{
|
{
|
||||||
|
TimelineFactory timelines = new();
|
||||||
return new World(
|
return new World(
|
||||||
map,
|
map,
|
||||||
new([Season.MakeRoot()]),
|
new([new(past: null, Season.FIRST_TURN, timelines.NextTimeline())]),
|
||||||
new([]),
|
new([]),
|
||||||
new([]),
|
new([]),
|
||||||
new(new Dictionary<string, OrderHistory>()),
|
new(new Dictionary<string, OrderHistory>()),
|
||||||
|
timelines,
|
||||||
new Options());
|
new Options());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,21 +219,12 @@ public class World
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a season immediately after this one in the same timeline.
|
/// Create a continuation of this season if it has no futures, otherwise ceate a fork.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public World ContinueSeason(string season)
|
public Season ContinueOrFork(Season season)
|
||||||
=> Update(seasons: Seasons.Append(SeasonLookup[season].MakeNext()));
|
=> GetFutures(season).Any()
|
||||||
|
? new(season.Designation, season.Turn + 1, Timelines.NextTimeline())
|
||||||
/// <summary>
|
: new(season.Designation, season.Turn + 1, season.Timeline);
|
||||||
/// Create a season immediately after this one in the same timeline.
|
|
||||||
/// </summary>
|
|
||||||
public World ContinueSeason(Season season) => ContinueSeason(season.ToString());
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a season immediately after this one in a new timeline.
|
|
||||||
/// </summary>
|
|
||||||
public World ForkSeason(string season)
|
|
||||||
=> Update(seasons: Seasons.Append(SeasonLookup[season].MakeFork()));
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A standard Diplomacy game setup.
|
/// A standard Diplomacy game setup.
|
||||||
|
|
|
@ -9,30 +9,22 @@ public class SeasonTests
|
||||||
[Test]
|
[Test]
|
||||||
public void TimelineForking()
|
public void TimelineForking()
|
||||||
{
|
{
|
||||||
World world = World
|
World world = World.WithMap(Map.Test);
|
||||||
.WithMap(Map.Test)
|
Season a0 = world.GetSeason("a0");
|
||||||
.ContinueSeason("a0")
|
world = world
|
||||||
.ContinueSeason("a1")
|
.ContinueOrFork(a0, out Season a1)
|
||||||
.ContinueSeason("a2")
|
.ContinueOrFork(a1, out Season a2)
|
||||||
.ForkSeason("a1")
|
.ContinueOrFork(a2, out Season a3)
|
||||||
.ContinueSeason("b2")
|
.ContinueOrFork(a1, out Season b2)
|
||||||
.ForkSeason("a1")
|
.ContinueOrFork(b2, out Season b3)
|
||||||
.ForkSeason("a2");
|
.ContinueOrFork(a1, out Season c2)
|
||||||
|
.ContinueOrFork(a2, out Season d3);
|
||||||
|
|
||||||
Assert.That(
|
Assert.That(
|
||||||
world.Seasons.Select(season => season.ToString()),
|
world.Seasons.Select(season => season.ToString()),
|
||||||
Is.EquivalentTo(new List<string> { "a0", "a1", "a2", "a3", "b2", "b3", "c2", "d3" }),
|
Is.EquivalentTo(new List<string> { "a0", "a1", "a2", "a3", "b2", "b3", "c2", "d3" }),
|
||||||
"Unexpected seasons");
|
"Unexpected seasons");
|
||||||
|
|
||||||
Season a0 = world.GetSeason("a0");
|
|
||||||
Season a1 = world.GetSeason("a1");
|
|
||||||
Season a2 = world.GetSeason("a2");
|
|
||||||
Season a3 = world.GetSeason("a3");
|
|
||||||
Season b2 = world.GetSeason("b2");
|
|
||||||
Season b3 = world.GetSeason("b3");
|
|
||||||
Season c2 = world.GetSeason("c2");
|
|
||||||
Season d3 = world.GetSeason("d3");
|
|
||||||
|
|
||||||
Assert.That(a0.Timeline, Is.EqualTo("a"), "Unexpected trunk timeline");
|
Assert.That(a0.Timeline, Is.EqualTo("a"), "Unexpected trunk timeline");
|
||||||
Assert.That(a1.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(a2.Timeline, Is.EqualTo("a"), "Unexpected trunk timeline");
|
||||||
|
|
|
@ -17,12 +17,10 @@ public class UnitTests
|
||||||
Season a0 = world.RootSeason;
|
Season a0 = world.RootSeason;
|
||||||
Unit u1 = Unit.Build(Mun, a0, pw1, UnitType.Army);
|
Unit u1 = Unit.Build(Mun, a0, pw1, UnitType.Army);
|
||||||
|
|
||||||
world = world.ContinueSeason(a0);
|
world = world.ContinueOrFork(a0, out Season a1);
|
||||||
Season a1 = world.GetSeason("a1");
|
|
||||||
Unit u2 = u1.Next(Boh, a1);
|
Unit u2 = u1.Next(Boh, a1);
|
||||||
|
|
||||||
world = world.ContinueSeason(a1);
|
_ = world.ContinueOrFork(a1, out Season a2);
|
||||||
Season a2 = world.GetSeason("a2");
|
|
||||||
Unit u3 = u2.Next(Tyr, a2);
|
Unit u3 = u2.Next(Tyr, a2);
|
||||||
|
|
||||||
Assert.That(u3.Past, Is.EqualTo(u2), "Missing unit past");
|
Assert.That(u3.Past, Is.EqualTo(u2), "Missing unit past");
|
||||||
|
|
Loading…
Reference in New Issue