Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using AdventLib;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
- namespace AdventOfCode2023;
- /// <summary>
- /// Solver for [day 20](https://adventofcode.com/2023/day/20)
- /// </summary>
- public class Day20 : AdventSolver {
- public override string Day => "day20";
- public override void Solve() {
- //// Part 1
- Dictionary<string, Module> part1Modules = BuildModuleMap();
- long sumLow = 0;
- long sumHigh = 0;
- for (int x = 0; x < 1000; x++) {
- (var low, var high) = CountTriggers(part1Modules, x + 1, []);
- sumLow += low;
- sumHigh += high;
- }
- Console.WriteLine("Solution for part 1 is: " + sumLow + "*" + sumHigh + "=" + (sumLow * sumHigh));
- //// Part 2
- // reload the modules so they reset
- Dictionary<string, Module> part2Modules = BuildModuleMap();
- // Assumes that rx has 1 Source, which is a 4x Conjunction
- Dictionary<string, List<long>> watchlist = [];
- // Get the source for the conjunction
- var conjunction = part2Modules["rx"].Sources[0];
- foreach (var conjSource in part2Modules[conjunction].Sources) {
- // for each input to the conjunction add it to the list
- watchlist.Add(conjSource, []);
- }
- // Run until we have 2 values and can determine the period between them
- for (int x = 0; watchlist.Values.Any(l => l.Count < 2); x++) {
- CountTriggers(part2Modules, x, watchlist);
- }
- // calculate the period between and generate an LCM
- Console.WriteLine("Solution for part 2 is = " + watchlist.Keys.Select(key => watchlist[key][1] - watchlist[key][0]).Lcm());
- }
- public Dictionary<string, Module> BuildModuleMap() {
- List<Module> modules = [];
- // Parse everything
- foreach (string game in ReadInputAsIEnumerable()) {
- if (game.StartsWith("broadcaster")) {
- modules.Add(new BroadcasterModule(game.Split("->")[1].Split(",").Select(s => s.Trim()).ToList()));
- } else if (game.StartsWith("%")) {
- var kvp = game.Substring(1).Split(" -> ");
- modules.Add(new FlipFlopModule(kvp[0], kvp[1].Split(",").Select(s => s.Trim()).ToList()));
- } else if (game.StartsWith("&")) {
- var kvp = game.Substring(1).Split(" -> ");
- modules.Add(new ConjunctionModule(kvp[0], kvp[1].Split(",").Select(s => s.Trim()).ToList()));
- }
- }
- RxModule rx = new();
- modules.Add(new ButtonModule());
- modules.Add(rx);
- Dictionary<string, Module> moduleMap = modules.Select(module => (module.Name, module)).ToDictionary();
- // Register all sources
- foreach (var (key, module) in moduleMap) {
- foreach (var target in module.Targets) {
- if (moduleMap.ContainsKey(target)) {
- moduleMap[target].RegisterSource(key);
- }
- }
- }
- return moduleMap;
- }
- public (long, long) CountTriggers(Dictionary<string, Module> modules, int iteration, Dictionary<string, List<long>> watchlist) {
- long numLow = 0;
- long numHigh = 0;
- Queue<ModuleTrigger> triggers = [];
- triggers.Enqueue(new ModuleTrigger("", "button", true));
- while (triggers.Count > 0) {
- var next = triggers.Dequeue();
- if (modules.ContainsKey(next.Target)) {
- foreach (var trigger in modules[next.Target].Pulse(next.Source, next.IsLowPulse)) {
- //Console.WriteLine("{0} -{1}-> {2}", trigger.source, trigger.IsLowPulse ? "low" : "high", trigger.Target);
- if (watchlist.TryGetValue(next.Source, out List<long>? value) && next.Target == "hf" && !next.IsLowPulse) {
- value.Add(iteration);
- }
- if (trigger.IsLowPulse) {
- numLow++;
- } else {
- numHigh++;
- }
- triggers.Enqueue(trigger);
- }
- }
- }
- return (numLow, numHigh);
- }
- public struct ModuleTrigger(string source, string target, bool isLowPulse) {
- public readonly string Source = source;
- public readonly string Target = target;
- public readonly bool IsLowPulse = isLowPulse;
- }
- public abstract class Module(string name, List<string> targets) {
- public readonly string Name = name;
- public readonly List<string> Targets = targets;
- public readonly List<string> Sources = [];
- public virtual IEnumerable<ModuleTrigger> Pulse(string source, bool isLow) {
- foreach (var target in Targets) {
- yield return new ModuleTrigger(Name, target, isLow);
- }
- }
- public virtual void RegisterSource(string source) {
- Sources.Add(source);
- }
- }
- public class RxModule() : Module("rx", []) {
- public bool hasReceivedLow = false;
- public override IEnumerable<ModuleTrigger> Pulse(string source, bool isLow) {
- if (isLow) hasReceivedLow = true;
- return base.Pulse(source, isLow);
- }
- }
- public class ButtonModule() : Module("button", ["broadcaster"]) {
- }
- public class FlipFlopModule(string name, List<string> targets) : Module(name, targets) {
- private bool lastIsLow = true;
- public override IEnumerable<ModuleTrigger> Pulse(string source, bool isLow) {
- if (isLow) {
- lastIsLow = !lastIsLow;
- // triggered after the flip, so the first time it sends high
- return base.Pulse(source, lastIsLow);
- } else {
- return [];
- }
- }
- }
- public class ConjunctionModule(string name, List<string> targets) : Module(name, targets) {
- private readonly Dictionary<string, bool> SourceStates = [];
- public override void RegisterSource(string name) {
- SourceStates.Add(name, true);
- base.RegisterSource(name);
- }
- public override IEnumerable<ModuleTrigger> Pulse(string source, bool isLow) {
- SourceStates[source] = isLow;
- if (SourceStates.Values.All(lastPulse => !lastPulse)) {
- // triggered after the flip, so the first time it sends high
- return base.Pulse(source, true);
- } else {
- return base.Pulse(source, false);
- }
- }
- }
- public class BroadcasterModule(List<string> targets) : Module("broadcaster", targets) {
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment