Quantcast
Channel: Raspberry Pi Forums
Viewing all articles
Browse latest Browse all 5791

Teaching and learning resources • Re: Advent of Code 2023

$
0
0
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.
I haven't got to day 21. Could there be a general approach avoiding the assumptions?
I'm about to start day 21. To me day 20 seemed to require a non-general approach.

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.
This is again a situation where the just-in-time warmup didn't seem to help much. Even so, the time to compute the puzzle solution met the 15 second cutoff.

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()
For a coding point of view I liked the broadcast

Code:

    c=fcycle.(tg.i)
which avoided an explicit loop over the different inputs.

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



Viewing all articles
Browse latest Browse all 5791

Trending Articles