Day 21
file:test/Day21Spec.jl
using AOC2024.Day21: robot, NUMPAD, CONTROLLER
let buf = Char[],
out(x) = push!(buf, x),
= out |> robot(NUMPAD),
r1 = out |> robot(NUMPAD) |> robot(CONTROLLER),
r2 = out |> robot(NUMPAD) |> robot(CONTROLLER) |> robot(CONTROLLER)
r3
r1("<A^A>^^AvvvA")
@test join(buf) == "029A"
empty!(buf)
r2("v<<A>>^A<A>AvA<^AA>A<vAAA>^A")
@test join(buf) == "029A"
empty!(buf)
r3("<vA<AA>>^AvAA<^A>A<v<A>>^AvA^A<vA>^A<v<A>^A>AAvA^A<v<A>A>^AAAvA<^A>A")
@test join(buf) == "029A"
end
file:src/Day21.jl
module Day21
using DataStructures
const Pos = CartesianIndex{2}
const Key = Union{Char, Missing}
const Keypad = Matrix{Key}
const NIL = missing
const NUMPAD::Keypad = [
'7' '8' '9';
'4' '5' '6';
'1' '2' '3';
'0' 'A' ]
NIL
const CONTROLLER::Keypad = [
'^' 'A';
NIL '<' 'v' '>' ]
const DIRECTION = IdDict{Char,Pos}(
'^' => Pos(-1, 0),
'<' => Pos(0, -1),
'v' => Pos( 1, 0),
'>' => Pos(0, 1))
struct Cmd{T} end
robot(keypad::Keypad) = Base.Fix1(robot, keypad)
function robot(keypad::Keypad, output::F) where {F}
= findfirst(isequal('A'), keypad)
pos
function f(k::Key)
@assert k ∈ "A^<v>"
if k == 'A'
output(keypad[pos])
else
+= DIRECTION[k]
pos if get(keypad, pos, NIL) === NIL
throw("Panic!")
end
end
end
function f(seq::AbstractString)
|> collect .|> f
seq return nothing
end
f(::Type{Cmd{:set!}}, k::Key) = pos = findfirst(isequal(k), keypad)
f(::Type{Cmd{:get}}) = keypad[pos]
f(::Type{Cmd{:reset!}}) = f(Cmd{:set!}, 'A')
return f
end
function gen_cmd(robots::Vector{Keypad}, seq::AbstractString)
isempty(robots) && return seq
join(gen_cmd(robots, a, b) for (a, b) in zip('A' * seq[1:end-1], seq[1:end]))
end
function gen_cmd(robots::Vector{Keypad}, a::Key, b::Key)
= robots[1]
r = findfirst(isequal(a), r)
pos1 = findfirst(isequal(b), r)
pos2 = pos2 - pos1
delta if delta == Pos(0, 0)
return "A"
end
= repeat((delta[1] > 0 ? 'v' : '^'), abs(delta[1]))
seq1 = repeat((delta[2] > 0 ? '>' : '<'), abs(delta[2]))
seq2 1] == 0 && return gen_cmd(robots[2:end], seq2 * 'A')
delta[2] == 0 && return gen_cmd(robots[2:end], seq1 * 'A')
delta[
if r[pos1 + Pos(delta[1], 0)] === missing
return gen_cmd(robots[2:end], seq2 * seq1 * 'A')
end
if r[pos1 + Pos(0, delta[2])] === missing
return gen_cmd(robots[2:end], seq1 * seq2 * 'A')
end
# it shouldn't matter which direction to move first
= gen_cmd(robots[2:end], seq1 * seq2 * 'A')
p = gen_cmd(robots[2:end], seq2 * seq1 * 'A')
q length(p) < length(q) ? p : q
end
function complexity(robots::Vector{Keypad}, robotid::Int, seq::AbstractString)
complexity(robots, robotid, seq, Dict{Tuple{Int,Char,Char},Int}())
end
function complexity(robots::Vector{Keypad}, robotid::Int, seq::AbstractString, cache)
== 0 && return length(seq)
robotid sum(complexity(robots, robotid, a, b, cache)
for (a, b) in zip('A' * seq[1:end-1], seq[1:end]))
end
function complexity(robots::Vector{Keypad}, id::Int, a::Key, b::Key, cache)
if (id, a, b) ∈ keys(cache)
return cache[(id, a, b)]
end
function compute()
= robots[id]
r = findfirst(isequal(a), r)
pos1 = findfirst(isequal(b), r)
pos2 = pos2 - pos1
delta
if delta == Pos(0, 0)
return cache[(id, a, b)] = 1
end
= repeat((delta[1] > 0 ? 'v' : '^'), abs(delta[1]))
seq1 = repeat((delta[2] > 0 ? '>' : '<'), abs(delta[2]))
seq2
if delta[1] == 0
return complexity(robots, id-1, seq2 * 'A', cache)
end
if delta[2] == 0
return complexity(robots, id-1, seq1 * 'A', cache)
end
if r[pos1 + Pos(delta[1], 0)] === missing
return complexity(robots, id-1, seq2 * seq1 * 'A', cache)
end
if r[pos1 + Pos(0, delta[2])] === missing
return complexity(robots, id-1, seq1 * seq2 * 'A', cache)
end
= complexity(robots, id-1, seq1 * seq2 * 'A', cache)
p = complexity(robots, id-1, seq2 * seq1 * 'A', cache)
q return min(p, q)
end
return cache[(id, a, b)] = compute()
end
function main(io::IO)
return nothing
end
end