using System.Text.RegularExpressions; namespace Day19; public partial class Rule { [GeneratedRegex(@"(\w)([<>])(\d+):(\w+)|(\w+)")] private partial Regex ItemMatch(); private readonly IList _items; public Rule(string ruleText) { _items = ItemMatch().Matches(ruleText).Select(m => { if (m.Groups[5].Length > 0) { return new Terminal(m.Groups[5].Value); } return new Item(m.Groups[1].Value, m.Groups[2].Value, int.Parse(m.Groups[3].Value), m.Groups[4].Value); }).ToList(); } public string Apply(Part p) { foreach (var item in _items) { //Console.WriteLine(item); var next = item.Apply(p); //Console.WriteLine($"=> {next}"); if (next != null) { return next; } } throw new Exception("Rule should always match"); } public IEnumerable Apply(PartRange p) { var range = p; foreach (var item in _items) { var result = item.Apply(range).ToList(); yield return result.First(); if (result.Count > 1) { range = result.Last().Part; } } } private interface IRuleItem { string? Apply(Part p); IEnumerable Apply(PartRange p); } private class Item : IRuleItem { private readonly string _property; private readonly string _op; private readonly int _value; private readonly string _next; public Item(string property, string op, int value, string next) { _property = property; _op = op; _value = value; _next = next; } public string? Apply(Part p) { if (_op == "<") { return p.Get(_property) < _value ? _next : null; } if (_op == ">") { return p.Get(_property) > _value ? _next : null; } return null; } public IEnumerable Apply(PartRange p) { var range = p.Get(_property); if (_op == "<") { yield return new RuleResultRange( p.Update(_property, range.LessThan(_value)), _next ); yield return new RuleResultRange( p.Update(_property, range.GreaterThan(_value - 1)), null ); } if (_op == ">") { yield return new RuleResultRange( p.Update(_property, range.GreaterThan(_value)), _next ); yield return new RuleResultRange( p.Update(_property, range.LessThan(_value + 1)), null ); } } public override string ToString() { return $"{_property} {_op} {_value} : {_next}"; } } private class Terminal : IRuleItem { private readonly string _next; public Terminal(string next) { _next = next; } public string? Apply(Part p) { return _next; } public IEnumerable Apply(PartRange p) { yield return new RuleResultRange(p, _next); } public override string ToString() { return $"Terminal<{_next}>"; } } }