From ebeb17898429ee90983f1843b49e4b2fe52c9e98 Mon Sep 17 00:00:00 2001 From: Tim Van Baak Date: Sun, 25 Aug 2024 04:26:10 +0000 Subject: [PATCH] Move some parsing code into OrderRegex --- MultiversalDiplomacy/Model/Regex.cs | 62 ++++++++++++++++++- .../Script/GameScriptHandler.cs | 50 +++++++-------- .../Script/SetupScriptHandler.cs | 41 ++---------- 3 files changed, 85 insertions(+), 68 deletions(-) diff --git a/MultiversalDiplomacy/Model/Regex.cs b/MultiversalDiplomacy/Model/Regex.cs index 87b3662..db55fb2 100644 --- a/MultiversalDiplomacy/Model/Regex.cs +++ b/MultiversalDiplomacy/Model/Regex.cs @@ -1,5 +1,8 @@ +using System.Diagnostics.CodeAnalysis; using System.Text.RegularExpressions; +using MultiversalDiplomacy.Orders; + namespace MultiversalDiplomacy.Model; public class OrderRegex(World world) @@ -16,7 +19,7 @@ public class OrderRegex(World world) public string FullLocation => $"(?:{Timeline}-)?{world.Map.ProvinceRegex}(?:{SlashLocation}|{ParenLocation})?(?:@{Turn})?"; - public string Unit => $"(?:(?:{world.Map.PowerRegex} )?{Type} )?{FullLocation}"; + public string UnitSpec => $"(?:(?:{world.Map.PowerRegex} )?{Type} )?{FullLocation}"; public const string HoldVerb = "(h|hold|holds)"; @@ -45,7 +48,7 @@ public class OrderRegex(World world) match.Groups[7].Value, match.Groups[8].Value); - public Regex Move => new($"^{Unit} {MoveVerb} {FullLocation}$(?: {ViaConvoy})?"); + public Regex Move => new($"^{UnitSpec} {MoveVerb} {FullLocation}$(?: {ViaConvoy})?"); public static ( string power, @@ -77,4 +80,59 @@ public class OrderRegex(World world) : match.Groups[12].Value, match.Groups[13].Value, match.Groups[14].Value); + + public static bool TryParseUnit(World world, string unitSpec, [NotNullWhen(true)] out Unit? newUnit) + { + newUnit = null; + + Regex reUnit = new( + $"^{world.Map.PowerRegex} {Type} {world.Map.ProvinceRegex}(?:{SlashLocation}|{ParenLocation})?$"); + Match match = reUnit.Match(unitSpec); + if (!match.Success) { + Console.WriteLine($"Could not match unit spec \"{unitSpec}\""); + return false; + } + + string power = world.Map.Powers.First(p => p.EqualsAnyCase(match.Groups[1].Value)); + + string typeName = Enum.GetNames().First(name => name.StartsWithAnyCase(match.Groups[2].Value)); + UnitType type = Enum.Parse(typeName); + + Province province = world.Map.Provinces.First(prov + => prov.Name.EqualsAnyCase(match.Groups[3].Value) + || prov.Abbreviations.Any(abv => abv.EqualsAnyCase(match.Groups[3].Value))); + + string locationName = match.Groups[4].Length > 0 ? match.Groups[4].Value : match.Groups[5].Value; + Location location = province.Locations.First(loc + => loc.Name.StartsWithAnyCase(locationName) + || loc.Abbreviation.StartsWithAnyCase(locationName)); + + newUnit = Unit.Build(location.Key, Season.First, power, type); + return true; + } + + public static bool TryParseOrder(World world, string power, string command, out Order? order) { + order = null; + OrderRegex re = new(world); + Match match; + if ((match = re.Hold.Match(command)).Success) { + var hold = ParseHold(match); + Unit unit = world.Units.First(unit + => world.Map.GetLocation(unit.Location).ProvinceName == hold.province + && unit.Season.Timeline == (hold.timeline.Length > 0 ? hold.timeline : "a") + && unit.Season.Turn.ToString() == (hold.turn.Length > 0 ? hold.turn : "0")); + order = new HoldOrder(power, unit); + return true; + } else if ((match = re.Move.Match(command)).Success) { + var move = ParseMove(match); + Unit unit = world.Units.First(unit + => world.Map.GetLocation(unit.Location).ProvinceName == move.province + && unit.Season.Timeline == (move.timeline.Length > 0 ? move.timeline : "a") + && unit.Season.Turn.ToString() == (move.turn.Length > 0 ? move.turn : "0")); + order = new MoveOrder(power, unit, Season.First, "l"); + return true; + } + + return false; + } } \ No newline at end of file diff --git a/MultiversalDiplomacy/Script/GameScriptHandler.cs b/MultiversalDiplomacy/Script/GameScriptHandler.cs index 647bb70..21f1263 100644 --- a/MultiversalDiplomacy/Script/GameScriptHandler.cs +++ b/MultiversalDiplomacy/Script/GameScriptHandler.cs @@ -50,39 +50,31 @@ public class GameScriptHandler(World world, bool strict = false) : IScriptHandle return this; } - if (TryParseOrder(input, out Order? order)) { - Console.WriteLine($"Parsed order \"{input}\" but doing anything with it isn't implemented yet"); - } else if (Strict) { - return null; + // If it's not a comment, submit, or order block, assume it's an order. + string orderPower; + string orderText; + if (CurrentPower is not null) { + // In a block of orders from a power, the power was specified at the top and each line is an order. + orderPower = CurrentPower; + orderText = input; + } else { + // Outside a power block, the power is prefixed to each order. + Regex re = new($"^{World.Map.PowerRegex}(?:[:])? (.*)$", RegexOptions.IgnoreCase); + var match = re.Match(input); + if (!match.Success) { + Console.WriteLine($"Could not determine ordering power in \"{input}\""); + return Strict ? null : this; + } + orderPower = match.Groups[1].Value; + orderText = match.Groups[2].Value; } + if (OrderRegex.TryParseOrder(World, orderPower, orderText, out Order? order)) { + Console.WriteLine($"Parsed {orderPower} order \"{orderText}\" but doing anything with it isn't implemented yet"); return this; } - private bool TryParseOrder(string input, out Order? order) { - order = null; - OrderRegex re = new(World); - Match match; - if ((match = re.Hold.Match(input)).Success) { - var hold = OrderRegex.ParseHold(match); - string power = hold.power.Length > 0 ? hold.power : CurrentPower ?? hold.power; - Unit unit = World.Units.First(unit - => World.Map.GetLocation(unit.Location).ProvinceName == hold.province - && unit.Season.Timeline == (hold.timeline.Length > 0 ? hold.timeline : "a") - && unit.Season.Turn.ToString() == (hold.turn.Length > 0 ? hold.turn : "0")); - order = new HoldOrder(power, unit); - return true; - } else if ((match = re.Move.Match(input)).Success) { - var move = OrderRegex.ParseMove(match); - Unit unit = World.Units.First(unit - => World.Map.GetLocation(unit.Location).ProvinceName == move.province - && unit.Season.Timeline == (move.timeline.Length > 0 ? move.timeline : "a") - && unit.Season.Turn.ToString() == (move.turn.Length > 0 ? move.turn : "0")); - order = new MoveOrder(move.power, unit, Season.First, "l"); - return true; - } - - Console.WriteLine($"Failed to parse \"{input}\""); - return false; + Console.WriteLine($"Failed to parse \"{orderText}\""); + return Strict ? null : this; } } diff --git a/MultiversalDiplomacy/Script/SetupScriptHandler.cs b/MultiversalDiplomacy/Script/SetupScriptHandler.cs index 9df9917..7c74523 100644 --- a/MultiversalDiplomacy/Script/SetupScriptHandler.cs +++ b/MultiversalDiplomacy/Script/SetupScriptHandler.cs @@ -1,10 +1,5 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text.RegularExpressions; - using MultiversalDiplomacy.Model; -using static MultiversalDiplomacy.Model.OrderRegex; - namespace MultiversalDiplomacy.Script; /// @@ -79,12 +74,13 @@ public class SetupScriptHandler(World world, bool strict = false) : IScriptHandl case "unit": string unitSpec = input["unit ".Length..]; - if (TryParseUnit(unitSpec, out Unit? newUnit)) { + if (OrderRegex.TryParseUnit(World, unitSpec, out Unit? newUnit)) { World = World.Update(units: World.Units.Append(newUnit)); Console.WriteLine($"Created {newUnit}"); - } else if (Strict) { - return null; + return this; } + Console.WriteLine($"Could not match unit spec \"{unitSpec}\""); + if (Strict) return null; break; default: @@ -95,33 +91,4 @@ public class SetupScriptHandler(World world, bool strict = false) : IScriptHandl return this; } - - private bool TryParseUnit(string unitSpec, [NotNullWhen(true)] out Unit? newUnit) - { - newUnit = null; - - Regex reUnit = new($"^{World.Map.PowerRegex} {OrderRegex.Type} {World.Map.ProvinceRegex}(?:{SlashLocation}|{ParenLocation})?$"); - Match match = reUnit.Match(unitSpec); - if (!match.Success) { - Console.WriteLine($"Could not match unit spec \"{unitSpec}\""); - return false; - } - - string power = World.Powers.First(p => p.EqualsAnyCase(match.Groups[1].Value)); - - string typeName = Enum.GetNames().First(name => name.StartsWithAnyCase(match.Groups[2].Value)); - UnitType type = Enum.Parse(typeName); - - Province province = World.Map.Provinces.First(prov - => prov.Name.EqualsAnyCase(match.Groups[3].Value) - || prov.Abbreviations.Any(abv => abv.EqualsAnyCase(match.Groups[3].Value))); - - string locationName = match.Groups[4].Length > 0 ? match.Groups[4].Value : match.Groups[5].Value; - Location location = province.Locations.First(loc - => loc.Name.StartsWithAnyCase(locationName) - || loc.Abbreviation.StartsWithAnyCase(locationName)); - - newUnit = Unit.Build(location.Key, Season.First, power, type); - return true; - } }