Refactor map definitions into World

This commit is contained in:
Jaculabilis 2022-03-12 23:15:26 -08:00
parent 8a7e90b949
commit 18c5435c96
9 changed files with 536 additions and 402 deletions

View File

@ -1,35 +0,0 @@
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);
}

View File

@ -1,311 +0,0 @@
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
}
}

View File

@ -0,0 +1,8 @@
namespace MultiversalDiplomacy.Model;
/// <summary>
/// Object representing immutable configuration options about the game.
/// </summary>
public class Options
{
}

View File

@ -25,15 +25,512 @@ public class World
/// </summary>
public IEnumerable<Unit> Units { get; }
/// <summary>
/// Immutable game options.
/// </summary>
public Options Options { get; }
public World(
IEnumerable<Province> provinces,
IEnumerable<Power> powers,
IEnumerable<Season> seasons,
IEnumerable<Unit> units)
IEnumerable<Unit> units,
Options options)
{
this.Provinces = provinces;
this.Powers = powers;
this.Seasons = seasons;
this.Units = units;
this.Options = options;
}
/// <summary>
/// Create a new world with no map, powers, or units, and a root season.
/// </summary>
public static World Empty => new World(
new List<Province>(),
new List<Power>(),
new List<Season> { Season.MakeRoot() },
new List<Unit>(),
new Options());
/// <summary>
/// Create a world with a standard map, powers, and initial unit placements.
/// </summary>
public static World Standard => Empty
.WithStandardMap()
.WithStandardPowers()
.WithStandardUnits();
/// <summary>
/// Get a province by name. Throws if the province is not found.
/// </summary>
private Province GetProvince(string provinceName)
{
string provinceNameUpper = provinceName.ToUpperInvariant();
Province? foundProvince = this.Provinces.FirstOrDefault(
p => p != null &&
(p.Name.ToUpperInvariant() == provinceNameUpper
|| p.Abbreviations.Any(a => a.ToUpperInvariant() == provinceNameUpper)),
null);
if (foundProvince == null) throw new ArgumentOutOfRangeException(
$"Province {provinceName} not found");
return foundProvince;
}
/// <summary>
/// Get the location in a province matching a predicate. Throws if there is not exactly one
/// such location.
/// </summary>
private Location GetLocation(string provinceName, Func<Location, bool> predicate)
{
Location? foundLocation = GetProvince(provinceName).Locations.SingleOrDefault(
l => l != null && predicate(l), null);
if (foundLocation == null) throw new ArgumentException(
$"No such location in {provinceName}");
return foundLocation;
}
/// <summary>
/// Get the sole land location of a province.
/// </summary>
public Location GetLand(string provinceName)
=> GetLocation(provinceName, l => l.Type == LocationType.Land);
/// <summary>
/// Get the sole water location of a province, optionally specifying a named coast.
/// </summary>
public Location GetWater(string provinceName, string? coastName = null)
=> coastName == null
? GetLocation(provinceName, l => l.Type == LocationType.Water)
: GetLocation(provinceName, l => l.Name == coastName || l.Abbreviation == coastName);
/// <summary>
/// Get a power by name. Throws if the power is not found.
/// </summary>
public Power GetPower(string powerName)
{
Power? foundPower = this.Powers.FirstOrDefault(
p =>
p != null
&& (p.Name == powerName || p.Name.StartsWith(powerName)),
null);
if (foundPower == null) throw new ArgumentOutOfRangeException(
$"Power {powerName} not found");
return foundPower;
}
/// <summary>
/// Create a new world from this one with new provinces.
/// </summary>
public World WithMap(IEnumerable<Province> provinces)
{
if (this.Units.Any()) throw new InvalidOperationException(
"Provinces cannot be changed once units have been placed on the map");
return new World(provinces, this.Powers, this.Seasons, this.Units, this.Options);
}
/// <summary>
/// Create a new world from this one with the standard Diplomacy provinces.
/// </summary>
public World WithStandardMap()
{
// Define the provinces of the standard world map.
List<Province> standardProvinces = 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(),
};
// Declare some helpers for border definitions
Location Land(string provinceName) => standardProvinces
.Single(p => p.Name == provinceName || p.Abbreviations.Contains(provinceName))
.Locations.Single(l => l.Type == LocationType.Land);
Location Water(string provinceName) => standardProvinces
.Single(p => p.Name == provinceName || p.Abbreviations.Contains(provinceName))
.Locations.Single(l => l.Type == LocationType.Water);
Location Coast(string provinceName, string coastName) => standardProvinces
.Single(p => p.Name == provinceName || p.Abbreviations.Contains(provinceName))
.Locations.Single(l => l.Name == coastName || l.Abbreviation == coastName);
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
return this.WithMap(standardProvinces);
}
/// <summary>
/// Create a new world from this one with new powers.
/// </summary>
public World WithPowers(IEnumerable<Power> powers)
=> new World(this.Provinces, powers, this.Seasons, this.Units, this.Options);
/// <summary>
/// Create a new world from this one with new powers created with the given names.
/// </summary>
public World WithPowers(IEnumerable<string> powerNames)
=> WithPowers(powerNames.Select(name => new Model.Power(name)));
/// <summary>
/// Create a new world from this one with new powers created with the given names.
/// </summary>
public World WithPowers(params string[] powerNames)
=> WithPowers(powerNames.AsEnumerable());
/// <summary>
/// Create a new world from this one with the standard Diplomacy powers.
/// </summary>
public World WithStandardPowers()
=> WithPowers("Austria", "England", "France", "Germany", "Italy", "Russia", "Turkey");
/// <summary>
/// Create a new world from this one 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 abbreviation should be used.
/// </summary>
public World WithUnits(IEnumerable<string> unitSpec)
{
IEnumerable<Unit> units = unitSpec.Select(spec =>
{
string[] splits = spec.Split(' ', 4);
Power power = this.GetPower(splits[0]);
UnitType type = splits[1] switch
{
"A" => UnitType.Army,
"F" => UnitType.Fleet,
_ => throw new ArgumentOutOfRangeException($"Unknown unit type {splits[1]}")
};
Location location = type == UnitType.Army
? this.GetLand(splits[2])
: splits.Length == 3
? this.GetWater(splits[2])
: this.GetWater(splits[2], splits[3]);
Unit unit = Unit.Build(location, this.Seasons.First(), power, type);
return unit;
});
return new World(this.Provinces, this.Powers, this.Seasons, units, this.Options);
}
/// <summary>
/// Create a new world from this one with new units created from unit specs. Units specs are
/// in the format "<power> <A/F> <province> [<coast>]".
/// </summary>
public World WithUnits(params string[] unitSpec)
=> this.WithUnits(unitSpec.AsEnumerable());
/// <summary>
/// Create a new world from this one with new units created according to the standard Diplomacy
/// initial unit deployments.
/// </summary>
public World WithStandardUnits()
{
return this.WithUnits(
"Austria A Bud",
"Austria A Vir",
"Austria F Tri",
"England A Lvp",
"England F Edi",
"England F Lon",
"France A Mar",
"France A Par",
"France F Bre",
"Germany A Ber",
"Germany A Mun",
"Germany F Kie",
"Italy A Rom",
"Italy A Ven",
"Italy F Nap",
"Russia A Mos",
"Russia A War",
"Russia F Sev",
"Russia F Stp wc",
"Turkey A Con",
"Turkey A Smy",
"Turkey F Ank"
);
}
}

