Day14
Mix.install([
{:kino_aoc, git: "https://github.com/ljgago/kino_aoc"}
])
Setup
{:ok, data} = KinoAOC.download_puzzle("2024", "14", System.fetch_env!("LB_AOC_SECRET"))
Solve
defmodule Day14 do
@digits ~r/(-?\d+)/
def parse(input) do
input
|> String.trim()
|> String.split("\n", trim: true)
|> Enum.map(&parse_robot/1)
end
def parse_robot(str) do
Regex.scan(@digits, str, capture: :all_but_first)
|> List.flatten()
|> Enum.map(&String.to_integer/1)
|> List.to_tuple()
end
def solve(data, max \\ {11, 7}) do
robots = parse(data)
{t1(robots, max), t2(robots, max)}
end
def t1(robots, max) do
robots = simulate(robots, max, 100)
factor(robots, max)
end
def t2(robots, max) do
all = length(robots)
find_tree(0, max, robots, all) |> handle_res(max)
end
def handle_res({:ok, {i, robots}}, max) do
plot(robots, max)
i
end
def handle_res({:error, {:not_found, robots}}, max) do
plot(robots, max)
:not_found
end
def find_tree(i, {mx, my}, robots, _) when i > mx*my, do: {:error, {:not_found, robots}}
def find_tree(i, max, robots, all) do
robots = simulate(robots, max, 1)
seen = Enum.reduce(robots, MapSet.new(), fn {x, y, _, _}, seen ->
MapSet.put(seen, {x,y})
end)
if MapSet.size(seen) == all do
{:ok, {i, robots}}
else
find_tree(i+1, max, robots, all)
end
end
def simulate(robots, {mx, my}, n \\ 1) do
Enum.reduce(robots, [], fn {x, y, vx, vy}, acc ->
x = prem(x + vx*n, mx)
y = prem(y + vy*n, my)
[{x,y, vx, vy} | acc]
end)
end
def prem(i, max) do
r = rem(i, max)
(r < 0) && max+r || r
end
def factor(robots, {mx, my}) do
{midx, midy} = {div(mx, 2), div(my, 2)}
Enum.reduce(robots, [0,0,0,0], fn robot, [q1,q2,q3,q4] = acc ->
case robot do
{x,y,_,_} when x < midx and y < midy -> [q1+1,q2,q3,q4]
{x,y,_,_} when x > midx and y < midy -> [q1,q2+1,q3,q4]
{x,y,_,_} when x < midx and y > midy -> [q1,q2,q3+1,q4]
{x,y,_,_} when x > midx and y > midy -> [q1,q2,q3,q4+1]
_ -> acc #mid
end
end)
|> Enum.product()
end
def plot(robots, {mx, my}) do
g = Enum.reduce(robots, %{}, fn {x, y, _, _}, g ->
Map.update(g, {x,y}, 1, fn cnt -> cnt + 1 end)
end)
Enum.map((0..my-1), fn y ->
Enum.map((0..mx-1), fn x ->
Map.get(g, {x, y}, ".") |> to_string()
end)
|> Enum.join("")
end)
|> Enum.join("\n")
|> IO.puts()
end
end
tdata = """
p=0,4 v=3,-3
p=6,3 v=-1,-3
p=10,3 v=-1,2
p=2,0 v=2,-1
p=0,0 v=1,3
p=3,0 v=-2,-2
p=7,6 v=-1,-3
p=3,0 v=-1,-2
p=9,3 v=2,3
p=7,3 v=-1,2
p=2,4 v=2,-3
p=9,5 v=-3,-3
"""
Day14.solve(data, {101, 103})