Rule.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. using System.Text.RegularExpressions;
  2. namespace Day19;
  3. public partial class Rule
  4. {
  5. [GeneratedRegex(@"(\w)([<>])(\d+):(\w+)|(\w+)")]
  6. private partial Regex ItemMatch();
  7. private readonly IList<IRuleItem> _items;
  8. public Rule(string ruleText)
  9. {
  10. _items = ItemMatch().Matches(ruleText).Select<Match, IRuleItem>(m =>
  11. {
  12. if (m.Groups[5].Length > 0)
  13. {
  14. return new Terminal(m.Groups[5].Value);
  15. }
  16. return new Item(m.Groups[1].Value, m.Groups[2].Value, int.Parse(m.Groups[3].Value), m.Groups[4].Value);
  17. }).ToList();
  18. }
  19. public string Apply(Part p)
  20. {
  21. foreach (var item in _items)
  22. {
  23. //Console.WriteLine(item);
  24. var next = item.Apply(p);
  25. //Console.WriteLine($"=> {next}");
  26. if (next != null)
  27. {
  28. return next;
  29. }
  30. }
  31. throw new Exception("Rule should always match");
  32. }
  33. public IEnumerable<RuleResultRange> Apply(PartRange p)
  34. {
  35. var range = p;
  36. foreach (var item in _items)
  37. {
  38. var result = item.Apply(range).ToList();
  39. yield return result.First();
  40. if (result.Count > 1)
  41. {
  42. range = result.Last().Part;
  43. }
  44. }
  45. }
  46. private interface IRuleItem
  47. {
  48. string? Apply(Part p);
  49. IEnumerable<RuleResultRange> Apply(PartRange p);
  50. }
  51. private class Item : IRuleItem
  52. {
  53. private readonly string _property;
  54. private readonly string _op;
  55. private readonly int _value;
  56. private readonly string _next;
  57. public Item(string property, string op, int value, string next)
  58. {
  59. _property = property;
  60. _op = op;
  61. _value = value;
  62. _next = next;
  63. }
  64. public string? Apply(Part p)
  65. {
  66. if (_op == "<")
  67. {
  68. return p.Get(_property) < _value ? _next : null;
  69. }
  70. if (_op == ">")
  71. {
  72. return p.Get(_property) > _value ? _next : null;
  73. }
  74. return null;
  75. }
  76. public IEnumerable<RuleResultRange> Apply(PartRange p)
  77. {
  78. var range = p.Get(_property);
  79. if (_op == "<")
  80. {
  81. yield return new RuleResultRange(
  82. p.Update(_property, range.LessThan(_value)),
  83. _next
  84. );
  85. yield return new RuleResultRange(
  86. p.Update(_property, range.GreaterThan(_value - 1)),
  87. null
  88. );
  89. }
  90. if (_op == ">")
  91. {
  92. yield return new RuleResultRange(
  93. p.Update(_property, range.GreaterThan(_value)),
  94. _next
  95. );
  96. yield return new RuleResultRange(
  97. p.Update(_property, range.LessThan(_value + 1)),
  98. null
  99. );
  100. }
  101. }
  102. public override string ToString()
  103. {
  104. return $"{_property} {_op} {_value} : {_next}";
  105. }
  106. }
  107. private class Terminal : IRuleItem
  108. {
  109. private readonly string _next;
  110. public Terminal(string next)
  111. {
  112. _next = next;
  113. }
  114. public string? Apply(Part p)
  115. {
  116. return _next;
  117. }
  118. public IEnumerable<RuleResultRange> Apply(PartRange p)
  119. {
  120. yield return new RuleResultRange(p, _next);
  121. }
  122. public override string ToString()
  123. {
  124. return $"Terminal<{_next}>";
  125. }
  126. }
  127. }