I'm about to start day 21. To me day 20 seemed to require a non-general approach.I haven't got to day 21. Could there be a general approach avoiding the assumptions?Day 21. Solving day 21 part 2 requires that you notice patterns in the input data that don't exist in the test data and use all sorts of assumptions about it. I really don't like this approach and AoC hasn't been much fun this year... mostly just a tedious slog, I'll finish it but this will be my last.
The dog developer took the inputs to the rule producing the rx output and then used something called the Aryabarka algorithm to solve the resulting system of Diophantine equations. It was too complicated for me so I replaced it with a call to Julia's built-in lcm function. I should have known better as it immediately became audible why the solution technique had been so named.
https://en.wikipedia.org/wiki/Aryabhata
Running on a Pi Zero I obtained
Code:
julia> include("day20.jl") # Pi Zero 700 MHzAdvent of Code 2023 Day 20 Pulse PropagationPart 1 The high-low pulse product is 819397964Part 2 The button presses needed are 252667369442479Total execution time 11.897676322 seconds.julia> main()Advent of Code 2023 Day 20 Pulse PropagationPart 1 The high-low pulse product is 819397964Part 2 The button presses needed are 252667369442479Total execution time 10.771361539 seconds.julia> main()Advent of Code 2023 Day 20 Pulse PropagationPart 1 The high-low pulse product is 819397964Part 2 The button presses needed are 252667369442479Total execution time 11.565770154 seconds.
The Julia code was
Code:
#= Advent of Code 2023 Day 20 Pulse Propagation Written 2023 by Eric Olson =#struct DoExit <: Exceptionendfunction gobble(s::String,m::String)::Tuple{String,Bool} if length(s)<length(m) return s,false end if s[1:length(m)]==m return s[length(m)+1:end],true end return s,falseendfunction wskip(s::String)::String for i=1:length(s) if !isspace(s[i]) return s[i:end] end end return ""endfunction vskip(data,i::Int)::Int while i<=length(data)&&length(data[i])==0 i+=1 end return iend function getalpha(s::String)::Tuple{String,String} for i=1:length(s) if !isletter(s[i]) return s[i:end],s[1:i-1] end end return "",sendfunction getint(s::String)::Tuple{String,Int64} n=Int64(0) for i=1:length(s) if s[i]>='0'&&s[i]<='9' n=n*10+Int64(s[i]-'0') else return s[i:end],n end end return "",nend@enum Mtype BCAST=0 FFLOP=1 MCONJ=2mutable struct Mrule a::String # Rule label t::Mtype # Rule type o::Vector{String} # List of output labels v::Vector{Int} # Internal state i::Vector{String} # List of input labels Mrule(a::String,t::Mtype,o::Vector{String})= new(a,t,o,Int[],String[])endmutable struct Node n::Int v::Int a::String b::Stringendfunction mkpriority() heap=Node[] function enqueue(p::Node) push!(heap,p) r=length(heap) while true s=r÷2 if s<1 break end if heap[s].n<=p.n break end heap[r]=heap[s] r=s end heap[r]=p end function dequeue()::Node if length(heap)==0 println("Tried to remove nonexistent point!\n") throw(DoExit()) end t=pop!(heap) if length(heap)==0 return t end p=heap[1] s0=1 while true r0=2*s0; r1=r0+1 if r0>length(heap) break end s1=r0 if r1<=length(heap) if heap[r0].n>heap[r1].n s1=r1 end end if t.n<=heap[s1].n break end heap[s0]=heap[s1] s0=s1 end heap[s0]=t return p end return enqueue,dequeueendfunction getmodule(s::String)::Mrule r=s; a="broadcaster"; t=BCAST if begin s,b=gobble(s,"broadcaster"); b==true end elseif begin s,b=gobble(s,"%"); b==true end s,a=getalpha(s) t=FFLOP elseif begin s,b=gobble(s,"&"); b==true end s,a=getalpha(s) t=MCONJ else println(r,"\nUnknown module type!\n") throw(DoExit()) end s,b=gobble(s," -> ") if b==false println(r,"\nMissing -> in module rule!\n") throw(DoExit()) end oa=String[] while length(s)>0 s,z=getalpha(s) s,b=gobble(s,", ") push!(oa,z) end return Mrule(a,t,oa)endfunction part1(mlist::Dict{String,Mrule})::Int64 enq,deq=mkpriority() n=0; h=0; l=0 function dopress() enq(Node(n,0,"button","broadcaster")); n+=1 l+=1 while length(enq.heap)>0 z=deq() m=get(mlist,z.b,nothing) if m==nothing continue end if m.t==BCAST for o in m.o enq(Node(n,0,z.b,o)); n+=1 l+=1 end elseif m.t==FFLOP if z.v==0 m.v[1]=(m.v[1]+1)%2 for o in m.o enq(Node(n,m.v[1],z.b,o)); n+=1 if m.v[1]==1 h+=1 else l+=1 end end end elseif m.t==MCONJ p=findfirst(x->x==z.a,m.i) if p==nothing println("Unable to find $(z.a) in ", "$(m.i) on rule $(z.b)!") throw(DoExit()) end m.v[p]=z.v r=(prod(m.v)+1)%2 for o in m.o enq(Node(n,r,z.b,o)); n+=1 if r==1 h+=1 else l+=1 end end else println("Unknown module type at ",p.a) throw(DoExit()) end end end for j=1:1000 dopress() end return Int64(h)*lendfunction part2(mlist::Dict{String,Mrule})::Int64 enq,deq=mkpriority() n=0; h=0; l=0 function dopress(a::String)::Bool q=false enq(Node(n,0,"button","broadcaster")); n+=1 l+=1 while length(enq.heap)>0 z=deq() if z.b==a&&z.v==0 q=true end m=get(mlist,z.b,nothing) if m==nothing continue end if m.t==BCAST for o in m.o enq(Node(n,0,z.b,o)); n+=1 l+=1 end elseif m.t==FFLOP if z.v==0 m.v[1]=(m.v[1]+1)%2 for o in m.o enq(Node(n,m.v[1],z.b,o)); n+=1 if m.v[1]==1 h+=1 else l+=1 end end end elseif m.t==MCONJ p=findfirst(x->x==z.a,m.i) if p==nothing println("Unable to find $(z.a) in ", "$(m.i) on rule $(z.b)!") throw(DoExit()) end m.v[p]=z.v r=(prod(m.v)+1)%2 for o in m.o enq(Node(n,r,z.b,o)); n+=1 if r==1 h+=1 else l+=1 end end else println("Unknown module type at ",p.a) throw(DoExit()) end end return q end function fcycle(a::String)::Int64 for (_,r) in mlist r.v.=0 end for j=1:100000 if dopress(a) return j end end println("No cycle found for output $a!") throw(DoExit()) end tg=nothing for (_,k) in mlist if "rx" in k.o tg=k break end end c=fcycle.(tg.i) return lcm(c)endfunction doinit() data=String[] open("day20.txt","r") do fp data=readlines(fp) end mlist=Dict{String,Mrule}() for i=1:length(data) v=getmodule(data[i]) mlist[v.a]=v end for (_,r) in mlist if r.t==FFLOP r.v=[0] end for o in r.o z=get(mlist,o,nothing) if z==nothing continue end push!(z.i,r.a) if z.t==MCONJ push!(z.v,0) end end end p1=part1(mlist) p2=part2(mlist) println("Part 1 The high-low pulse product is ",p1) println("Part 2 The button presses needed are ",p2)endfunction main() t=@elapsed try println("Advent of Code 2023 Day 20 Pulse Propagation\n") doinit() throw(DoExit()) catch r if !isa(r,DoExit) rethrow(r) end end println("\nTotal execution time ",t," seconds.")end main()
Code:
c=fcycle.(tg.i)
Note that the program uses hash tables and strings to represent the graph rather than pointers. I started with pointers, got confused and resorted to indexing with strings with the intention of going back if the code didn't run fast enough. Since the inefficient version ran fast enough, that explains why software engineers shouldn't use fast machines.
Statistics: Posted by ejolson — Thu Jan 25, 2024 8:34 pm