From 356164ec46eaa53958af4514f3f466f37c1407b6 Mon Sep 17 00:00:00 2001 From: Philip Corliss Date: Wed, 20 Dec 2023 16:46:50 -0600 Subject: [PATCH] 2023 Day 20 Part 2 Generalized Solution --- 2023/20/pulses.rb | 58 +++++++++++++++++++++++++++++++++++-- 2023/20/run.rb | 5 +++- 2023/20/spec/pulses_spec.rb | 15 +++++----- 3 files changed, 66 insertions(+), 12 deletions(-) diff --git a/2023/20/pulses.rb b/2023/20/pulses.rb index c225cd0..eb4aa43 100644 --- a/2023/20/pulses.rb +++ b/2023/20/pulses.rb @@ -25,6 +25,7 @@ def initialize(input) @instructions[name] = [type.to_sym, *inputs] end + @instructions.each do |name, (t, *outs)| outs.each do |out| if @instructions[out] && @instructions[out].first == :& @@ -32,11 +33,19 @@ def initialize(input) end end end + + @reverse_map = {} + @instructions.each do |name, (t, *outs)| + outs.each do |out| + @reverse_map[out] ||= [] + @reverse_map[out] << name + end + end end LOW_HIGH = ['-low', '-high'] - def send_pulse(name, from, value = 0) + def send_pulse(name, from, value = 0, idx = -1, block = nil) puts "#{from} #{LOW_HIGH[value]}-> #{name}" if @debug pulse_count = [0, 0] queue = [[name, from, value]] @@ -45,6 +54,10 @@ def send_pulse(name, from, value = 0) n, f, v = queue.shift t, *outs = @instructions[n] pulse_count[v] += 1 + + if block + block.call(n, f, v, idx) + end # puts "Handling #{t} #{n} from #{f}, received #{v}" if @debug # binding.pry if @debug case t @@ -81,6 +94,7 @@ def send_pulse(name, from, value = 0) if n == 'output' @out ||= [] @out << v + elsif n == 'rx' else # raise "Unknown type #{t}" puts "WARN: Unknown type #{t} for #{n}" if @debug @@ -91,8 +105,8 @@ def send_pulse(name, from, value = 0) pulse_count end - def button! - send_pulse('broadcaster', 'button', 0) + def button!(idx = -1, &block) + send_pulse('broadcaster', 'button', 0, idx, block) end def pulse_mult(cycles) @@ -105,6 +119,44 @@ def pulse_mult(cycles) sum[0] * sum[1] end + def state_diagram + sd = '' + sd << "stateDiagram-v2" << "\n" + @instructions.each do |name, (t, *outs)| + outs.each do |out| + sd << "#{name} --> #{out}" << "\n" + end + end + + # Output is usable with https://mermaid.live/ + # Reveals a diagram where 4 paths with about 12-bits of state + # Certain bits need to be on in order to send the right pulse to the aggregator + sd + end + + def rx_cycles + # detect modules that are connected to the rx module + # Assumes a modules -> aggregator_module -> rx relationship + aggregator_module = @reverse_map['rx'].first + feeder_modules = @reverse_map[aggregator_module] + feeder_cycle_map = feeder_modules.map {|k| [k,0]}.to_h + i = 0 + until feeder_cycle_map.values.all? { |v| v > 0 } do + i += 1 + raise "Too many cycles #{i}" if i > 2**12 + + # Normally I'd just check the state after pressing the button + # But the pulse we're looking for gets set and reset in the same cycle + button!(i) do |name, from, value, idx| + if value == 1 && name == aggregator_module && feeder_cycle_map[from] == 0 + feeder_cycle_map[from] = idx + end + end + end + + feeder_cycle_map.values.inject(:lcm) + end + def debug! @debug = true end diff --git a/2023/20/run.rb b/2023/20/run.rb index 7955c0b..362c01d 100755 --- a/2023/20/run.rb +++ b/2023/20/run.rb @@ -5,4 +5,7 @@ input = File.read('./input.txt') ad = Advent::Pulses.new(input) -puts "Part 1: #{ad.pulse_mult(1000)}" \ No newline at end of file +puts "Part 1: #{ad.pulse_mult(1000)}" + +ad = Advent::Pulses.new(input) +puts "Part 2: #{ad.rx_cycles}" \ No newline at end of file diff --git a/2023/20/spec/pulses_spec.rb b/2023/20/spec/pulses_spec.rb index 03086f0..2c14cf7 100644 --- a/2023/20/spec/pulses_spec.rb +++ b/2023/20/spec/pulses_spec.rb @@ -110,7 +110,7 @@ describe "#button!" do it "sends a broadcast pulse" do - expect(ad).to receive(:send_pulse).with('broadcaster', 'button', 0) + expect(ad).to receive(:send_pulse).with('broadcaster', 'button', 0, anything, anything) ad.button! expect(ad.state['a']).to eq(0) expect(ad.state['b']).to eq(0) @@ -180,14 +180,13 @@ end end -# button -low-> broadcaster -# broadcaster -low-> a -# a -low-> inv -# a -low-> con -# inv -high-> b -# con -high-> output + describe "#rx_cycles" do + let(:input) { File.read('./input.txt') } - context "validation" do + it "returns the number of cycles to send a low pulse to rx" do + # ad.debug! + expect(ad.rx_cycles).to eq(244178746156661) + end end end end