From 66a85f236059ae6498c0592bcb26fe249a4a2ad0 Mon Sep 17 00:00:00 2001 From: Jaculabilis Date: Sat, 18 Mar 2023 21:44:51 -0700 Subject: [PATCH] Add game setup script handler This will allow setting up arbitrary test cases when writing test scripts --- .../Script/ReplScriptHandler.cs | 12 +- .../Script/SetupScriptHandler.cs | 135 ++++++++++++++++++ 2 files changed, 141 insertions(+), 6 deletions(-) create mode 100644 MultiversalDiplomacy/Script/SetupScriptHandler.cs diff --git a/MultiversalDiplomacy/Script/ReplScriptHandler.cs b/MultiversalDiplomacy/Script/ReplScriptHandler.cs index 37d2622..16726a7 100644 --- a/MultiversalDiplomacy/Script/ReplScriptHandler.cs +++ b/MultiversalDiplomacy/Script/ReplScriptHandler.cs @@ -32,21 +32,21 @@ public class ReplScriptHandler : IScriptHandler case "new" when args.Length == 1: Console.WriteLine("usage:"); - Console.WriteLine(" new blank: standard map, no units"); + Console.WriteLine(" new custom: standard map, declare units"); Console.WriteLine(" new standard: standard map, standard units"); break; - case "new" when args[1] == "blank": + case "new" when args[1] == "custom": var world = MultiversalDiplomacy.Model.World.WithStandardMap(); - var handler = new GameScriptHandler(world); + var setupHandler = new SetupScriptHandler(world); Console.WriteLine("Created an empty game"); - return handler; + return setupHandler; case "new" when args[1] == "standard": world = GameController.InitializeWorld(); - handler = new GameScriptHandler(world); + var gameHandler = new GameScriptHandler(world); Console.WriteLine("Created a standard game"); - return handler; + return gameHandler; default: Console.WriteLine("Unrecognized command"); diff --git a/MultiversalDiplomacy/Script/SetupScriptHandler.cs b/MultiversalDiplomacy/Script/SetupScriptHandler.cs new file mode 100644 index 0000000..f2c73e8 --- /dev/null +++ b/MultiversalDiplomacy/Script/SetupScriptHandler.cs @@ -0,0 +1,135 @@ +using MultiversalDiplomacy.Model; + +namespace MultiversalDiplomacy.Script; + +/// +/// A script handler for interacting with a loaded game. +/// +public class SetupScriptHandler : IScriptHandler +{ + public SetupScriptHandler(World world) + { + World = world; + } + + public string Prompt => "5dp> "; + + public World World { get; set; } + + public IScriptHandler? HandleInput(string input) + { + var args = input.Split(' ', StringSplitOptions.RemoveEmptyEntries); + if (args.Length == 0) + { + return new GameScriptHandler(World); + } + + var command = args[0]; + switch (command) + { + case "help": + case "?": + Console.WriteLine("commands:"); + Console.WriteLine(" unit: add a unit to the map"); + Console.WriteLine(" list: list things in a game category"); + Console.WriteLine(" enter a blank line to complete setup"); + break; + + case "list" when args.Length == 1: + Console.WriteLine("usage:"); + Console.WriteLine(" list powers: the powers in the game"); + Console.WriteLine(" list units: units created so far"); + break; + + case "list" when args[1] == "powers": + Console.WriteLine("Powers:"); + foreach (Power power in World.Powers) + { + Console.WriteLine($" {power.Name}"); + } + break; + + case "list" when args[1] == "units": + Console.WriteLine("Units:"); + foreach (Unit unit in World.Units) + { + Console.WriteLine($" {unit}"); + } + break; + + case "unit" when args.Length < 4: + Console.WriteLine("usage: unit [power] [type] [province]"); + break; + + case "unit": + ParseUnit(args); + break; + + default: + Console.WriteLine("Unrecognized command"); + break; + } + + return this; + } + + private void ParseUnit(string[] args) + { + // Parse the power name by substring matching. + string powerArg = args[1].ToLower(); + var matchingPowers = World.Powers.Where(p => p.Name.ToLower().StartsWith(powerArg)); + if (matchingPowers.Count() < 1) + { + Console.WriteLine($"No power named \"{args[1]}\""); + return; + } + if (matchingPowers.Count() > 1) + { + Console.WriteLine($"Ambiguous power \"{args[1]}\""); + return; + } + Power power = matchingPowers.First(); + + // Parse the unit type by substring matching. + string typeArg = args[2].ToLower(); + var matchingTypes = Enum.GetNames().Where(t => t.ToLower().StartsWith(typeArg)); + if (matchingTypes.Count() < 1) + { + Console.WriteLine($"No unit type \"{args[2]}\""); + return; + } + if (matchingTypes.Count() > 1) + { + Console.WriteLine($"Ambiguous unit type \"{args[2]}\""); + return; + } + UnitType type = (UnitType)Enum.Parse(typeof(UnitType), matchingTypes.First()); + + // Parse the province by matching the province name or one of its abbreviations, + // allowing location specifications separated by a forward slash, e.g. spa/nc. + string provinceArg = args[3].ToLower(); + var matchingProvs = World.Provinces.Where(pr + => pr.Abbreviations.Any(abv => abv.ToLower() == provinceArg) + || pr.Name.ToLower() == provinceArg); + if (matchingProvs.Count() < 1) + { + Console.WriteLine($"No province matches \"{args[3]}\""); + return; + } + if (matchingTypes.Count() > 1) + { + Console.WriteLine($"Ambiguous province \"{args[3]}\""); + return; + } + // TODO: this does not support multi-location provinces correctly + Province province = matchingProvs.First(); + Location location = province.Locations.First(loc + => type == UnitType.Army && loc.Type == LocationType.Land + || type == UnitType.Fleet && loc.Type == LocationType.Water); + + Unit unit = Unit.Build(location, World.RootSeason, power, type); + World = World.Update(units: World.Units.Append(unit)); + + Console.WriteLine($"Created unit {unit}"); + } +} \ No newline at end of file