Simplify world updates and expose root season
This commit is contained in:
parent
aa9c9c548b
commit
d4e68844c6
|
@ -357,10 +357,10 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator
|
||||||
|
|
||||||
// TODO provide more structured information about order outcomes
|
// TODO provide more structured information about order outcomes
|
||||||
|
|
||||||
World updated = world
|
World updated = world.Update(
|
||||||
.WithSeasons(world.Seasons.Concat(createdFutures.Values))
|
seasons: world.Seasons.Concat(createdFutures.Values),
|
||||||
.WithUnits(world.Units.Concat(createdUnits))
|
units: world.Units.Concat(createdUnits),
|
||||||
.WithRetreats(retreats);
|
retreats: retreats);
|
||||||
|
|
||||||
return updated;
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,11 @@ public class World
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ReadOnlyCollection<Season> Seasons { get; }
|
public ReadOnlyCollection<Season> Seasons { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The first season of the game.
|
||||||
|
/// </summary>
|
||||||
|
public Season RootSeason { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All units in the multiverse.
|
/// All units in the multiverse.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -37,10 +42,14 @@ public class World
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Options Options { get; }
|
public Options Options { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new World, providing all state data.
|
||||||
|
/// </summary>
|
||||||
private World(
|
private World(
|
||||||
ReadOnlyCollection<Province> provinces,
|
ReadOnlyCollection<Province> provinces,
|
||||||
ReadOnlyCollection<Power> powers,
|
ReadOnlyCollection<Power> powers,
|
||||||
ReadOnlyCollection<Season> seasons,
|
ReadOnlyCollection<Season> seasons,
|
||||||
|
Season rootSeason,
|
||||||
ReadOnlyCollection<Unit> units,
|
ReadOnlyCollection<Unit> units,
|
||||||
ReadOnlyCollection<RetreatingUnit> retreatingUnits,
|
ReadOnlyCollection<RetreatingUnit> retreatingUnits,
|
||||||
Options options)
|
Options options)
|
||||||
|
@ -48,22 +57,49 @@ public class World
|
||||||
this.Provinces = provinces;
|
this.Provinces = provinces;
|
||||||
this.Powers = powers;
|
this.Powers = powers;
|
||||||
this.Seasons = seasons;
|
this.Seasons = seasons;
|
||||||
|
this.RootSeason = rootSeason;
|
||||||
this.Units = units;
|
this.Units = units;
|
||||||
this.RetreatingUnits = retreatingUnits;
|
this.RetreatingUnits = retreatingUnits;
|
||||||
this.Options = options;
|
this.Options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new world with specified provinces and powers.
|
/// Create a new World from a previous one, replacing some state data.
|
||||||
|
/// </summary>
|
||||||
|
private World(
|
||||||
|
World previous,
|
||||||
|
ReadOnlyCollection<Province>? provinces = null,
|
||||||
|
ReadOnlyCollection<Power>? powers = null,
|
||||||
|
ReadOnlyCollection<Season>? seasons = null,
|
||||||
|
ReadOnlyCollection<Unit>? units = null,
|
||||||
|
ReadOnlyCollection<RetreatingUnit>? retreatingUnits = null,
|
||||||
|
Options? options = null)
|
||||||
|
: this(
|
||||||
|
provinces ?? previous.Provinces,
|
||||||
|
powers ?? previous.Powers,
|
||||||
|
seasons ?? previous.Seasons,
|
||||||
|
previous.RootSeason, // Can't change the root season
|
||||||
|
units ?? previous.Units,
|
||||||
|
retreatingUnits ?? previous.RetreatingUnits,
|
||||||
|
options ?? previous.Options)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new world with specified provinces and powers and an initial season.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static World WithMap(IEnumerable<Province> provinces, IEnumerable<Power> powers)
|
public static World WithMap(IEnumerable<Province> provinces, IEnumerable<Power> powers)
|
||||||
=> new World(
|
{
|
||||||
|
Season root = Season.MakeRoot();
|
||||||
|
return new World(
|
||||||
new(provinces.ToList()),
|
new(provinces.ToList()),
|
||||||
new(powers.ToList()),
|
new(powers.ToList()),
|
||||||
new(new List<Season>()),
|
new(new List<Season> { root }),
|
||||||
|
root,
|
||||||
new(new List<Unit>()),
|
new(new List<Unit>()),
|
||||||
new(new List<RetreatingUnit>()),
|
new(new List<RetreatingUnit>()),
|
||||||
new Options());
|
new Options());
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new world with the standard Diplomacy provinces and powers.
|
/// Create a new world with the standard Diplomacy provinces and powers.
|
||||||
|
@ -71,42 +107,22 @@ public class World
|
||||||
public static World WithStandardMap()
|
public static World WithStandardMap()
|
||||||
=> WithMap(StandardProvinces, StandardPowers);
|
=> WithMap(StandardProvinces, StandardPowers);
|
||||||
|
|
||||||
/// <summary>
|
public World Update(
|
||||||
/// Create a new world with new seasons.
|
IEnumerable<Season>? seasons = null,
|
||||||
/// </summary>
|
IEnumerable<Unit>? units = null,
|
||||||
public World WithSeasons(IEnumerable<Season> seasons)
|
IEnumerable<RetreatingUnit>? retreats = null)
|
||||||
=> new World(
|
=> new World(
|
||||||
this.Provinces,
|
previous: this,
|
||||||
this.Powers,
|
seasons: seasons == null ? this.Seasons : new(seasons.ToList()),
|
||||||
new(seasons.ToList()),
|
units: units == null ? this.Units : new(units.ToList()),
|
||||||
this.Units,
|
retreatingUnits: retreats == null ? this.RetreatingUnits : new(retreats.ToList()));
|
||||||
this.RetreatingUnits,
|
|
||||||
this.Options);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new world with an initial season.
|
|
||||||
/// </summary>
|
|
||||||
public World WithInitialSeason()
|
|
||||||
=> WithSeasons(new List<Season> { Season.MakeRoot() });
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new world with new units.
|
|
||||||
/// </summary>
|
|
||||||
public World WithUnits(IEnumerable<Unit> units)
|
|
||||||
=> new World(
|
|
||||||
this.Provinces,
|
|
||||||
this.Powers,
|
|
||||||
this.Seasons,
|
|
||||||
new(units.ToList()),
|
|
||||||
this.RetreatingUnits,
|
|
||||||
this.Options);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new world with new units created from unit specs. Units specs are in the format
|
/// Create a new world with new units created from unit specs. Units specs are in the format
|
||||||
/// "<power> <A/F> <province> [<coast>]". If the province or coast name has a space in it, the
|
/// "<power> <A/F> <province> [<coast>]". If the province or coast name has a space in it, the
|
||||||
/// abbreviation should be used.
|
/// abbreviation should be used. Unit specs always describe units in the root season.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public World WithUnits(params string[] unitSpecs)
|
public World AddUnits(params string[] unitSpecs)
|
||||||
{
|
{
|
||||||
IEnumerable<Unit> units = unitSpecs.Select(spec =>
|
IEnumerable<Unit> units = unitSpecs.Select(spec =>
|
||||||
{
|
{
|
||||||
|
@ -123,18 +139,18 @@ public class World
|
||||||
: splits.Length == 3
|
: splits.Length == 3
|
||||||
? this.GetWater(splits[2])
|
? this.GetWater(splits[2])
|
||||||
: this.GetWater(splits[2], splits[3]);
|
: this.GetWater(splits[2], splits[3]);
|
||||||
Unit unit = Unit.Build(location, this.Seasons.First(), power, type);
|
Unit unit = Unit.Build(location, this.RootSeason, power, type);
|
||||||
return unit;
|
return unit;
|
||||||
});
|
});
|
||||||
return this.WithUnits(units);
|
return this.Update(units: units);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new world with standard Diplomacy initial unit placements.
|
/// Create a new world with standard Diplomacy initial unit placements.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public World WithStandardUnits()
|
public World AddStandardUnits()
|
||||||
{
|
{
|
||||||
return this.WithUnits(
|
return this.AddUnits(
|
||||||
"Austria A Bud",
|
"Austria A Bud",
|
||||||
"Austria A Vir",
|
"Austria A Vir",
|
||||||
"Austria F Tri",
|
"Austria F Tri",
|
||||||
|
@ -160,22 +176,12 @@ public class World
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public World WithRetreats(IEnumerable<RetreatingUnit> retreatingUnits)
|
|
||||||
=> new World(
|
|
||||||
this.Provinces,
|
|
||||||
this.Powers,
|
|
||||||
this.Seasons,
|
|
||||||
this.Units,
|
|
||||||
new(retreatingUnits.ToList()),
|
|
||||||
this.Options);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A standard Diplomacy game setup.
|
/// A standard Diplomacy game setup.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static World Standard => World
|
public static World Standard => World
|
||||||
.WithStandardMap()
|
.WithStandardMap()
|
||||||
.WithInitialSeason()
|
.AddStandardUnits();
|
||||||
.WithStandardUnits();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get a province by name. Throws if the province is not found.
|
/// Get a province by name. Throws if the province is not found.
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace MultiversalDiplomacyTests;
|
||||||
|
|
||||||
public class DATC_A
|
public class DATC_A
|
||||||
{
|
{
|
||||||
private World StandardEmpty { get; } = World.WithStandardMap().WithInitialSeason();
|
private World StandardEmpty { get; } = World.WithStandardMap();
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void DATC_6_A_1_MoveToAnAreaThatIsNotANeighbor()
|
public void DATC_6_A_1_MoveToAnAreaThatIsNotANeighbor()
|
||||||
|
|
|
@ -11,72 +11,72 @@ public class MovementAdjudicatorTest
|
||||||
[Test]
|
[Test]
|
||||||
public void Validation_ValidHold()
|
public void Validation_ValidHold()
|
||||||
{
|
{
|
||||||
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap().WithInitialSeason());
|
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap());
|
||||||
setup["Germany"]
|
setup["Germany"]
|
||||||
.Army("Mun").Holds().GetReference(out var order);
|
.Army("Mun").Holds().GetReference(out var order);
|
||||||
|
|
||||||
setup.ValidateOrders(MovementPhaseAdjudicator.Instance);
|
setup.ValidateOrders(MovementPhaseAdjudicator.Instance);
|
||||||
|
|
||||||
Assert.That(order.Validation, Is.Valid, "Unexpected validation result");
|
Assert.That(order, Is.Valid, "Unexpected validation result");
|
||||||
Assert.That(order.Replacement, Is.Null, "Unexpected order replacement");
|
Assert.That(order.Replacement, Is.Null, "Unexpected order replacement");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Validation_ValidMove()
|
public void Validation_ValidMove()
|
||||||
{
|
{
|
||||||
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap().WithInitialSeason());
|
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap());
|
||||||
setup["Germany"]
|
setup["Germany"]
|
||||||
.Army("Mun").MovesTo("Tyr").GetReference(out var order);
|
.Army("Mun").MovesTo("Tyr").GetReference(out var order);
|
||||||
|
|
||||||
setup.ValidateOrders(MovementPhaseAdjudicator.Instance);
|
setup.ValidateOrders(MovementPhaseAdjudicator.Instance);
|
||||||
|
|
||||||
Assert.That(order.Validation, Is.Valid, "Unexpected validation result");
|
Assert.That(order, Is.Valid, "Unexpected validation result");
|
||||||
Assert.That(order.Replacement, Is.Null, "Unexpected order replacement");
|
Assert.That(order.Replacement, Is.Null, "Unexpected order replacement");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Validation_ValidConvoy()
|
public void Validation_ValidConvoy()
|
||||||
{
|
{
|
||||||
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap().WithInitialSeason());
|
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap());
|
||||||
setup["Germany"]
|
setup["Germany"]
|
||||||
.Fleet("Nth").Convoys.Army("Hol").To("Lon").GetReference(out var order);
|
.Fleet("Nth").Convoys.Army("Hol").To("Lon").GetReference(out var order);
|
||||||
|
|
||||||
setup.ValidateOrders(MovementPhaseAdjudicator.Instance);
|
setup.ValidateOrders(MovementPhaseAdjudicator.Instance);
|
||||||
|
|
||||||
Assert.That(order.Validation, Is.Valid, "Unexpected validation result");
|
Assert.That(order, Is.Valid, "Unexpected validation result");
|
||||||
Assert.That(order.Replacement, Is.Null, "Unexpected order replacement");
|
Assert.That(order.Replacement, Is.Null, "Unexpected order replacement");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Validation_ValidSupportHold()
|
public void Validation_ValidSupportHold()
|
||||||
{
|
{
|
||||||
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap().WithInitialSeason());
|
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap());
|
||||||
setup["Germany"]
|
setup["Germany"]
|
||||||
.Army("Mun").Supports.Army("Kie").Hold().GetReference(out var order);
|
.Army("Mun").Supports.Army("Kie").Hold().GetReference(out var order);
|
||||||
|
|
||||||
setup.ValidateOrders(MovementPhaseAdjudicator.Instance);
|
setup.ValidateOrders(MovementPhaseAdjudicator.Instance);
|
||||||
|
|
||||||
Assert.That(order.Validation, Is.Valid, "Unexpected validation result");
|
Assert.That(order, Is.Valid, "Unexpected validation result");
|
||||||
Assert.That(order.Replacement, Is.Null, "Unexpected order replacement");
|
Assert.That(order.Replacement, Is.Null, "Unexpected order replacement");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Validation_ValidSupportMove()
|
public void Validation_ValidSupportMove()
|
||||||
{
|
{
|
||||||
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap().WithInitialSeason());
|
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap());
|
||||||
setup["Germany"]
|
setup["Germany"]
|
||||||
.Army("Mun").Supports.Army("Kie").MoveTo("Ber").GetReference(out var order);
|
.Army("Mun").Supports.Army("Kie").MoveTo("Ber").GetReference(out var order);
|
||||||
|
|
||||||
setup.ValidateOrders(MovementPhaseAdjudicator.Instance);
|
setup.ValidateOrders(MovementPhaseAdjudicator.Instance);
|
||||||
|
|
||||||
Assert.That(order.Validation, Is.Valid, "Unexpected validation result");
|
Assert.That(order, Is.Valid, "Unexpected validation result");
|
||||||
Assert.That(order.Replacement, Is.Null, "Unexpected order replacement");
|
Assert.That(order.Replacement, Is.Null, "Unexpected order replacement");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Adjudication_Hold()
|
public void Adjudication_Hold()
|
||||||
{
|
{
|
||||||
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap().WithInitialSeason());
|
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap());
|
||||||
setup["Germany"]
|
setup["Germany"]
|
||||||
.Army("Mun").Holds().GetReference(out var order);
|
.Army("Mun").Holds().GetReference(out var order);
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ public class MovementAdjudicatorTest
|
||||||
[Test]
|
[Test]
|
||||||
public void Adjudication_Move()
|
public void Adjudication_Move()
|
||||||
{
|
{
|
||||||
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap().WithInitialSeason());
|
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap());
|
||||||
setup["Germany"]
|
setup["Germany"]
|
||||||
.Army("Mun").MovesTo("Tyr").GetReference(out var order);
|
.Army("Mun").MovesTo("Tyr").GetReference(out var order);
|
||||||
|
|
||||||
|
@ -122,7 +122,7 @@ public class MovementAdjudicatorTest
|
||||||
[Test]
|
[Test]
|
||||||
public void Adjudication_Support()
|
public void Adjudication_Support()
|
||||||
{
|
{
|
||||||
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap().WithInitialSeason());
|
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap());
|
||||||
setup["Germany"]
|
setup["Germany"]
|
||||||
.Army("Mun").MovesTo("Tyr").GetReference(out var move)
|
.Army("Mun").MovesTo("Tyr").GetReference(out var move)
|
||||||
.Army("Boh").Supports.Army("Mun").MoveTo("Tyr").GetReference(out var support);
|
.Army("Boh").Supports.Army("Mun").MoveTo("Tyr").GetReference(out var support);
|
||||||
|
|
|
@ -217,7 +217,7 @@ public class TestCaseBuilder
|
||||||
|
|
||||||
// Not found
|
// Not found
|
||||||
Unit newUnit = Unit.Build(location, season, power, type);
|
Unit newUnit = Unit.Build(location, season, power, type);
|
||||||
this.World = this.World.WithUnits(this.World.Units.Append(newUnit));
|
this.World = this.World.Update(units: this.World.Units.Append(newUnit));
|
||||||
return newUnit;
|
return newUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ class TestCaseBuilderTest
|
||||||
[Test]
|
[Test]
|
||||||
public void BuilderCreatesUnits()
|
public void BuilderCreatesUnits()
|
||||||
{
|
{
|
||||||
TestCaseBuilder setup = new(World.WithStandardMap().WithInitialSeason());
|
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.World.Units, Is.Empty, "Expected no units to be created yet");
|
||||||
|
@ -50,7 +50,7 @@ class TestCaseBuilderTest
|
||||||
[Test]
|
[Test]
|
||||||
public void BuilderCreatesOrders()
|
public void BuilderCreatesOrders()
|
||||||
{
|
{
|
||||||
TestCaseBuilder setup = new(World.WithStandardMap().WithInitialSeason());
|
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.World.Units, Is.Empty, "Expected no units to be created yet");
|
||||||
|
@ -120,7 +120,7 @@ class TestCaseBuilderTest
|
||||||
{
|
{
|
||||||
IPhaseAdjudicator rubberStamp = new TestAdjudicator(validate: TestAdjudicator.RubberStamp);
|
IPhaseAdjudicator rubberStamp = new TestAdjudicator(validate: TestAdjudicator.RubberStamp);
|
||||||
|
|
||||||
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap().WithInitialSeason());
|
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap());
|
||||||
setup["Germany"]
|
setup["Germany"]
|
||||||
.Army("Mun").Holds().GetReference(out var orderMun);
|
.Army("Mun").Holds().GetReference(out var orderMun);
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@ class TestCaseBuilderTest
|
||||||
validate: TestAdjudicator.RubberStamp,
|
validate: TestAdjudicator.RubberStamp,
|
||||||
adjudicate: TestAdjudicator.NoMoves);
|
adjudicate: TestAdjudicator.NoMoves);
|
||||||
|
|
||||||
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap().WithInitialSeason());
|
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap());
|
||||||
setup["Germany"]
|
setup["Germany"]
|
||||||
.Army("Mun").Holds().GetReference(out var orderMun);
|
.Army("Mun").Holds().GetReference(out var orderMun);
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ public class UnitTests
|
||||||
[Test]
|
[Test]
|
||||||
public void MovementTest()
|
public void MovementTest()
|
||||||
{
|
{
|
||||||
World world = World.WithStandardMap().WithInitialSeason();
|
World world = World.WithStandardMap();
|
||||||
Location Mun = world.GetLand("Mun"),
|
Location Mun = world.GetLand("Mun"),
|
||||||
Boh = world.GetLand("Boh"),
|
Boh = world.GetLand("Boh"),
|
||||||
Tyr = world.GetLand("Tyr");
|
Tyr = world.GetLand("Tyr");
|
||||||
|
|
Loading…
Reference in New Issue