View File

@ -11,14 +11,16 @@ public class AdjudicatorTests
[Test]
public void OrderValidationTest()
{
IPhaseAdjudicator rubberStamp = new TestAdjudicator(orders => {
return orders.Select(o => o.Validate(ValidationReason.Valid));
IPhaseAdjudicator rubberStamp = new TestAdjudicator((world, orders) =>
{
return orders.Select(o => o.Validate(ValidationReason.Valid)).ToList();
});
Power power = new Power(nameof(Power));
World world = World.Empty.WithPowers("Power");
Power power = world.GetPower("Power");
Order order = new NullOrder(power);
List<Order> orders = new List<Order> { order };
IEnumerable<OrderValidation> results = rubberStamp.ValidateOrders(orders);
IEnumerable<OrderValidation> results = rubberStamp.ValidateOrders(world, orders);
Assert.That(results.Count(), Is.EqualTo(1));
Assert.That(results.First().Order, Is.EqualTo(order));

View File

@ -1,4 +1,3 @@
using MultiversalDiplomacy.Map;
using MultiversalDiplomacy.Model;
using NUnit.Framework;
@ -51,8 +50,14 @@ public class MapTests
[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");
World map = World.Empty.WithStandardMap();
Assert.That(
map.GetLand("NAF").Adjacents.Count(),
Is.EqualTo(1),
"Expected 1 bordering land province");
Assert.That(
map.GetWater("NAF").Adjacents.Count(),
Is.EqualTo(3),
"Expected 3 bordering sea provinces");
}
}

View File

@ -1,18 +1,19 @@
using MultiversalDiplomacy.Adjudicate;
using MultiversalDiplomacy.Model;
using MultiversalDiplomacy.Orders;
namespace MultiversalDiplomacyTests;
public class TestAdjudicator : IPhaseAdjudicator
{
private Func<IEnumerable<Order>, IEnumerable<OrderValidation>> ValidateOrdersCallback;
private Func<World, List<Order>, List<OrderValidation>> ValidateOrdersCallback;
public TestAdjudicator(
Func<IEnumerable<Order>, IEnumerable<OrderValidation>> validateOrdersCallback)
Func<World, List<Order>, List<OrderValidation>> validateOrdersCallback)
{
this.ValidateOrdersCallback = validateOrdersCallback;
}
public IEnumerable<OrderValidation> ValidateOrders(IEnumerable<Order> orders)
=> this.ValidateOrdersCallback.Invoke(orders);
public List<OrderValidation> ValidateOrders(World world, List<Order> orders)
=> this.ValidateOrdersCallback.Invoke(world, orders);
}

View File

@ -1,32 +0,0 @@
using MultiversalDiplomacy.Map;
using MultiversalDiplomacy.Model;
namespace MultiversalDiplomacyTests;
public class TestMap : Map
{
public override IEnumerable<Province> Provinces { get; }
public static TestMap Instance { get; } = new TestMap();
private TestMap()
{
Provinces = new List<Province>()
{
Province.Supply("Left", "LEF")
.AddLandLocation(),
Province.Supply("Right", "RIG")
.AddLandLocation(),
Province.Empty("Center", "CEN")
.AddLandLocation(),
};
Land("LEF").AddBorder(Land("RIG"));
Land("LEF").AddBorder(Land("CEN"));
Land("RIG").AddBorder(Land("LEF"));
Land("RIG").AddBorder(Land("CEN"));
Land("CEN").AddBorder(Land("LEF"));
Land("CEN").AddBorder(Land("RIG"));
}
}

View File

@ -1,4 +1,3 @@
using MultiversalDiplomacy.Map;
using MultiversalDiplomacy.Model;
using NUnit.Framework;
@ -10,17 +9,17 @@ public class UnitTests
[Test]
public void MovementTest()
{
Map map = TestMap.Instance;
Location left = map.Land("LEF"), right = map.Land("RIG"), center = map.Land("CEN");
Power pw1 = new Power("First");
Season s1 = Season.MakeRoot();
Unit u1 = Unit.Build(left, s1, pw1, UnitType.Army);
World world = World.Empty.WithStandardMap().WithPowers("First");
Location Mun = world.GetLand("Mun"), Boh = world.GetLand("Boh"), Tyr = world.GetLand("Tyr");
Power pw1 = world.GetPower("First");
Season s1 = world.Seasons.First();
Unit u1 = Unit.Build(Mun, s1, pw1, UnitType.Army);
Season s2 = s1.MakeNext();
Unit u2 = u1.Next(right, s2);
Unit u2 = u1.Next(Boh, s2);
Season s3 = s2.MakeNext();
Unit u3 = u2.Next(center, s3);
Unit u3 = u2.Next(Tyr, s3);
Assert.That(u3.Past, Is.EqualTo(u2), "Missing unit past");
Assert.That(u2.Past, Is.EqualTo(u1), "Missing unit past");
@ -30,8 +29,8 @@ public class UnitTests
Assert.That(u2.Season, Is.EqualTo(s2), "Unexpected unit season");
Assert.That(u3.Season, Is.EqualTo(s3), "Unexpected unit season");
Assert.That(u1.Location, Is.EqualTo(left), "Unexpected unit location");
Assert.That(u2.Location, Is.EqualTo(right), "Unexpected unit location");
Assert.That(u3.Location, Is.EqualTo(center), "Unexpected unit location");
Assert.That(u1.Location, Is.EqualTo(Mun), "Unexpected unit location");
Assert.That(u2.Location, Is.EqualTo(Boh), "Unexpected unit location");
Assert.That(u3.Location, Is.EqualTo(Tyr), "Unexpected unit location");
}
}