Advertisement
mgla

Advent of Code 2023 - Day 20

Dec 20th, 2023 (edited)
1,025
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 6.17 KB | None | 0 0
  1. var input = File.ReadAllLines("input.txt");
  2.  
  3. var modules = new Dictionary<string, IModule>();
  4.  
  5. foreach (var line in input)
  6. {
  7.     var split = line.Split(" -> ");
  8.     var name = split[0];
  9.     var targets = split[1].Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToList();
  10.  
  11.     if (name == "broadcaster")
  12.     {
  13.         modules[name] = new Broadcaster(name, targets);
  14.     }
  15.     else if (name.StartsWith('%'))
  16.     {
  17.         modules[name[1..]] = new FlipFlop(name[1..], targets);
  18.     }
  19.     else if (name.StartsWith('&'))
  20.     {
  21.         modules[name[1..]] = new Conjunction(name[1..], targets);
  22.     }
  23. }
  24.  
  25. foreach (var module in modules.Values)
  26. {
  27.     if (module is not Conjunction conjunction) continue;
  28.  
  29.     var inputs = modules.Values.Where(m => m.Targets.ContainsKey(module.Name)).ToList();
  30.     foreach (var inputModule in inputs)
  31.     {
  32.         conjunction.Inputs[inputModule.Name] = PulseType.Low;
  33.     }
  34. }
  35.  
  36. var lowPulses = 0;
  37. var highPulses = 0;
  38. var queue = new Queue<(IModule Module, PulseType PulseType)>();
  39. var currentPulse = PulseType.Low;
  40. var count = 0;
  41.  
  42. // The module that sends pulses to rx - it's a conjunction.
  43. // In order to send a low pulse to rx, all of its inputs must be sending a high pulse.
  44. var rxSource = modules.Values.First(m => m.Targets.ContainsKey("rx"));
  45.  
  46. // The modules that send pulses to the conjunction that sends pulses to rx.
  47. var rxSourceInputs = modules.Values.Where(m => m.Targets.ContainsKey(rxSource.Name)).ToList();
  48.  
  49. // We will count how many pushes are needed for each module to send out a high pulse.
  50. var rxSourceInputCounts = new Dictionary<string, (int Count, bool Done)>();
  51. foreach (var rxSourceModule in rxSourceInputs)
  52. {
  53.     rxSourceInputCounts[rxSourceModule.Name] = (0, false);
  54. }
  55.  
  56. while (true)
  57. {
  58.     if (count == 1000)
  59.     {
  60.         Console.WriteLine($"Part 1: {lowPulses * highPulses}");
  61.     }
  62.  
  63.     count++;
  64.  
  65.     queue.Enqueue((modules["broadcaster"], currentPulse));
  66.  
  67.     if (currentPulse == PulseType.Low)
  68.     {
  69.         lowPulses++;
  70.     }
  71.     else
  72.     {
  73.         highPulses++;
  74.     }
  75.  
  76.     if (rxSourceInputCounts.All(rxs => rxs.Value.Done))
  77.     {
  78.         // We know that all of the inputs to the conjunction that sends pulses to rx have sent out a high pulse.
  79.         // Now we just find the LCM of the counts for each input in order to find the first time that all of the inputs
  80.         // have sent out a high pulse, which in turn would cause rx to receive a low pulse.
  81.         Console.WriteLine($"Part 2: {Lcm(rxSourceInputCounts.Select(rxs => rxs.Value.Count).ToArray())}");
  82.         break;
  83.     }
  84.  
  85.     while (queue.TryDequeue(out var current))
  86.     {
  87.         current.Module.Process(current.PulseType);
  88.  
  89.         foreach (var target in current.Module.Targets.Keys)
  90.         {
  91.             var targetPulse = current.Module.Targets[target];
  92.  
  93.             if (target == "rx")
  94.             {
  95.                 if (current.Module is not Conjunction conjunction) continue;
  96.  
  97.                 var highInputs = conjunction.Inputs.Where(conjunctionInput => conjunctionInput.Value == PulseType.High);
  98.                 foreach (var conjunctionInput in highInputs)
  99.                 {
  100.                     // The module is sending a high pulse to rx, so store its count.
  101.                     rxSourceInputCounts[conjunctionInput.Key] = (count, true);
  102.                 }
  103.             }
  104.  
  105.             switch (targetPulse)
  106.             {
  107.                 case PulseType.Low:
  108.                     lowPulses++;
  109.                     break;
  110.                 case PulseType.High:
  111.                     highPulses++;
  112.                     break;
  113.                 case PulseType.None:
  114.                 default:
  115.                     break;
  116.             }
  117.  
  118.             if (targetPulse != PulseType.None)
  119.             {
  120.                 if (!modules.ContainsKey(target))
  121.                 {
  122.                     continue;
  123.                 }
  124.  
  125.                 if (modules[target] is Conjunction conjunction)
  126.                 {
  127.                     conjunction.Inputs[current.Module.Name] = targetPulse;
  128.                 }
  129.  
  130.                 queue.Enqueue((modules[target], targetPulse));
  131.             }
  132.  
  133.             if (queue.Count == 0)
  134.             {
  135.                 currentPulse = current.PulseType == PulseType.Low ? PulseType.High : PulseType.Low;
  136.             }
  137.         }
  138.     }
  139. }
  140.  
  141. return;
  142.  
  143. static long Lcm(IEnumerable<int> numbers) => numbers.Select(Convert.ToInt64).Aggregate((a, b) => a * b / Gcd(a, b));
  144.  
  145. static long Gcd(long a, long b) => b == 0 ? a : Gcd(b, a % b);
  146.  
  147. internal interface IModule
  148. {
  149.     string Name { get; set; }
  150.     Dictionary<string, PulseType> Targets { get; set; }
  151.     void Process(PulseType pulse);
  152. }
  153.  
  154. internal class Broadcaster : IModule
  155. {
  156.     public Broadcaster(string name, List<string> targets)
  157.     {
  158.         Name = name;
  159.  
  160.         foreach (var target in targets)
  161.         {
  162.             Targets[target] = PulseType.Low;
  163.         }
  164.     }
  165.  
  166.     public string Name { get; set; }
  167.     public Dictionary<string, PulseType> Targets { get; set; } = new();
  168.  
  169.     public virtual void Process(PulseType pulse)
  170.     {
  171.         foreach (var name in Targets.Keys)
  172.         {
  173.             Targets[name] = pulse;
  174.         }
  175.     }
  176. }
  177.  
  178. internal class FlipFlop(string name, List<string> targets) : Broadcaster(name, targets)
  179. {
  180.     public bool PoweredOn { get; set; }
  181.  
  182.     public override void Process(PulseType pulse)
  183.     {
  184.         var output = PulseType.None;
  185.  
  186.         if (pulse == PulseType.Low)
  187.         {
  188.             output = PoweredOn ? PulseType.Low : PulseType.High;
  189.             PoweredOn = !PoweredOn;
  190.         }
  191.  
  192.         foreach (var targetName in Targets.Keys)
  193.         {
  194.             Targets[targetName] = output;
  195.         }
  196.     }
  197. }
  198.  
  199. internal class Conjunction(string name, List<string> targets) : Broadcaster(name, targets)
  200. {
  201.     public Dictionary<string, PulseType> Inputs { get; set; } = new();
  202.  
  203.     public override void Process(PulseType pulse)
  204.     {
  205.         foreach (var name in Targets.Keys)
  206.         {
  207.             Targets[name] = Inputs.Values.All(x => x == PulseType.High) ? PulseType.Low : PulseType.High;
  208.         }
  209.     }
  210. }
  211.  
  212. internal enum PulseType
  213. {
  214.     None,
  215.     Low,
  216.     High
  217. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement