5dplomacy/MultiversalDiplomacy/Script/AdjudicationQueryScriptHand...

177 lines
7.3 KiB
C#
Raw Normal View History

2024-09-01 04:54:28 +00:00
using System.Text.RegularExpressions;
using MultiversalDiplomacy.Adjudicate;
2024-09-03 04:25:37 +00:00
using MultiversalDiplomacy.Adjudicate.Decision;
using MultiversalDiplomacy.Model;
2024-09-01 04:54:28 +00:00
using MultiversalDiplomacy.Orders;
namespace MultiversalDiplomacy.Script;
2024-08-28 21:10:41 +00:00
public class AdjudicationQueryScriptHandler(
Action<string> WriteLine,
2024-09-01 04:54:28 +00:00
List<OrderValidation> validations,
2024-09-03 04:25:37 +00:00
List<AdjudicationDecision> adjudications,
2024-08-28 21:10:41 +00:00
World world,
IPhaseAdjudicator adjudicator)
2024-08-28 21:10:41 +00:00
: IScriptHandler
{
public string Prompt => "valid> ";
2024-09-01 04:54:28 +00:00
public List<OrderValidation> Validations { get; } = validations;
2024-09-03 04:25:37 +00:00
public List<AdjudicationDecision> Adjudications { get; } = adjudications;
public World World { get; private set; } = world;
public ScriptResult HandleInput(string input)
{
2024-08-28 15:01:27 +00:00
var args = input.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries);
2024-08-21 16:47:13 +00:00
if (args.Length == 0 || input.StartsWith('#'))
{
return ScriptResult.Succeed(this);
}
var command = args[0];
switch (command)
{
case "---":
2024-08-28 21:10:41 +00:00
WriteLine("Ready for orders");
return ScriptResult.Succeed(new GameScriptHandler(WriteLine, World, adjudicator));
case "assert" when args.Length == 1:
2024-08-28 21:10:41 +00:00
WriteLine("Usage:");
break;
case "assert":
return EvaluateAssertion(args[1]);
case "status":
throw new NotImplementedException();
default:
return ScriptResult.Fail($"Unrecognized command: \"{command}\"", this);
}
return ScriptResult.Succeed(this);
}
2024-08-28 15:01:27 +00:00
private ScriptResult EvaluateAssertion(string assertion)
2024-08-28 15:01:27 +00:00
{
2024-08-28 19:14:19 +00:00
var args = assertion.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries);
switch (args[0])
2024-08-28 15:01:27 +00:00
{
case "true":
return ScriptResult.Succeed(this);
2024-08-28 15:01:27 +00:00
case "false":
return ScriptResult.Fail("assert false", this);
2024-08-28 19:14:19 +00:00
case "order-valid":
case "order-invalid":
2024-09-01 04:54:28 +00:00
OrderParser re = new(World);
Regex prov = new($"^{re.FullLocation}$", RegexOptions.IgnoreCase);
Match match = prov.Match(args[1]);
if (!match.Success) return ScriptResult.Fail($"Could not parse province from \"{args[1]}\"", this);
2024-09-01 04:54:28 +00:00
string timeline = match.Groups[1].Length > 0
? match.Groups[1].Value
: Season.First.Timeline;
var seasonsInTimeline = World.Timelines.Seasons.Where(season => season.Timeline == timeline);
if (!seasonsInTimeline.Any()) return ScriptResult.Fail($"No seasons in timeline {timeline}", this);
2024-09-01 04:54:28 +00:00
int turn = match.Groups[4].Length > 0
? int.Parse(match.Groups[4].Value)
// If turn is unspecified, use the second-latest turn in the timeline,
// since we want to assert against the subjects of the orders just adjudicated,
// and adjudication created a new set of seasons.
: seasonsInTimeline.Max(season => season.Turn) - 1;
Season season = new(timeline, turn);
Province province = World.Map.Provinces.Single(province => province.Is(match.Groups[2].Value));
var matching = Validations.Where(val
=> val.Order is UnitOrder order
&& order.Unit.Season == season
&& World.Map.GetLocation(order.Unit.Location).ProvinceName == province.Name);
if (!matching.Any()) return ScriptResult.Fail("No matching validations");
2024-09-01 04:54:28 +00:00
if (args[0] == "order-valid" && !matching.First().Valid) {
return ScriptResult.Fail($"Order \"{matching.First().Order} is invalid");
}
if (args[0] == "order-invalid" && matching.First().Valid) {
return ScriptResult.Fail($"Order \"{matching.First().Order} is valid");
}
return ScriptResult.Succeed(this);
2024-08-28 19:14:19 +00:00
case "has-past":
2024-09-03 03:49:38 +00:00
Regex hasPast = new($"^([a-z]+[0-9]+)>([a-z]+[0-9]+)$");
Match hpMatch = hasPast.Match(args[1]);
if (!hpMatch.Success) return ScriptResult.Fail("Expected format s1>s2", this);
Season future = new(hpMatch.Groups[1].Value);
if (!World.Timelines.Pasts.TryGetValue(future.Key, out Season? actual)) {
return ScriptResult.Fail($"No such season \"{future}\"");
}
Season expected = new(hpMatch.Groups[2].Value);
if (actual != expected) return ScriptResult.Fail(
$"Expected past of {future} to be {expected}, but it was {actual}");
return ScriptResult.Succeed(this);
2024-08-28 19:14:19 +00:00
case "holds":
// Assert a unit successfully held
case "dislodged":
// Assert a unit was dislodged
case "moves":
case "no-move":
2024-09-03 04:25:37 +00:00
re = new(World);
prov = new($"^{re.FullLocation}$", RegexOptions.IgnoreCase);
match = prov.Match(args[1]);
if (!match.Success) return ScriptResult.Fail($"Could not parse province from \"{args[1]}\"", this);
timeline = match.Groups[1].Length > 0
? match.Groups[1].Value
: Season.First.Timeline;
seasonsInTimeline = World.Timelines.Seasons.Where(season => season.Timeline == timeline);
if (!seasonsInTimeline.Any()) return ScriptResult.Fail($"No seasons in timeline {timeline}", this);
turn = match.Groups[4].Length > 0
? int.Parse(match.Groups[4].Value)
// If turn is unspecified, use the second-latest turn in the timeline,
// since we want to assert against the subjects of the orders just adjudicated,
// and adjudication created a new set of seasons.
: seasonsInTimeline.Max(season => season.Turn) - 1;
season = new(timeline, turn);
province = World.Map.Provinces.Single(province => province.Is(match.Groups[2].Value));
var matchingMoves = Adjudications.Where(adj
=> adj is DoesMove moves
&& moves.Order.Unit.Season == season
&& World.Map.GetLocation(moves.Order.Unit.Location).ProvinceName == province.Name);
if (!matchingMoves.Any()) return ScriptResult.Fail("No matching movement decisions");
var doesMove = matchingMoves.Cast<DoesMove>().First();
if (args[0] == "moves" && doesMove.Outcome != true) {
return ScriptResult.Fail($"Adjudication {doesMove} is false");
}
if (args[0] == "no-move" && doesMove.Outcome != false) {
return ScriptResult.Fail($"Adjudication {doesMove} is true");
}
return ScriptResult.Succeed(this);
2024-08-28 19:14:19 +00:00
case "supports":
// Assert a unit's support was given
case "cut":
// Assert a unit's support was cut
default:
return ScriptResult.Fail($"Unknown assertion \"{args[0]}\"", this);
2024-08-28 15:01:27 +00:00
}
}
}