AdventOfCode/2024/day14.exs

140 lines
4.2 KiB
Elixir
Executable file

#!/usr/bin/env elixir
defmodule Day14 do
def part1({robots, size_x, size_y}) do
mid_x = div(size_x, 2)
mid_y = div(size_y, 2)
robots
|> Enum.reduce({0, 0, 0, 0}, fn [px, py, vx, vy], {a, b, c, d} ->
x = Integer.mod(px + vx * 100, size_x)
y = Integer.mod(py + vy * 100, size_y)
case {x, y} do
{x, y} when x < mid_x and y < mid_y -> {a + 1, b, c, d}
{x, y} when x < mid_x and y > mid_y -> {a, b + 1, c, d}
{x, y} when x > mid_x and y < mid_y -> {a, b, c + 1, d}
{x, y} when x > mid_x and y > mid_y -> {a, b, c, d + 1}
{x, y} when x == mid_x or y == mid_y -> {a, b, c, d}
end
end)
|> Tuple.product()
end
def part2({robots, size_x, size_y}) do
# The tree looks like this
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# X X
# X X
# X X
# X X
# X X X
# X XXX X
# X XXXXX X
# X XXXXXXX X
# X XXXXXXXXX X
# X XXXXX X
# X XXXXXXX X
# X XXXXXXXXX X
# X XXXXXXXXXXX X
# X XXXXXXXXXXXXX X
# X XXXXXXXXX X
# X XXXXXXXXXXX X
# X XXXXXXXXXXXXX X
# X XXXXXXXXXXXXXXX X
# X XXXXXXXXXXXXXXXXX X
# X XXXXXXXXXXXXX X
# X XXXXXXXXXXXXXXX X
# X XXXXXXXXXXXXXXXXX X
# X XXXXXXXXXXXXXXXXXXX X
# X XXXXXXXXXXXXXXXXXXXXX X
# X XXX X
# X XXX X
# X XXX X
# X X
# X X
# X X
# X X
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# Could improve this by searching for that pattern explicitly. In this case I just searched
# manually: noticed things looked suspicious after 27 seconds
# also noticed that repeated every 101 seconds
# Checked each 101 second jump until found the above tree
tick_forever(robots, size_x, size_y)
end
def tick_forever(robots, size_x, size_y, count \\ 27, acc \\ 27) do
robots = tick(robots, size_x, size_y, count)
robomap = MapSet.new(robots, fn [x, y, _, _] -> {x, y} end)
for y <- 0..(size_y - 1) do
for x <- 0..(size_x - 1) do
if MapSet.member?(robomap, {x, y}) do
IO.write("X")
else
IO.write(" ")
end
end
IO.puts("|")
end
IO.gets("#{acc} tree?")
tick_forever(robots, size_x, size_y, 101, acc + 101)
end
def tick(robots, size_x, size_y, times) do
Enum.map(robots, fn [px, py, vx, vy] ->
[Integer.mod(px + vx * times, size_x), Integer.mod(py + vy * times, size_y), vx, vy]
end)
end
def input do
with [input_filename, size_x, size_y] <- System.argv(),
{:ok, input} <- File.read(input_filename),
{size_x, ""} <- Integer.parse(size_x),
{size_y, ""} <- Integer.parse(size_y) do
robots =
input
|> String.split(["p=", ",", " ", "v=", "\n"], trim: true)
|> Enum.map(&String.to_integer/1)
|> Enum.chunk_every(4)
{robots, size_x, size_y}
else
_ -> :error
end
end
#######################
# HERE BE BOILERPLATE #
#######################
def run do
case input() do
:error -> print_usage()
input -> run_parts_with_timer(input)
end
end
defp run_parts_with_timer(input) do
run_with_timer(1, fn -> part1(input) end)
run_with_timer(2, fn -> part2(input) end)
end
defp run_with_timer(part, fun) do
{time, result} = :timer.tc(fun)
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
IO.puts("#{inspect(result)}\n")
end
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
defp print_usage do
IO.puts("Usage: elixir day14.exs input_filename size_x size_y")
end
end
Day14.run()