Day 17
file:test/Day17Spec.jl
# add tests
file:src/Day17.jl
module Day17
using ..Monads
using ..Parsing
struct Input
::Int
a::Int
b::Int
c::Vector{Int}
programend
function compile(program::Vector{Int})
function combo(n::Int)
<= 3 && return n
n == 4 && return :a
n == 5 && return :b
n == 6 && return :c
n throw("illegal operand")
end
adv(x::Int) = :(a >>= $(combo(x)))
bxl(x::Int) = :(b ⊻= $(x))
bst(x::Int) = :(b = $(combo(x)) % 8)
jnz(x::Int) = :(a != 0 && @goto $(Symbol("pos", x)))
bxc(x::Int) = :(b ⊻= c)
out(x::Int) = :(push!(out, $(combo(x)) % 8))
bdv(x::Int) = :(b = a >> $(combo(x)))
cdv(x::Int) = :(c = a >> $(combo(x)))
instruction(opcode, operand) =
+1](operand)
[adv, bxl, bst, jnz, bxc, out, bdv, cdv][opcode
= reshape(program, (2, :))
instr = instr[2,instr[1,:] .== 3]
gotos = []
code for (i, (opcode, operand)) in enumerate(eachcol(instr))
if i - 1 ∈ gotos
push!(code, :(@label $(Symbol("pos", i - 1))))
end
push!(code, instruction(opcode, operand))
end
:(function (a, b, c)
= Int[]
out $(code...)
return out
end)
end
function find_input(rev_prog::Vector{Int}, a)
isempty(rev_prog) && return a
for q in 0:7
= a << 3 + q
a_next = q ⊻ 1
b = (a_next >> b) % 8
c if b ⊻ c ⊻ 6 == rev_prog[1]
= find_input(rev_prog[2:end], a_next)
solution !== nothing && return solution
solution end
end
return nothing
end
function read_input(io::IO)
register(a) = token(match_p("Register $a:")) >>> integer
= token(match_p("Program:")) >>> sep_by_p(integer, match_p(","))
program = fmap(splat(Input), sequence(
input register("A"), register("B"), register("C"), program))
read(io, String) |> parse(input) |> result
end
function main(io::IO)
return nothing
end
end
Part 2
My input is
Register A: 25358015
Register B: 0
Register C: 0
Program: 2,4,1,1,7,5,0,3,4,7,1,6,5,5,3,0
Disassembled that would be:
bst 4
bxl 1
cdv 5
adv 3
bxc 7
bxl 6
out 5
jnz 0
This is a single loop!
while a != 0
= a % 8
b ⊻= 1
b = a >> b
c = a >> 3
a ⊻= c
b ⊻= 6
b print(b % 8)
end
We may reshuffle, and then the core loop is
while a != 0
# do stuff
>>= 3
a end
In that “do stuff” is:
- flip the 1st bit from
a
, so:b = a % 8 + 1
ora % 8 - 1
this determines which bits froma
we’re reading intoc
c = a >> b
- print
b%8 ⊻ c%8 ⊻ 6
The first number we should write is 2, or 0b010
. Then
b%8 ⊻ c%8 = 0b100
. We could make a table for admissible
values of b
and c
.
using CairoMakie
function plot_tables()
= Figure()
fig for r = 1:2
for c = 1:4
= (r-1) * 4 + c - 1
n = Axis(fig[r, c], title="$n")
ax = xor.(xor.((0:7)[:,na], (0:7)[na,:]), n) .== 0
table heatmap!(ax, table)
end
end
save("day16-table.svg", fig)
end
We know that the value of b
is one-to-one with the first
three bits in a
. And, these tables give a one-to-one
mapping of b
to c
. So choosing b
fixes the values for some other bits in a
, which in turn
fix values later on, drastically shrinking our state space.
Furthermore, if xor(b, 1) < 3
then the bits of
c
and b
overlap.