Create models for the province map and add test project
The full standard map is big and I'll fill the rest in later.
This commit is contained in:
parent
62da5e780e
commit
ec85ce8729
35
MultiversalDiplomacy/Map/Map.cs
Normal file
35
MultiversalDiplomacy/Map/Map.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using MultiversalDiplomacy.Model;
|
||||
|
||||
namespace MultiversalDiplomacy.Map;
|
||||
|
||||
/// <summary>
|
||||
/// A collection of provinces. Provides shortcut functions for referencing provinces.
|
||||
/// </summary>
|
||||
public abstract class Map
|
||||
{
|
||||
public abstract IEnumerable<Province> Provinces { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sole army-accessible location of a province.
|
||||
/// </summary>
|
||||
public Location Land(string provinceName)
|
||||
=> Provinces
|
||||
.Single(p => p.Name == provinceName || p.Abbreviations.Contains(provinceName))
|
||||
.Locations.Single(l => l.Type == LocationType.Land);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sole fleet-accessible location of a province.
|
||||
/// </summary>
|
||||
public Location Water(string provinceName)
|
||||
=> Provinces
|
||||
.Single(p => p.Name == provinceName || p.Abbreviations.Contains(provinceName))
|
||||
.Locations.Single(l => l.Type == LocationType.Water);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the specified fleet-accessible location of a province with distinct coasts.
|
||||
/// </summary>
|
||||
public Location Coast(string provinceName, string coastName)
|
||||
=> Provinces
|
||||
.Single(p => p.Name == provinceName || p.Abbreviations.Contains(provinceName))
|
||||
.Locations.Single(l => l.Name == coastName || l.Abbreviation == coastName);
|
||||
}
|
311
MultiversalDiplomacy/Map/StandardMap.cs
Normal file
311
MultiversalDiplomacy/Map/StandardMap.cs
Normal file
@ -0,0 +1,311 @@
|
||||
using System.Reflection;
|
||||
|
||||
using MultiversalDiplomacy.Model;
|
||||
|
||||
namespace MultiversalDiplomacy.Map;
|
||||
|
||||
/// <summary>
|
||||
/// The standard Diplomacy map.
|
||||
/// </summary>
|
||||
public class StandardMap : Map
|
||||
{
|
||||
public override IEnumerable<Province> Provinces { get; }
|
||||
|
||||
public static StandardMap Instance { get; } = new StandardMap();
|
||||
|
||||
private StandardMap()
|
||||
{
|
||||
this.Provinces = new List<Province>()
|
||||
{
|
||||
Province.Empty("North Africa", "NAF")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Supply("Tunis", "TUN")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Empty("Bohemia", "BOH")
|
||||
.AddLandLocation(),
|
||||
Province.Supply("Budapest", "BUD")
|
||||
.AddLandLocation(),
|
||||
Province.Empty("Galacia", "GAL")
|
||||
.AddLandLocation(),
|
||||
Province.Supply("Trieste", "TRI")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Empty("Tyrolia", "TYR")
|
||||
.AddLandLocation(),
|
||||
Province.Time("Vienna", "VIE")
|
||||
.AddLandLocation(),
|
||||
Province.Empty("Albania", "ALB")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Supply("Bulgaria", "BUL")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation("east coast", "ec")
|
||||
.AddCoastLocation("south coast", "sc"),
|
||||
Province.Supply("Greece", "GRE")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Supply("Rumania", "RUM", "RMA")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Supply("Serbia", "SER")
|
||||
.AddLandLocation(),
|
||||
Province.Empty("Clyde", "CLY")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Supply("Edinburgh", "EDI")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Supply("Liverpool", "LVP", "LPL")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Time("London", "LON")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Empty("Wales", "WAL")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Empty("Yorkshire", "YOR")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Supply("Brest", "BRE")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Empty("Burgundy", "BUR")
|
||||
.AddLandLocation(),
|
||||
Province.Empty("Gascony", "GAS")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Supply("Marseilles", "MAR")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Time("Paris", "PAR")
|
||||
.AddLandLocation(),
|
||||
Province.Empty("Picardy", "PIC")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Time("Berlin", "BER")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Supply("Kiel", "KIE")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Supply("Munich", "MUN")
|
||||
.AddLandLocation(),
|
||||
Province.Empty("Prussia", "PRU")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Empty("Ruhr", "RUH", "RHR")
|
||||
.AddLandLocation(),
|
||||
Province.Empty("Silesia", "SIL")
|
||||
.AddLandLocation(),
|
||||
Province.Supply("Spain", "SPA")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation("north coast", "nc")
|
||||
.AddCoastLocation("south coast", "sc"),
|
||||
Province.Supply("Portugal", "POR")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Empty("Apulia", "APU")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Supply("Naples", "NAP")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Empty("Piedmont", "PIE")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Time("Rome", "ROM", "RME")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Empty("Tuscany", "TUS")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Supply("Venice", "VEN")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Supply("Belgium", "BEL")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Supply("Holland", "HOL")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Empty("Finland", "FIN")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Empty("Livonia", "LVN", "LVA")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Time("Moscow", "MOS")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Supply("Sevastopol", "SEV")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Supply("Saint Petersburg", "STP")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation("north coast", "nc")
|
||||
.AddCoastLocation("west coast", "wc"),
|
||||
Province.Empty("Ukraine", "UKR")
|
||||
.AddLandLocation(),
|
||||
Province.Supply("Warsaw", "WAR")
|
||||
.AddLandLocation(),
|
||||
Province.Supply("Denmark", "DEN")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Supply("Norway", "NWY")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Supply("Sweden", "SWE")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Supply("Ankara", "ANK")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Empty("Armenia", "ARM")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Time("Constantinople", "CON")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Supply("Smyrna", "SMY")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Empty("Syria", "SYR")
|
||||
.AddLandLocation()
|
||||
.AddCoastLocation(),
|
||||
Province.Empty("Barents Sea", "BAR")
|
||||
.AddOceanLocation(),
|
||||
Province.Empty("English Channel", "ENC", "ECH")
|
||||
.AddOceanLocation(),
|
||||
Province.Empty("Heligoland Bight", "HEL", "HGB")
|
||||
.AddOceanLocation(),
|
||||
Province.Empty("Irish Sea", "IRS", "IRI")
|
||||
.AddOceanLocation(),
|
||||
Province.Empty("Mid-Atlantic Ocean", "MAO", "MID")
|
||||
.AddOceanLocation(),
|
||||
Province.Empty("North Atlantic Ocean", "NAO", "NAT")
|
||||
.AddOceanLocation(),
|
||||
Province.Empty("North Sea", "NTH", "NTS")
|
||||
.AddOceanLocation(),
|
||||
Province.Empty("Norwegian Sea", "NWS", "NWG")
|
||||
.AddOceanLocation(),
|
||||
Province.Empty("Skagerrak", "SKA", "SKG")
|
||||
.AddOceanLocation(),
|
||||
Province.Empty("Baltic Sea", "BAL")
|
||||
.AddOceanLocation(),
|
||||
Province.Empty("Guld of Bothnia", "GOB", "BOT")
|
||||
.AddOceanLocation(),
|
||||
Province.Empty("Adriatic Sea", "ADS", "ADR")
|
||||
.AddOceanLocation(),
|
||||
Province.Empty("Aegean Sea", "AEG")
|
||||
.AddOceanLocation(),
|
||||
Province.Empty("Black Sea", "BLA")
|
||||
.AddOceanLocation(),
|
||||
Province.Empty("Eastern Mediterranean Sea", "EMS", "EAS")
|
||||
.AddOceanLocation(),
|
||||
Province.Empty("Gulf of Lyons", "GOL", "LYO")
|
||||
.AddOceanLocation(),
|
||||
Province.Empty("Ionian Sea", "IOS", "ION", "INS")
|
||||
.AddOceanLocation(),
|
||||
Province.Empty("Tyrrhenian Sea", "TYS", "TYN")
|
||||
.AddOceanLocation(),
|
||||
Province.Empty("Western Mediterranean Sea", "WMS", "WES")
|
||||
.AddOceanLocation(),
|
||||
};
|
||||
|
||||
Land("NAF").AddBorder(Land("TUN"));
|
||||
Water("NAF").AddBorder(Water("MAO"));
|
||||
Water("NAF").AddBorder(Water("WES"));
|
||||
Water("NAF").AddBorder(Water("TUN"));
|
||||
|
||||
Land("TUN").AddBorder(Land("NAF"));
|
||||
Water("TUN").AddBorder(Water("NAF"));
|
||||
Water("TUN").AddBorder(Water("WES"));
|
||||
Water("TUN").AddBorder(Water("TYS"));
|
||||
Water("TUN").AddBorder(Water("ION"));
|
||||
|
||||
Land("BOH").AddBorder(Land("MUN"));
|
||||
Land("BOH").AddBorder(Land("SIL"));
|
||||
Land("BOH").AddBorder(Land("GAL"));
|
||||
Land("BOH").AddBorder(Land("VIE"));
|
||||
Land("BOH").AddBorder(Land("TYR"));
|
||||
|
||||
Land("BUD").AddBorder(Land("VIE"));
|
||||
Land("BUD").AddBorder(Land("GAL"));
|
||||
Land("BUD").AddBorder(Land("RUM"));
|
||||
Land("BUD").AddBorder(Land("SER"));
|
||||
Land("BUD").AddBorder(Land("TRI"));
|
||||
|
||||
Land("GAL").AddBorder(Land("BOH"));
|
||||
Land("GAL").AddBorder(Land("SIL"));
|
||||
Land("GAL").AddBorder(Land("WAR"));
|
||||
Land("GAL").AddBorder(Land("UKR"));
|
||||
Land("GAL").AddBorder(Land("RUM"));
|
||||
Land("GAL").AddBorder(Land("BUD"));
|
||||
Land("GAL").AddBorder(Land("VIE"));
|
||||
|
||||
Land("TRI").AddBorder(Land("VEN"));
|
||||
Land("TRI").AddBorder(Land("TYR"));
|
||||
Land("TRI").AddBorder(Land("VIE"));
|
||||
Land("TRI").AddBorder(Land("BUD"));
|
||||
Land("TRI").AddBorder(Land("SER"));
|
||||
Land("TRI").AddBorder(Land("ALB"));
|
||||
Water("TRI").AddBorder(Water("ALB"));
|
||||
Water("TRI").AddBorder(Water("ADR"));
|
||||
Water("TRI").AddBorder(Water("VEN"));
|
||||
|
||||
Land("TYR").AddBorder(Land("MUN"));
|
||||
Land("TYR").AddBorder(Land("BOH"));
|
||||
Land("TYR").AddBorder(Land("VIE"));
|
||||
Land("TYR").AddBorder(Land("TRI"));
|
||||
Land("TYR").AddBorder(Land("VEN"));
|
||||
Land("TYR").AddBorder(Land("PIE"));
|
||||
|
||||
Land("VIE").AddBorder(Land("TYR"));
|
||||
Land("VIE").AddBorder(Land("BOH"));
|
||||
Land("VIE").AddBorder(Land("GAL"));
|
||||
Land("VIE").AddBorder(Land("BUD"));
|
||||
Land("VIE").AddBorder(Land("TRI"));
|
||||
|
||||
Land("ALB").AddBorder(Land("TRI"));
|
||||
Land("ALB").AddBorder(Land("SER"));
|
||||
Land("ALB").AddBorder(Land("GRE"));
|
||||
Water("ALB").AddBorder(Water("TRI"));
|
||||
Water("ALB").AddBorder(Water("ADR"));
|
||||
Water("ALB").AddBorder(Water("ION"));
|
||||
Water("ALB").AddBorder(Water("GRE"));
|
||||
|
||||
Land("BUL").AddBorder(Land("GRE"));
|
||||
Land("BUL").AddBorder(Land("SER"));
|
||||
Land("BUL").AddBorder(Land("RUM"));
|
||||
Land("BUL").AddBorder(Land("CON"));
|
||||
Coast("BUL", "ec").AddBorder(Water("BLA"));
|
||||
Coast("BUL", "ec").AddBorder(Water("CON"));
|
||||
Coast("BUL", "sc").AddBorder(Water("CON"));
|
||||
Coast("BUL", "sc").AddBorder(Water("AEG"));
|
||||
Coast("BUL", "sc").AddBorder(Water("GRE"));
|
||||
|
||||
Land("GRE").AddBorder(Land("ALB"));
|
||||
Land("GRE").AddBorder(Land("SER"));
|
||||
Land("GRE").AddBorder(Land("BUL"));
|
||||
Water("GRE").AddBorder(Water("ALB"));
|
||||
Water("GRE").AddBorder(Water("ION"));
|
||||
Water("GRE").AddBorder(Water("AEG"));
|
||||
Water("GRE").AddBorder(Coast("BUL", "sc"));
|
||||
|
||||
// TODO
|
||||
|
||||
Water("IOS").AddBorder(Water("TUN"));
|
||||
Water("IOS").AddBorder(Water("TYS"));
|
||||
Water("IOS").AddBorder(Water("NAP"));
|
||||
Water("IOS").AddBorder(Water("APU"));
|
||||
Water("IOS").AddBorder(Water("ADR"));
|
||||
Water("IOS").AddBorder(Water("ALB"));
|
||||
Water("IOS").AddBorder(Water("GRE"));
|
||||
Water("IOS").AddBorder(Water("AEG"));
|
||||
|
||||
// TODO
|
||||
}
|
||||
}
|
52
MultiversalDiplomacy/Model/Location.cs
Normal file
52
MultiversalDiplomacy/Model/Location.cs
Normal file
@ -0,0 +1,52 @@
|
||||
namespace MultiversalDiplomacy.Model;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a locus of connectivity in a province. Land-locked and ocean/sea provinces
|
||||
/// have one location. Coastal provinces have a land location and some number of named
|
||||
/// water locations.
|
||||
/// </summary>
|
||||
public class Location
|
||||
{
|
||||
/// <summary>
|
||||
/// The province to which this location belongs.
|
||||
public Province Province { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The location's full human-readable name.
|
||||
/// </summary>
|
||||
public string? Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The location's shorthand abbreviation.
|
||||
/// </summary>
|
||||
public string? Abbreviation { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The location's type.
|
||||
/// </summary>
|
||||
public LocationType Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The locations that border this location.
|
||||
/// </summary>
|
||||
public IEnumerable<Location> Adjacents => this.AdjacentList;
|
||||
private List<Location> AdjacentList { get; set; }
|
||||
|
||||
public Location(Province province, string? name, string? abbreviation, LocationType type)
|
||||
{
|
||||
this.Province = province;
|
||||
this.Name = name;
|
||||
this.Abbreviation = abbreviation;
|
||||
this.Type = type;
|
||||
this.AdjacentList = new List<Location>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set another location as bordering this location.
|
||||
/// </summary>
|
||||
public void AddBorder(Location other)
|
||||
{
|
||||
if (!this.AdjacentList.Contains(other)) this.AdjacentList.Add(other);
|
||||
if (!other.AdjacentList.Contains(this)) other.AdjacentList.Add(this);
|
||||
}
|
||||
}
|
17
MultiversalDiplomacy/Model/LocationType.cs
Normal file
17
MultiversalDiplomacy/Model/LocationType.cs
Normal file
@ -0,0 +1,17 @@
|
||||
namespace MultiversalDiplomacy.Model;
|
||||
|
||||
/// <summary>
|
||||
/// The type of a location in a province.
|
||||
/// </summary>
|
||||
public enum LocationType
|
||||
{
|
||||
/// <summary>
|
||||
/// An army-accessible location.
|
||||
/// </summary>
|
||||
Land = 0,
|
||||
|
||||
/// <summary>
|
||||
/// A fleet-accessible location.
|
||||
/// </summary>
|
||||
Water = 1,
|
||||
}
|
103
MultiversalDiplomacy/Model/Province.cs
Normal file
103
MultiversalDiplomacy/Model/Province.cs
Normal file
@ -0,0 +1,103 @@
|
||||
namespace MultiversalDiplomacy.Model;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a single province as it exists across all timelines.
|
||||
/// </summary>
|
||||
public class Province
|
||||
{
|
||||
/// <summary>
|
||||
/// The province's full human-readable name.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The province's shorthand abbreviation.
|
||||
/// </summary>
|
||||
public string[] Abbreviations { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the province contains a supply center.
|
||||
/// </summary>
|
||||
public bool IsSupplyCenter { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the province contains a time center.
|
||||
/// </summary>
|
||||
public bool IsTimeCenter { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The occupiable locations in this province. Only one location can be occupied at a time.
|
||||
/// </summary>
|
||||
public IEnumerable<Location> Locations => LocationList;
|
||||
private List<Location> LocationList { get; set; }
|
||||
|
||||
public Province(string name, string[] abbreviations, bool isSupply, bool isTime)
|
||||
{
|
||||
this.Name = name;
|
||||
this.Abbreviations = abbreviations;
|
||||
this.IsSupplyCenter = isSupply;
|
||||
this.IsTimeCenter = isTime;
|
||||
this.LocationList = new List<Location>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new province with no supply center.
|
||||
/// </summary>
|
||||
public static Province Empty(string name, params string[] abbreviations)
|
||||
=> new Province(name, abbreviations, isSupply: false, isTime: false);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new province with a supply center.
|
||||
/// </summary>
|
||||
public static Province Supply(string name, params string[] abbreviations)
|
||||
=> new Province(name, abbreviations, isSupply: true, isTime: false);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new province with a time center.
|
||||
/// </summary>
|
||||
public static Province Time(string name, params string[] abbreviations)
|
||||
=> new Province(name, abbreviations, isSupply: true, isTime: true);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new land location in this province.
|
||||
/// </summary>
|
||||
public Province AddLandLocation()
|
||||
{
|
||||
Location location = new Location(this, name: null, abbreviation: null, LocationType.Land);
|
||||
this.LocationList.Add(location);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new ocean location.
|
||||
/// </summary>
|
||||
public Province AddOceanLocation()
|
||||
{
|
||||
Location location = new Location(this, name: null, abbreviation: null, LocationType.Water);
|
||||
this.LocationList.Add(location);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new coastal location. Coastal locations must have names to disambiguate them
|
||||
/// from the single land location in coastal provinces.
|
||||
/// </summary>
|
||||
public Province AddCoastLocation()
|
||||
{
|
||||
// Use a default name for provinces with only one coastal location
|
||||
Location location = new Location(this, "coast", "c", LocationType.Water);
|
||||
this.LocationList.Add(location);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new coastal location. Coastal locations must have names to disambiguate them
|
||||
/// from the single land location in coastal provinces.
|
||||
/// </summary>
|
||||
public Province AddCoastLocation(string name, string abbreviation)
|
||||
{
|
||||
Location location = new Location(this, name, abbreviation, LocationType.Water);
|
||||
this.LocationList.Add(location);
|
||||
return this;
|
||||
}
|
||||
}
|
58
MultiversalDiplomacyTests/MapTests.cs
Normal file
58
MultiversalDiplomacyTests/MapTests.cs
Normal file
@ -0,0 +1,58 @@
|
||||
using MultiversalDiplomacy.Map;
|
||||
using MultiversalDiplomacy.Model;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace MultiversalDiplomacyTests;
|
||||
|
||||
public class MapTests
|
||||
{
|
||||
IEnumerable<Location> LocationClosure(Location location)
|
||||
{
|
||||
IEnumerable<Location> visited = new List<Location>();
|
||||
IEnumerable<Location> toVisit = new List<Location>() { location };
|
||||
|
||||
while (toVisit.Any())
|
||||
{
|
||||
Location next = toVisit.First();
|
||||
toVisit = toVisit.Skip(1);
|
||||
visited = visited.Append(next);
|
||||
foreach (Location other in next.Adjacents)
|
||||
{
|
||||
if (!visited.Contains(other)) toVisit = toVisit.Append(other);
|
||||
}
|
||||
}
|
||||
|
||||
return visited;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MapCreation()
|
||||
{
|
||||
Province left = Province.Empty("Left", "Lef")
|
||||
.AddLandLocation();
|
||||
Province center = Province.Empty("Center", "Cen")
|
||||
.AddLandLocation();
|
||||
Province right = Province.Empty("Right", "Rig")
|
||||
.AddLandLocation();
|
||||
|
||||
Location leftL = left.Locations.First();
|
||||
Location centerL = center.Locations.First();
|
||||
Location rightL = right.Locations.First();
|
||||
centerL.AddBorder(leftL);
|
||||
rightL.AddBorder(centerL);
|
||||
|
||||
IEnumerable<Location> closure = LocationClosure(leftL);
|
||||
Assert.That(closure.Contains(leftL), Is.True, "Expected Left in closure");
|
||||
Assert.That(closure.Contains(centerL), Is.True, "Expected Center in closure");
|
||||
Assert.That(closure.Contains(rightL), Is.True, "Expected Right in closure");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LandAndSeaBorders()
|
||||
{
|
||||
Map map = StandardMap.Instance;
|
||||
Assert.That(map.Land("NAF").Adjacents.Count(), Is.EqualTo(1), "Expected 1 bordering land province");
|
||||
Assert.That(map.Water("NAF").Adjacents.Count(), Is.EqualTo(3), "Expected 3 bordering sea provinces");
|
||||
}
|
||||
}
|
21
MultiversalDiplomacyTests/MultiversalDiplomacyTests.csproj
Normal file
21
MultiversalDiplomacyTests/MultiversalDiplomacyTests.csproj
Normal file
@ -0,0 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MultiversalDiplomacy\MultiversalDiplomacy.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
33
README.md
33
README.md
@ -116,3 +116,36 @@ Neat. VS Code doesn't seamlessly work with nix, so to get the extensions working
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Comprehending all of time and space
|
||||
|
||||
Now for the data model. The state of the world can be described in three layers, so to speak.
|
||||
|
||||
1. The board is the same across time and multiverse. Since it consists of (i) provinces and (ii) borders connecting two provinces, we're going to model it like a graph. However, a simple graph won't work because provinces are differently accessible to armies and fleets, and coasts are part of the same province while having distinct connectivity. Following the data model described in [godip](https://github.com/zond/godip), we will model this by subdividing provinces and making connections between those subdivisions. This will effectively create multiple distinct graphs for fleets and armies within the map, with some nodes grouped for control purposes into provinces. Since the map itself does not change across time, we can make this "layer" completely immutable and shared between turns and timelines.
|
||||
2. Since we need to preserve the state of the past in order to time travel effectively, we won't be mutating a board state. Instead, we'll use orders submitted for each turn to append a new copy of the board state. This can't be represented by a simple list, since we can have more than one timeline branch off of a particular turn, so instead we'll use something like a directed graph and create turns pointing to their immediate past.
|
||||
3. Given the map of the board and the state of all timelines, all units have a spatial location on the board and a temporal location in one of the turns. As the game progresses and timelines are extended, we'll create copies of the unit in each turn it appears in.
|
||||
|
||||
This is going to get complicated, and we're looking forward to implementing the [Diplomacy Adjudicator Test Cases](http://web.inter.nl.net/users/L.B.Kruijswijk/), so let's also create a test project:
|
||||
|
||||
```
|
||||
5dplomacy:5dplomacy$ dotnet new nunit --name MultiversalDiplomacyTests --output MultiversalDiplomacyTests
|
||||
```
|
||||
|
||||
I think dotnet will fetch NUnit when it needs it, but to get it into our environment so VS Code recognizes it, we add it to the nix shell:
|
||||
|
||||
```
|
||||
packages = [ pkgs.dotnet-sdk pkgs.dotnetPackages.NUnit3 ];
|
||||
```
|
||||
|
||||
After writing some basic tests, we can run them with:
|
||||
|
||||
```
|
||||
5dplomacy:5dplomacy$ dotnet test MultiversalDiplomacyTests/
|
||||
[...]
|
||||
Starting test execution, please wait...
|
||||
A total of 1 test files matched the specified pattern.
|
||||
|
||||
Passed! - Failed: 0, Passed: 2, Skipped: 0, Total: 2, Duration: 29 ms - 5dplomacy/MultiversalDiplomacyTests/bin/Debug/net6.0/MultiversalDiplomacyTests.dll (net6.0)
|
||||
```
|
||||
|
||||
Neat.
|
||||
|
Loading…
Reference in New Issue
Block a user