diff --git a/advent-of-code-2023.csproj b/advent-of-code-2023.csproj index 0a674b7..18907a5 100644 --- a/advent-of-code-2023.csproj +++ b/advent-of-code-2023.csproj @@ -70,6 +70,9 @@ + + + diff --git a/inputs/20.txt b/inputs/20.txt new file mode 100644 index 0000000..5e74bfd --- /dev/null +++ b/inputs/20.txt @@ -0,0 +1,58 @@ +%hm -> lg +%ph -> gr, cd +%xs -> st, zf +%xc -> vv, bn +%fx -> lx, st +%dn -> zc +%nd -> lg, tc +%pd -> dn, lg +%pc -> gr +%lx -> gp +%tc -> kj +%tl -> nn +%kk -> xs +%gq -> pd +%xq -> st, xj +%nn -> bn, xc +&st -> kk, fx, hz, lx, zb +&hb -> rx +%xj -> st, vq +%sz -> gr, ph +%bz -> kx +%vq -> st +%vv -> hh, bn +%gp -> st, hz +&js -> hb +%lf -> bn, qg +broadcaster -> nd, fx, mc, lf +%cd -> vr +%vr -> qc, gr +%kx -> kc, lg +%jr -> bn, tl +&gr -> cz, dh, mc, qc, js, nj, cd +%qg -> hq +%mc -> gr, cz +%nl -> st, ch +%hz -> nl +%kt -> gr, jc +%zc -> lg, qn +%vj -> rs +&zb -> hb +%kc -> gq +%qc -> kt +&bn -> qg, hq, rs, lf, bs, vj, tl +%cz -> dh +&bs -> hb +%jc -> gr, pc +%nj -> sz +%kj -> lg, bz +%hh -> jv, bn +%hq -> vj +%dh -> nj +%ch -> st, kk +%jv -> bn +%rs -> jr +%zf -> xq, st +%qn -> hm, lg +&lg -> gq, bz, tc, nd, rr, kc, dn +&rr -> hb \ No newline at end of file diff --git a/inputs/20a.txt b/inputs/20a.txt new file mode 100644 index 0000000..9ed10dd --- /dev/null +++ b/inputs/20a.txt @@ -0,0 +1,5 @@ +broadcaster -> a, b, c +%a -> b +%b -> c +%c -> inv +&inv -> a \ No newline at end of file diff --git a/inputs/20b.txt b/inputs/20b.txt new file mode 100644 index 0000000..4da4379 --- /dev/null +++ b/inputs/20b.txt @@ -0,0 +1,5 @@ +broadcaster -> a +%a -> inv, con +&inv -> b +%b -> con +&con -> output \ No newline at end of file diff --git a/src/20.cs b/src/20.cs new file mode 100644 index 0000000..44e9f87 --- /dev/null +++ b/src/20.cs @@ -0,0 +1,191 @@ +namespace aoc2023; + +internal class Day20 : Day +{ + private record command(bool state, string source, string target); + + abstract class module(string n, IList o) + { + protected readonly string name = n; + public readonly List outputs = [..o]; + + public abstract void Signal(string source, bool state, Queue queue); + public abstract void Reset(); + } + + private class flipflop(string n, IList o) : module(n, o) + { + private bool state; + + public override void Signal(string source, bool pulse, Queue queue) + { + if (pulse) + { + return; + } + + state = !state; + foreach (var r in outputs) + { + queue.Enqueue(new command(state, name, r)); + } + } + + public override void Reset() + { + state = false; + } + } + + private class conjunction(string n, IList o) : module(n, o) + { + public readonly Dictionary inputs = []; + + public override void Signal(string source, bool state, Queue queue) + { + inputs[source] = state; + bool send = !inputs.All(r => r.Value); + + foreach (var r in outputs) + { + queue.Enqueue(new command(send, name, r)); + } + } + + public override void Reset() + { + foreach (var i in inputs) + { + inputs[i.Key] = false; + } + } + } + + private readonly Dictionary modules = []; + private readonly List broadcastReceivers = []; + + internal override void Parse() + { + var lines = Util.Parsing.ReadAllLines($"{GetDay()}"); + foreach (var line in lines) + { + var split = line.Split(" -> "); + if (split[0].StartsWith('%')) + { + var n = split[0][1..]; + var receivers = split[1].Split(", "); + modules.Add(n, new flipflop(n, receivers)); + } + else if (split[0].StartsWith('&')) + { + var n = split[0][1..]; + var receivers = split[1].Split(", "); + modules.Add(n, new conjunction(n, receivers)); + } + else if (split[0] == "broadcaster") + { + broadcastReceivers.AddRange(split[1].Split(", ")); + } + else + { + throw new Exception(); + } + } + + foreach (var m in modules.Where(m => m.Value is conjunction)) + { + foreach (var i in modules.Where(m2 => m2.Value.outputs.Contains(m.Key))) + { + (m.Value as conjunction)!.inputs.TryAdd(i.Key, false); + } + } + } + + internal override string Part1() + { + modules.ForEach(m => m.Value.Reset()); + Queue q = []; + + long lows = 0; + long highs = 0; + for (int i = 0; i < 1000; i++) + { + lows++; + foreach (var r in broadcastReceivers) + { + q.Enqueue(new command(false, "broadcaster", r)); + } + + while (q.TryDequeue(out command? result)) + { + if (result.state) + { + highs++; + } + else + { + lows++; + } + + if (modules.TryGetValue(result.target, out module? value)) + { + value.Signal(result.source, result.state, q); + } + } + } + + return $"{lows} low signals * {highs} high signals = <+white>{lows*highs}"; + } + + internal override string Part2() + { + modules.ForEach(m => m.Value.Reset()); + Queue q = []; + + var rxin = modules.First(m => m.Value.outputs.Contains("rx")); + var rxinin = modules.Where(m => m.Value.outputs.Contains(rxin.Key)).ToDictionary(); + Dictionary loops = []; + + bool seenRx = false; + long presses = 0; + while (!seenRx) + { + presses++; + foreach (var r in broadcastReceivers) + { + q.Enqueue(new command(false, "broadcaster", r)); + } + + while (q.TryDequeue(out command? result)) + { + if (result is {target: "rx", state: false}) + { + seenRx = true; + break; + } + + if (!modules.TryGetValue(result.target, out module? value)) + { + continue; + } + + if (!result.state && rxinin.Any(m => m.Key == result.target)) + { + if (loops.TryAdd(result.target, presses)) + { + if (loops.Count == rxinin.Count) + { + seenRx = true; + break; + } + } + } + + value.Signal(result.source, result.state, q); + } + } + + // technically this should use LCM, but they're all prime. + return $"rx will receive a low pulse after <+white>{loops.Aggregate(1L, (curr, m) => curr * m.Value)}<+black> button presses"; + } +}