AdventOfCode/2018/day10/day10.exs

92 lines
2.3 KiB
Elixir

defmodule Star do
defstruct [:pos_x, :pos_y, :vel_x, :vel_y]
def move_all(stars, secs) do
Enum.map(stars, &move(&1, secs))
end
def move(star, secs) do
%Star{star | pos_x: star.pos_x + star.vel_x * secs, pos_y: star.pos_y + star.vel_y * secs}
end
end
defmodule Day10 do
@input_pattern ~r/=< ?(-?\d+), ?(-?\d+)>.*=< ?(-?\d+), ?(-?\d+)>/
def parse_line(line) do
[pos_x, pos_y, v_x, v_y] =
Regex.run(@input_pattern, line, capture: :all_but_first)
|> Enum.map(&String.to_integer/1)
%Star{pos_x: pos_x, pos_y: pos_y, vel_x: v_x, vel_y: v_y}
end
def init do
File.stream!("input")
|> Enum.map(&parse_line/1)
end
# Start with the first star, then compare all the others to find the extremities
def bounding_box([%Star{pos_x: x, pos_y: y} | _] = stars) do
Enum.reduce(stars, {x, y, x, y}, fn star, {min_x, min_y, max_x, max_y} ->
{
min(star.pos_x, min_x),
min(star.pos_y, min_y),
max(star.pos_x, max_x),
max(star.pos_y, max_y)
}
end)
end
def draw(stars) do
starfield = MapSet.new(stars, fn star -> {star.pos_x, star.pos_y} end)
{min_x, min_y, max_x, max_y} = bounding_box(stars)
grid =
for y <- min_y..max_y, x <- min_x..max_x do
eol = if x === max_x, do: "\n", else: ""
char = if MapSet.member?(starfield, {x, y}), do: "#", else: " "
char <> eol
end
Enum.join(grid)
end
def aligned?(stars) do
starfield = MapSet.new(stars, fn star -> {star.pos_x, star.pos_y} end)
Enum.all?(stars, &aligned?(&1, starfield))
end
def aligned?(star, starfield) do
neighbours = [
{star.pos_x - 1, star.pos_y - 1},
{star.pos_x - 1, star.pos_y},
{star.pos_x - 1, star.pos_y + 1},
{star.pos_x, star.pos_y - 1},
{star.pos_x, star.pos_y + 1},
{star.pos_x + 1, star.pos_y - 1},
{star.pos_x + 1, star.pos_y},
{star.pos_x + 1, star.pos_y + 1}
]
Enum.any?(neighbours, &MapSet.member?(starfield, &1))
end
def wait_for_alignment(stars, count) do
if aligned?(stars) do
{stars, count}
else
stars |> Star.move_all(1) |> wait_for_alignment(count + 1)
end
end
def go do
{stars, count} = init() |> wait_for_alignment(0)
IO.puts(draw(stars))
IO.puts("#{count} seconds")
end
end
Day10.go()