Compare commits
7 Commits
b17ce9485a
...
c9bd8c8194
Author | SHA1 | Date |
---|---|---|
Tim Van Baak | c9bd8c8194 | |
Tim Van Baak | 5989970c42 | |
Tim Van Baak | cc2c29980a | |
Tim Van Baak | 984676f587 | |
Tim Van Baak | fd8c725286 | |
Tim Van Baak | 0dec1e1eec | |
Tim Van Baak | 27ffaccd20 |
|
@ -61,25 +61,25 @@ public class MovementDecisions
|
||||||
case MoveOrder move:
|
case MoveOrder move:
|
||||||
AdvanceTimeline.Ensure(
|
AdvanceTimeline.Ensure(
|
||||||
move.Season,
|
move.Season,
|
||||||
() => new(move.Season, world.OrderHistory[move.Season].Orders));
|
() => new(move.Season, world.OrderHistory[move.Season.Designation].Orders));
|
||||||
AdvanceTimeline[move.Season].Orders.Add(move);
|
AdvanceTimeline[move.Season].Orders.Add(move);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SupportHoldOrder supportHold:
|
case SupportHoldOrder supportHold:
|
||||||
AdvanceTimeline.Ensure(
|
AdvanceTimeline.Ensure(
|
||||||
supportHold.Target.Season,
|
supportHold.Target.Season,
|
||||||
() => new(supportHold.Target.Season, world.OrderHistory[supportHold.Target.Season].Orders));
|
() => new(supportHold.Target.Season, world.OrderHistory[supportHold.Target.Season.Designation].Orders));
|
||||||
AdvanceTimeline[supportHold.Target.Season].Orders.Add(supportHold);
|
AdvanceTimeline[supportHold.Target.Season].Orders.Add(supportHold);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SupportMoveOrder supportMove:
|
case SupportMoveOrder supportMove:
|
||||||
AdvanceTimeline.Ensure(
|
AdvanceTimeline.Ensure(
|
||||||
supportMove.Target.Season,
|
supportMove.Target.Season,
|
||||||
() => new(supportMove.Target.Season, world.OrderHistory[supportMove.Target.Season].Orders));
|
() => new(supportMove.Target.Season, world.OrderHistory[supportMove.Target.Season.Designation].Orders));
|
||||||
AdvanceTimeline[supportMove.Target.Season].Orders.Add(supportMove);
|
AdvanceTimeline[supportMove.Target.Season].Orders.Add(supportMove);
|
||||||
AdvanceTimeline.Ensure(
|
AdvanceTimeline.Ensure(
|
||||||
supportMove.Season,
|
supportMove.Season,
|
||||||
() => new(supportMove.Season, world.OrderHistory[supportMove.Season].Orders));
|
() => new(supportMove.Season, world.OrderHistory[supportMove.Season.Designation].Orders));
|
||||||
AdvanceTimeline[supportMove.Season].Orders.Add(supportMove);
|
AdvanceTimeline[supportMove.Season].Orders.Add(supportMove);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,11 +384,11 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record the adjudication results to the season's order history
|
// Record the adjudication results to the season's order history
|
||||||
Dictionary<Season, OrderHistory> newHistory = new();
|
Dictionary<string, OrderHistory> newHistory = [];
|
||||||
foreach (UnitOrder unitOrder in decisions.OfType<IsDislodged>().Select(d => d.Order))
|
foreach (UnitOrder unitOrder in decisions.OfType<IsDislodged>().Select(d => d.Order))
|
||||||
{
|
{
|
||||||
newHistory.Ensure(unitOrder.Unit.Season, () => new());
|
newHistory.Ensure(unitOrder.Unit.Season.Designation, () => new());
|
||||||
OrderHistory history = newHistory[unitOrder.Unit.Season];
|
OrderHistory history = newHistory[unitOrder.Unit.Season.Designation];
|
||||||
// TODO does this add every order to every season??
|
// TODO does this add every order to every season??
|
||||||
history.Orders.Add(unitOrder);
|
history.Orders.Add(unitOrder);
|
||||||
history.IsDislodgedOutcomes[unitOrder.Unit] = dislodges[unitOrder.Unit].Outcome == true;
|
history.IsDislodgedOutcomes[unitOrder.Unit] = dislodges[unitOrder.Unit].Outcome == true;
|
||||||
|
@ -401,7 +399,7 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log the new order history
|
// Log the new order history
|
||||||
foreach ((Season season, OrderHistory history) in newHistory)
|
foreach ((string season, OrderHistory history) in newHistory)
|
||||||
{
|
{
|
||||||
string verb = world.OrderHistory.ContainsKey(season) ? "Updating" : "Adding";
|
string verb = world.OrderHistory.ContainsKey(season) ? "Updating" : "Adding";
|
||||||
logger.Log(1, "{0} history for {1}", verb, season);
|
logger.Log(1, "{0} history for {1}", verb, season);
|
||||||
|
@ -411,7 +409,7 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerable<KeyValuePair<Season, OrderHistory>> updatedHistory = world.OrderHistory
|
IEnumerable<KeyValuePair<string, OrderHistory>> updatedHistory = world.OrderHistory
|
||||||
.Where(kvp => !newHistory.ContainsKey(kvp.Key))
|
.Where(kvp => !newHistory.ContainsKey(kvp.Key))
|
||||||
.Concat(newHistory);
|
.Concat(newHistory);
|
||||||
|
|
||||||
|
@ -496,7 +494,7 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator
|
||||||
IEnumerable<MoveOrder> newIncomingMoves = decision.Orders
|
IEnumerable<MoveOrder> newIncomingMoves = decision.Orders
|
||||||
.OfType<MoveOrder>()
|
.OfType<MoveOrder>()
|
||||||
.Where(order => order.Season == decision.Season
|
.Where(order => order.Season == decision.Season
|
||||||
&& !world.OrderHistory[order.Season].DoesMoveOutcomes.ContainsKey(order));
|
&& !world.OrderHistory[order.Season.Designation].DoesMoveOutcomes.ContainsKey(order));
|
||||||
foreach (MoveOrder moveOrder in newIncomingMoves)
|
foreach (MoveOrder moveOrder in newIncomingMoves)
|
||||||
{
|
{
|
||||||
DoesMove doesMove = decisions.DoesMove[moveOrder];
|
DoesMove doesMove = decisions.DoesMove[moveOrder];
|
||||||
|
@ -513,7 +511,7 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator
|
||||||
// 1. The outcome of a dislodge decision is changed,
|
// 1. The outcome of a dislodge decision is changed,
|
||||||
// 2. The outcome of an intra-timeline move decision is changed, or
|
// 2. The outcome of an intra-timeline move decision is changed, or
|
||||||
// 3. The outcome of an inter-timeline move decision with that season as the destination is changed.
|
// 3. The outcome of an inter-timeline move decision with that season as the destination is changed.
|
||||||
OrderHistory history = world.OrderHistory[decision.Season];
|
OrderHistory history = world.OrderHistory[decision.Season.Designation];
|
||||||
bool anyUnresolved = false;
|
bool anyUnresolved = false;
|
||||||
foreach (UnitOrder order in decision.Orders)
|
foreach (UnitOrder order in decision.Orders)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
using CommandLine;
|
||||||
|
|
||||||
|
namespace MultiversalDiplomacy.CommandLine;
|
||||||
|
|
||||||
|
[Verb("image", HelpText = "Generate an image of a game state.")]
|
||||||
|
public class ImageOptions
|
||||||
|
{
|
||||||
|
[Value(0, HelpText = "Input file describing the game state to visualize, or - to read from stdin.")]
|
||||||
|
public string? InputFile { get; set; }
|
||||||
|
|
||||||
|
public static void Execute(ImageOptions args)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
using CommandLine;
|
||||||
|
|
||||||
|
namespace MultiversalDiplomacy.CommandLine;
|
||||||
|
|
||||||
|
[Verb("repl", HelpText = "Begin an interactive 5dplomacy session.")]
|
||||||
|
public class ReplOptions
|
||||||
|
{
|
||||||
|
[Option('i', "input", HelpText = "Begin the repl session by executing the commands in this file.")]
|
||||||
|
public string? InputFile { get; set; }
|
||||||
|
|
||||||
|
[Option('o', "output", HelpText = "Echo the repl session to this file. Specify a directory to autogenerate a filename.")]
|
||||||
|
public string? OutputFile { get; set; }
|
||||||
|
|
||||||
|
public static void Execute(ReplOptions args)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace MultiversalDiplomacy.Model;
|
namespace MultiversalDiplomacy.Model;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -9,8 +11,12 @@ public class Location
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The province to which this location belongs.
|
/// The province to which this location belongs.
|
||||||
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
public Province Province { get; }
|
public Province Province { get; }
|
||||||
|
|
||||||
|
public string ProvinceName => Province.Name;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The location's full human-readable name.
|
/// The location's full human-readable name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -29,6 +35,7 @@ public class Location
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The locations that border this location.
|
/// The locations that border this location.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
public IEnumerable<Location> Adjacents => this.AdjacentList;
|
public IEnumerable<Location> Adjacents => this.AdjacentList;
|
||||||
private List<Location> AdjacentList { get; set; }
|
private List<Location> AdjacentList { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,13 @@ namespace MultiversalDiplomacy.Model;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Encapsulation of the world map and playable powers constituting a Diplomacy variant.
|
/// Encapsulation of the world map and playable powers constituting a Diplomacy variant.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Map {
|
public class Map
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The map type.
|
||||||
|
/// </summary>
|
||||||
|
public MapType Type { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The game map.
|
/// The game map.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -16,7 +22,7 @@ public class Map {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ReadOnlyCollection<Power> Powers { get; }
|
public ReadOnlyCollection<Power> Powers { get; }
|
||||||
|
|
||||||
private Map(IEnumerable<Province> provinces, IEnumerable<Power> powers)
|
private Map(MapType type, IEnumerable<Province> provinces, IEnumerable<Power> powers)
|
||||||
{
|
{
|
||||||
Provinces = new(provinces.ToList());
|
Provinces = new(provinces.ToList());
|
||||||
Powers = new(powers.ToList());
|
Powers = new(powers.ToList());
|
||||||
|
@ -102,7 +108,7 @@ public class Map {
|
||||||
Power a = new("Alpha");
|
Power a = new("Alpha");
|
||||||
Power b = new("Beta");
|
Power b = new("Beta");
|
||||||
|
|
||||||
return new([lef, cen, rig], [a, b]);
|
return new(MapType.Test, [lef, cen, rig], [a, b]);
|
||||||
});
|
});
|
||||||
|
|
||||||
public static Map Classical => _Classical.Value;
|
public static Map Classical => _Classical.Value;
|
||||||
|
@ -553,7 +559,7 @@ public class Map {
|
||||||
new("Turkey"),
|
new("Turkey"),
|
||||||
];
|
];
|
||||||
|
|
||||||
return new(provinces, powers);
|
return new(MapType.Classical, provinces, powers);
|
||||||
});
|
});
|
||||||
|
|
||||||
#endregion Variants
|
#endregion Variants
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -13,7 +13,7 @@ public class OrderHistory
|
||||||
public Dictionary<MoveOrder, bool> DoesMoveOutcomes;
|
public Dictionary<MoveOrder, bool> DoesMoveOutcomes;
|
||||||
|
|
||||||
public OrderHistory()
|
public OrderHistory()
|
||||||
: this(new(), new(), new())
|
: this([], [], [])
|
||||||
{}
|
{}
|
||||||
|
|
||||||
public OrderHistory(
|
public OrderHistory(
|
||||||
|
|
|
@ -5,10 +5,10 @@ namespace MultiversalDiplomacy.Model;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a state of the map produced by a set of move orders on a previous season.
|
/// Represents a state of the map produced by a set of move orders on a previous season.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Season
|
public class Season(string? past, int turn, string timeline)
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The first turn number.
|
/// The first turn number. This is defined to reduce confusion about whether the first turn is 0 or 1.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int FIRST_TURN = 0;
|
public const int FIRST_TURN = 0;
|
||||||
|
|
||||||
|
@ -17,19 +17,19 @@ public class Season
|
||||||
/// If this season is an alternate timeline root, the past is from the origin timeline.
|
/// If this season is an alternate timeline root, the past is from the origin timeline.
|
||||||
/// The initial season does not have a past.
|
/// The initial season does not have a past.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Past { get; }
|
public string? Past { get; } = past;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current turn, beginning at 0. Each season (spring and fall) is one turn.
|
/// The current turn, beginning at 0. Each season (spring and fall) is one turn.
|
||||||
/// Phases that only occur after the fall phase occur when Turn % 2 == 1.
|
/// Phases that only occur after the fall phase occur when Turn % 2 == 1.
|
||||||
/// The current year is (Turn / 2) + 1901.
|
/// The current year is (Turn / 2) + 1901.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Turn { get; }
|
public int Turn { get; } = turn;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The timeline to which this season belongs.
|
/// The timeline to which this season belongs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Timeline { get; }
|
public string Timeline { get; } = timeline;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The multiversal designation of this season.
|
/// The multiversal designation of this season.
|
||||||
|
@ -37,50 +37,5 @@ public class Season
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public string Designation => $"{this.Timeline}{this.Turn}";
|
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);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The shared timeline number generator.
|
|
||||||
/// </summary>
|
|
||||||
[JsonIgnore]
|
|
||||||
private TimelineFactory Timelines { get; }
|
|
||||||
|
|
||||||
private Season(Season? past, int turn, string timeline, TimelineFactory factory)
|
|
||||||
{
|
|
||||||
this.Past = past?.ToString();
|
|
||||||
this.Turn = turn;
|
|
||||||
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 TimelineFactory();
|
|
||||||
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, Turn + 1, Timeline, Timelines);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a season immediately after this one in a new timeline.
|
|
||||||
/// </summary>
|
|
||||||
public Season MakeFork()
|
|
||||||
=> new(this, 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',
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace MultiversalDiplomacy.Model;
|
namespace MultiversalDiplomacy.Model;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -18,6 +20,7 @@ public class Unit
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The province where the unit is.
|
/// The province where the unit is.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
public Province Province => this.Location.Province;
|
public Province Province => this.Location.Province;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -59,11 +62,11 @@ public class Unit
|
||||||
/// method after accepting a build order.
|
/// method after accepting a build order.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Unit Build(Location location, Season season, Power power, UnitType type)
|
public static Unit Build(Location location, Season season, Power power, UnitType type)
|
||||||
=> new Unit(past: null, location, season, power, type);
|
=> new(past: null, location, season, power, type);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Advance this unit's timeline to a new location and season.
|
/// Advance this unit's timeline to a new location and season.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Unit Next(Location location, Season season)
|
public Unit Next(Location location, Season season)
|
||||||
=> new Unit(past: this, location, season, this.Power, this.Type);
|
=> new(past: this, location, season, this.Power, this.Type);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using MultiversalDiplomacy.Orders;
|
|
||||||
|
|
||||||
namespace MultiversalDiplomacy.Model;
|
namespace MultiversalDiplomacy.Model;
|
||||||
|
|
||||||
|
@ -12,16 +11,24 @@ public class World
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The map variant of the game.
|
/// The map variant of the game.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly Map Map;
|
[JsonIgnore]
|
||||||
|
public Map Map { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The map variant of the game.
|
||||||
|
/// </summary>
|
||||||
|
public MapType MapType => this.Map.Type;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The game map.
|
/// The game map.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
public ReadOnlyCollection<Province> Provinces => this.Map.Provinces;
|
public ReadOnlyCollection<Province> Provinces => this.Map.Provinces;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The game powers.
|
/// The game powers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
public ReadOnlyCollection<Power> Powers => this.Map.Powers;
|
public ReadOnlyCollection<Power> Powers => this.Map.Powers;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -32,11 +39,13 @@ public class World
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lookup for seasons by designation.
|
/// Lookup for seasons by designation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
public ReadOnlyDictionary<string, Season> SeasonLookup { get; }
|
public ReadOnlyDictionary<string, Season> SeasonLookup { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The first season of the game.
|
/// The first season of the game.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
public Season RootSeason => GetSeason("a0");
|
public Season RootSeason => GetSeason("a0");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -52,7 +61,12 @@ public class World
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Orders given to units in each season.
|
/// Orders given to units in each season.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ReadOnlyDictionary<Season, 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.
|
||||||
|
@ -67,7 +81,8 @@ public class World
|
||||||
ReadOnlyCollection<Season> seasons,
|
ReadOnlyCollection<Season> seasons,
|
||||||
ReadOnlyCollection<Unit> units,
|
ReadOnlyCollection<Unit> units,
|
||||||
ReadOnlyCollection<RetreatingUnit> retreatingUnits,
|
ReadOnlyCollection<RetreatingUnit> retreatingUnits,
|
||||||
ReadOnlyDictionary<Season, OrderHistory> orderHistory,
|
ReadOnlyDictionary<string, OrderHistory> orderHistory,
|
||||||
|
TimelineFactory timelines,
|
||||||
Options options)
|
Options options)
|
||||||
{
|
{
|
||||||
this.Map = map;
|
this.Map = map;
|
||||||
|
@ -75,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}"));
|
||||||
|
@ -88,7 +104,7 @@ public class World
|
||||||
ReadOnlyCollection<Season>? seasons = null,
|
ReadOnlyCollection<Season>? seasons = null,
|
||||||
ReadOnlyCollection<Unit>? units = null,
|
ReadOnlyCollection<Unit>? units = null,
|
||||||
ReadOnlyCollection<RetreatingUnit>? retreatingUnits = null,
|
ReadOnlyCollection<RetreatingUnit>? retreatingUnits = null,
|
||||||
ReadOnlyDictionary<Season, OrderHistory>? orderHistory = null,
|
ReadOnlyDictionary<string, OrderHistory>? orderHistory = null,
|
||||||
Options? options = null)
|
Options? options = null)
|
||||||
: this(
|
: this(
|
||||||
previous.Map,
|
previous.Map,
|
||||||
|
@ -96,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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -105,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<Season, OrderHistory>()),
|
new(new Dictionary<string, OrderHistory>()),
|
||||||
|
timelines,
|
||||||
new Options());
|
new Options());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +143,7 @@ public class World
|
||||||
IEnumerable<Season>? seasons = null,
|
IEnumerable<Season>? seasons = null,
|
||||||
IEnumerable<Unit>? units = null,
|
IEnumerable<Unit>? units = null,
|
||||||
IEnumerable<RetreatingUnit>? retreats = null,
|
IEnumerable<RetreatingUnit>? retreats = null,
|
||||||
IEnumerable<KeyValuePair<Season, OrderHistory>>? orders = null)
|
IEnumerable<KeyValuePair<string, OrderHistory>>? orders = null)
|
||||||
=> new(
|
=> new(
|
||||||
previous: this,
|
previous: this,
|
||||||
seasons: seasons == null
|
seasons: seasons == null
|
||||||
|
@ -200,28 +219,17 @@ 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static World Standard => World
|
public static World Standard => WithStandardMap().AddStandardUnits();
|
||||||
.WithStandardMap()
|
|
||||||
.AddStandardUnits();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get a season by coordinate. Throws if the season is not found.
|
/// Get a season by coordinate. Throws if the season is not found.
|
||||||
|
@ -288,11 +296,10 @@ public class World
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a unit in a province. Throws if there are duplicate units.
|
/// Returns a unit in a province. Throws if there are duplicate units.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Unit GetUnitAt(string provinceName, (string timeline, int turn)? seasonCoord = null)
|
public Unit GetUnitAt(string provinceName, Season? season = null)
|
||||||
{
|
{
|
||||||
Province province = Map.GetProvince(provinceName);
|
Province province = Map.GetProvince(provinceName);
|
||||||
seasonCoord ??= (this.RootSeason.Timeline, this.RootSeason.Turn);
|
season ??= RootSeason;
|
||||||
Season season = GetSeason(seasonCoord.Value.timeline, seasonCoord.Value.turn);
|
|
||||||
Unit? foundUnit = this.Units.SingleOrDefault(
|
Unit? foundUnit = this.Units.SingleOrDefault(
|
||||||
u => u!.Province == province && u.Season == season,
|
u => u!.Province == province && u.Season == season,
|
||||||
null)
|
null)
|
||||||
|
|
|
@ -9,9 +9,15 @@ internal class Program
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
var parser = Parser.Default;
|
var parser = Parser.Default;
|
||||||
var parseResult = parser.ParseArguments<AdjudicateOptions>(args);
|
var parseResult = parser.ParseArguments(
|
||||||
|
args,
|
||||||
|
typeof(AdjudicateOptions),
|
||||||
|
typeof(ImageOptions),
|
||||||
|
typeof(ReplOptions));
|
||||||
|
|
||||||
parseResult
|
parseResult
|
||||||
.WithParsed(AdjudicateOptions.Execute);
|
.WithParsed<AdjudicateOptions>(AdjudicateOptions.Execute)
|
||||||
|
.WithParsed<ImageOptions>(ImageOptions.Execute)
|
||||||
|
.WithParsed<ReplOptions>(ReplOptions.Execute);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -43,14 +43,14 @@ public class TimeTravelTest
|
||||||
|
|
||||||
// Confirm that there is a unit in Tyr b1 originating from Mun a1
|
// Confirm that there is a unit in Tyr b1 originating from Mun a1
|
||||||
Season fork = world.GetSeason("b1");
|
Season fork = world.GetSeason("b1");
|
||||||
Unit originalUnit = world.GetUnitAt("Mun", s0.Coord);
|
Unit originalUnit = world.GetUnitAt("Mun", s0);
|
||||||
Unit aMun0 = world.GetUnitAt("Mun", s1.Coord);
|
Unit aMun0 = world.GetUnitAt("Mun", s1);
|
||||||
Unit aTyr = world.GetUnitAt("Tyr", fork.Coord);
|
Unit aTyr = world.GetUnitAt("Tyr", fork);
|
||||||
Assert.That(aTyr.Past, Is.EqualTo(mun1.Order.Unit));
|
Assert.That(aTyr.Past, Is.EqualTo(mun1.Order.Unit));
|
||||||
Assert.That(aTyr.Past?.Past, Is.EqualTo(mun0.Order.Unit));
|
Assert.That(aTyr.Past?.Past, Is.EqualTo(mun0.Order.Unit));
|
||||||
|
|
||||||
// Confirm that there is a unit in Mun b1 originating from Mun a0
|
// Confirm that there is a unit in Mun b1 originating from Mun a0
|
||||||
Unit aMun1 = world.GetUnitAt("Mun", fork.Coord);
|
Unit aMun1 = world.GetUnitAt("Mun", fork);
|
||||||
Assert.That(aMun1.Past, Is.EqualTo(originalUnit));
|
Assert.That(aMun1.Past, Is.EqualTo(originalUnit));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ public class TimeTravelTest
|
||||||
// Confirm that an alternate future is created.
|
// Confirm that an alternate future is created.
|
||||||
World world = setup.UpdateWorld();
|
World world = setup.UpdateWorld();
|
||||||
Season fork = world.GetSeason("b1");
|
Season fork = world.GetSeason("b1");
|
||||||
Unit tyr1 = world.GetUnitAt("Tyr", fork.Coord);
|
Unit tyr1 = world.GetUnitAt("Tyr", fork);
|
||||||
Assert.That(
|
Assert.That(
|
||||||
tyr1.Past,
|
tyr1.Past,
|
||||||
Is.EqualTo(mun0.Order.Unit),
|
Is.EqualTo(mun0.Order.Unit),
|
||||||
|
|
|
@ -206,7 +206,7 @@ public class MovementAdjudicatorTest
|
||||||
Assert.That(s2.Turn, Is.EqualTo(s1.Turn + 1));
|
Assert.That(s2.Turn, Is.EqualTo(s1.Turn + 1));
|
||||||
|
|
||||||
// Confirm the unit was created in the future
|
// Confirm the unit was created in the future
|
||||||
Unit u2 = updated.GetUnitAt("Mun", s2.Coord);
|
Unit u2 = updated.GetUnitAt("Mun", s2);
|
||||||
Assert.That(updated.Units.Count, Is.EqualTo(2));
|
Assert.That(updated.Units.Count, Is.EqualTo(2));
|
||||||
Assert.That(u2, Is.Not.EqualTo(mun1.Order.Unit));
|
Assert.That(u2, Is.Not.EqualTo(mun1.Order.Unit));
|
||||||
Assert.That(u2.Past, Is.EqualTo(mun1.Order.Unit));
|
Assert.That(u2.Past, Is.EqualTo(mun1.Order.Unit));
|
||||||
|
@ -228,7 +228,7 @@ public class MovementAdjudicatorTest
|
||||||
// Update the world again
|
// Update the world again
|
||||||
updated = setup.UpdateWorld();
|
updated = setup.UpdateWorld();
|
||||||
Season s3 = updated.GetSeason(s2.Timeline, s2.Turn + 1);
|
Season s3 = updated.GetSeason(s2.Timeline, s2.Turn + 1);
|
||||||
Unit u3 = updated.GetUnitAt("Mun", s3.Coord);
|
Unit u3 = updated.GetUnitAt("Mun", s3);
|
||||||
Assert.That(u3.Past, Is.EqualTo(mun2.Order.Unit));
|
Assert.That(u3.Past, Is.EqualTo(mun2.Order.Unit));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,7 +256,7 @@ public class MovementAdjudicatorTest
|
||||||
Assert.That(s2.Turn, Is.EqualTo(s1.Turn + 1));
|
Assert.That(s2.Turn, Is.EqualTo(s1.Turn + 1));
|
||||||
|
|
||||||
// Confirm the unit was created in the future
|
// Confirm the unit was created in the future
|
||||||
Unit u2 = updated.GetUnitAt("Tyr", s2.Coord);
|
Unit u2 = updated.GetUnitAt("Tyr", s2);
|
||||||
Assert.That(updated.Units.Count, Is.EqualTo(2));
|
Assert.That(updated.Units.Count, Is.EqualTo(2));
|
||||||
Assert.That(u2, Is.Not.EqualTo(mun1.Order.Unit));
|
Assert.That(u2, Is.Not.EqualTo(mun1.Order.Unit));
|
||||||
Assert.That(u2.Past, Is.EqualTo(mun1.Order.Unit));
|
Assert.That(u2.Past, Is.EqualTo(mun1.Order.Unit));
|
||||||
|
@ -278,7 +278,7 @@ public class MovementAdjudicatorTest
|
||||||
// Update the world again
|
// Update the world again
|
||||||
updated = setup.UpdateWorld();
|
updated = setup.UpdateWorld();
|
||||||
Season s3 = updated.GetSeason(s2.Timeline, s2.Turn + 1);
|
Season s3 = updated.GetSeason(s2.Timeline, s2.Turn + 1);
|
||||||
Unit u3 = updated.GetUnitAt("Mun", s3.Coord);
|
Unit u3 = updated.GetUnitAt("Mun", s3);
|
||||||
Assert.That(u3.Past, Is.EqualTo(u2));
|
Assert.That(u3.Past, Is.EqualTo(u2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
using MultiversalDiplomacy.Adjudicate;
|
||||||
|
using MultiversalDiplomacy.Model;
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace MultiversalDiplomacyTests;
|
||||||
|
|
||||||
|
public class SerializationTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void SerializeRoundTrip_NewGame()
|
||||||
|
{
|
||||||
|
JsonSerializerOptions options = new() {
|
||||||
|
WriteIndented = true,
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||||
|
};
|
||||||
|
|
||||||
|
World world = World.WithStandardMap();
|
||||||
|
string serialized = JsonSerializer.Serialize(world, options);
|
||||||
|
Console.WriteLine(serialized);
|
||||||
|
World? deserialized = JsonSerializer.Deserialize<World>(serialized, options);
|
||||||
|
Assert.That(deserialized, Is.Not.Null, "Failed to deserialize");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SerializeRoundTrip_MDATC_3_A_2()
|
||||||
|
{
|
||||||
|
// Set up MDATC 3.A.2
|
||||||
|
TestCaseBuilder setup = new(World.WithStandardMap(), MovementPhaseAdjudicator.Instance);
|
||||||
|
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();
|
||||||
|
|
||||||
|
// Serialize the world
|
||||||
|
JsonSerializerOptions options = new() {
|
||||||
|
WriteIndented = true,
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||||
|
};
|
||||||
|
JsonElement serialized = JsonSerializer.SerializeToDocument(setup.World, options).RootElement;
|
||||||
|
|
||||||
|
Assert.That(
|
||||||
|
serialized.EnumerateObject().Select(prop => prop.Name),
|
||||||
|
Is.EquivalentTo(new List<string> {
|
||||||
|
"mapType",
|
||||||
|
"seasons",
|
||||||
|
"units",
|
||||||
|
"retreatingUnits",
|
||||||
|
"orderHistory",
|
||||||
|
"options",
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Deserialize the world
|
||||||
|
World reserialized = JsonSerializer.Deserialize<World>(serialized)
|
||||||
|
?? throw new AssertionException("Failed to reserialize world");
|
||||||
|
|
||||||
|
// Resume the test case
|
||||||
|
setup = new(reserialized, MovementPhaseAdjudicator.Instance);
|
||||||
|
setup[("a", 1)]
|
||||||
|
["Germany"]
|
||||||
|
.Army("Mun").Supports.Army("Mun", season: reserialized.GetSeason("a0")).MoveTo("Tyr").GetReference(out var mun1)
|
||||||
|
["Austria"]
|
||||||
|
.Army("Tyr").Holds();
|
||||||
|
|
||||||
|
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.GetSeason("b1");
|
||||||
|
Unit tyr1 = world.GetUnitAt("Tyr", fork);
|
||||||
|
Assert.That(
|
||||||
|
tyr1.Past,
|
||||||
|
Is.EqualTo(mun0.Order.Unit),
|
||||||
|
"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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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