5dplomacy/MultiversalDiplomacy/Script/AdjudicationQueryScriptHand...

138 lines
4.4 KiB
C#

using System.Text.RegularExpressions;
using MultiversalDiplomacy.Adjudicate;
using MultiversalDiplomacy.Model;
using MultiversalDiplomacy.Orders;
namespace MultiversalDiplomacy.Script;
public class AdjudicationQueryScriptHandler(
Action<string> WriteLine,
List<OrderValidation> validations,
World world,
IPhaseAdjudicator adjudicator,
bool strict = false)
: IScriptHandler
{
public string Prompt => "valid> ";
public List<OrderValidation> Validations { get; } = validations;
public World World { get; private set; } = world;
/// <summary>
/// Whether unsuccessful commands should terminate the script.
/// </summary>
public bool Strict { get; } = strict;
public IScriptHandler? HandleInput(string input)
{
var args = input.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries);
if (args.Length == 0 || input.StartsWith('#'))
{
return this;
}
var command = args[0];
switch (command)
{
case "---":
WriteLine("Ready for orders");
return new GameScriptHandler(WriteLine, World, adjudicator, Strict);
case "assert" when args.Length == 1:
WriteLine("Usage:");
break;
case "assert":
if (!EvaluateAssertion(args[1])) return Strict ? null : this;
break;
case "status":
throw new NotImplementedException();
default:
WriteLine($"Unrecognized command: \"{command}\"");
if (Strict) return null;
break;
}
return this;
}
private bool EvaluateAssertion(string assertion)
{
var args = assertion.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries);
switch (args[0])
{
case "true":
return true;
case "false":
return false;
case "order-valid":
case "order-invalid":
OrderParser re = new(World);
Regex prov = new($"^{re.FullLocation}$", RegexOptions.IgnoreCase);
Match match = prov.Match(args[1]);
if (!match.Success) {
WriteLine($"Could not parse province from \"{args[1]}\"");
return !Strict;
}
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 false;
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 false;
return args[0] == "order-valid"
? matching.First().Valid
: !matching.First().Valid;
case "has-past":
// Assert a timeline's past
case "holds":
// Assert a unit successfully held
case "dislodged":
// Assert a unit was dislodged
case "moves":
// Assert a unit successfully moved
case "no-move":
// Assert a unit did not move
case "supports":
// Assert a unit's support was given
case "cut":
// Assert a unit's support was cut
default:
WriteLine($"Unknown assertion \"{args[0]}\"");
return !Strict;
}
}
}