2023 Day 20 Part 1
This commit is contained in:
parent
afcaba41eb
commit
680e168478
1 changed files with 210 additions and 0 deletions
210
2023/day20.rb
Normal file
210
2023/day20.rb
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
# frozen_string_literal: true
|
||||
# typed: true
|
||||
|
||||
require 'sorbet-runtime'
|
||||
require 'sorbet-struct-comparable'
|
||||
|
||||
module Day20
|
||||
class Pulse < T::Enum
|
||||
enums do
|
||||
High = new
|
||||
Low = new
|
||||
end
|
||||
end
|
||||
|
||||
class Wire < T::Struct
|
||||
include T::Struct::ActsAsComparable
|
||||
|
||||
const :src, String
|
||||
const :dst, String
|
||||
const :pulse, Pulse
|
||||
end
|
||||
|
||||
class Module
|
||||
extend T::Sig
|
||||
extend T::Helpers
|
||||
|
||||
abstract!
|
||||
|
||||
sig { abstract.returns(String) }
|
||||
def name; end
|
||||
|
||||
sig { abstract.returns(T::Array[String]) }
|
||||
def dests; end
|
||||
|
||||
sig { abstract.params(source: String, pulse: Pulse).returns(T::Array[Wire]) }
|
||||
def handle(source, pulse); end
|
||||
end
|
||||
|
||||
class Broadcaster < Module
|
||||
extend T::Sig
|
||||
|
||||
sig { override.returns(String) }
|
||||
attr_reader :name
|
||||
|
||||
sig { override.returns(T::Array[String]) }
|
||||
attr_reader :dests
|
||||
|
||||
sig { params(name: String, dests: T::Array[String]).void }
|
||||
def initialize(name, dests)
|
||||
@name = name
|
||||
@dests = dests
|
||||
end
|
||||
|
||||
sig { override.params(source: String, pulse: Pulse).returns(T::Array[Wire]) }
|
||||
def handle(source, pulse)
|
||||
@dests.map do |dst|
|
||||
Wire::new(src: @name, dst: dst, pulse: pulse)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class FlipFlop < Module
|
||||
extend T::Sig
|
||||
|
||||
sig { override.returns(String) }
|
||||
attr_reader :name
|
||||
|
||||
sig { override.returns(T::Array[String]) }
|
||||
attr_reader :dests
|
||||
|
||||
sig { params(name: String, dests: T::Array[String]).void }
|
||||
def initialize(name, dests)
|
||||
@name = name
|
||||
@dests = dests
|
||||
@inputs = T.let([], T::Array[String])
|
||||
@on = false
|
||||
end
|
||||
|
||||
sig { override.params(source: String, pulse: Pulse).returns(T::Array[Wire]) }
|
||||
def handle(source, pulse)
|
||||
return [] if pulse == Pulse::High
|
||||
|
||||
if @on
|
||||
@on = false
|
||||
output = Pulse::Low
|
||||
else
|
||||
@on = true
|
||||
output = Pulse::High
|
||||
end
|
||||
|
||||
@dests.map do |dst|
|
||||
Wire::new(src: @name, dst: dst, pulse: output)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Conjunction < Module
|
||||
extend T::Sig
|
||||
|
||||
sig { override.returns(String) }
|
||||
attr_reader :name
|
||||
|
||||
sig { override.returns(T::Array[String]) }
|
||||
attr_reader :dests
|
||||
|
||||
sig { override.params(name: String, dests: T::Array[String]).void }
|
||||
def initialize(name, dests)
|
||||
@name = name
|
||||
@dests = dests
|
||||
@inputs = T.let({}, T::Hash[String, Pulse])
|
||||
end
|
||||
|
||||
sig { params(name: String).void }
|
||||
def register_input(name)
|
||||
@inputs[name] = Pulse::Low
|
||||
end
|
||||
|
||||
sig { override.params(source: String, pulse: Pulse).returns(T::Array[Wire]) }
|
||||
def handle(source, pulse)
|
||||
@inputs[source] = pulse
|
||||
|
||||
output = if @inputs.all? { |k, v| v == Pulse::High }
|
||||
Pulse::Low
|
||||
else
|
||||
Pulse::High
|
||||
end
|
||||
|
||||
@dests.map do |dst|
|
||||
Wire::new(src: @name, dst: dst, pulse: output)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Machine
|
||||
extend T::Sig
|
||||
|
||||
def initialize(text)
|
||||
@modules = T.let({}, T::Hash[String, Module])
|
||||
|
||||
text.strip.split("\n").each do |line|
|
||||
name, dests = line.split('->').map(&:strip)
|
||||
dests = dests.split(',').map(&:strip)
|
||||
|
||||
mod = if name[0] == '%'
|
||||
FlipFlop::new(name[1..name.length], dests)
|
||||
elsif name[0] == '&'
|
||||
Conjunction::new(name[1..name.length], dests)
|
||||
elsif name == 'broadcaster'
|
||||
Broadcaster::new(name, dests)
|
||||
else
|
||||
raise StandardError, name
|
||||
end
|
||||
|
||||
@modules[mod.name] = mod
|
||||
end
|
||||
|
||||
@modules.each_pair do |src_name, src_mod|
|
||||
src_mod.dests.each do |dst_name|
|
||||
dst = @modules[dst_name]
|
||||
if dst.is_a? Conjunction
|
||||
dst.register_input(src_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T::Hash[Pulse, Integer] )}
|
||||
def push_button
|
||||
counts = {
|
||||
Pulse::Low => 0,
|
||||
Pulse::High => 0,
|
||||
}
|
||||
|
||||
queue = T.let([], T::Array[Wire])
|
||||
queue << Wire::new(src: 'button', dst: 'broadcaster', pulse: Pulse::Low)
|
||||
|
||||
while !queue.empty?
|
||||
wire = T.must(queue.shift)
|
||||
counts[wire.pulse] += 1
|
||||
|
||||
mod = @modules[wire.dst] or next
|
||||
mod.handle(wire.src, wire.pulse).each do |output|
|
||||
queue << output
|
||||
end
|
||||
end
|
||||
|
||||
counts
|
||||
end
|
||||
end
|
||||
|
||||
class << self
|
||||
extend T::Sig
|
||||
|
||||
sig { params(path: String).returns(Integer) }
|
||||
def part1(path)
|
||||
text = File.read(path)
|
||||
machine = Machine::new(text)
|
||||
low = high = 0
|
||||
1_000.times do
|
||||
pulses = machine.push_button
|
||||
high += pulses.fetch(Pulse::High)
|
||||
low += pulses.fetch(Pulse::Low)
|
||||
end
|
||||
high * low
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
puts Day20::part1 File.join(__dir__, 'input/test.txt')
|
||||
puts Day20::part1 File.join(__dir__, 'input/day20.txt')
|
||||
Loading…
Reference in a new issue