advent_of_code/elixir/livebook/2024/day14.livemd
2024-12-15 00:32:14 -05:00

2.7 KiB

AOC 2024 Day 14

Section

input = """
"""

positive rem that only returns positive number

rrem = fn a, b -> 
  t = rem(a, b)
  if t < 0 do
    t + b
  else
    t
  end
end
quadrant = fn {x, y}, {w, h} -> 
  {cx, cy} = {div(w, 2), div(h, 2)}
  {
    (if x < cx, do: -1, else: (if cx < x, do: 1, else: nil)), 
    (if y < cy, do: -1, else: (if cy < y, do: 1, else: nil))
  }
end
w = 101
h = 103
pos = for line <- input |> String.trim() |> String.split("\n", trim: true),
    [p, v] = String.split(line, " "),
    {x,y} = p |> String.split("=") |> Enum.at(1) |> String.split(",") |> Enum.map(&String.to_integer/1) |> List.to_tuple(),
    {px, py} = v |> String.split("=") |> Enum.at(1) |> String.split(",") |> Enum.map(&String.to_integer/1) |> List.to_tuple(),
  reduce: %{}
do
  acc ->
    {fx, fy} = {rrem.(x + 100*px, w), rrem.(y + 100*py, h)}
    if (fx != div(w, 2) and fy != div(h, 2)) do
      acc |> Map.update(quadrant.({fx, fy}, {w, h}), 1, &(&1 + 1))
    else
      acc
    end
end
pos |> Map.values() |> Enum.product()
points = for line <- input |> String.trim() |> String.split("\n", trim: true),
    [p, v] = String.split(line, " "),
    {x,y} = p |> String.split("=") |> Enum.at(1) |> String.split(",") |> Enum.map(&String.to_integer/1) |> List.to_tuple(),
    {px, py} = v |> String.split("=") |> Enum.at(1) |> String.split(",") |> Enum.map(&String.to_integer/1) |> List.to_tuple(),
  reduce: MapSet.new
do
  acc ->
    MapSet.put(acc, {{x, y}, {px, py}})
end
draw = fn points -> 
  for y <- 0..(h-1) do
    for x <- 0..(w-1) do
      File.write("buffer.txt", (if MapSet.member?(points, {x, y}), do: "*", else: "."), [:write, :append])
      if x == (w-1) do
        File.write("buffer.txt", "\n", [:write, :append])
      end
    end
  end
end
q = Stream.iterate(0, &(&1 + 1))
  |> Enum.take(10_000)
  |> Enum.reduce(%{}, fn i, seen -> 
      safety = for {{x, y}, {px, py}} <- points,
        reduce: %{} do
        acc ->
          f = quadrant.({rrem.(x + i*px, w), rrem.(y + i*py, h)}, {w, h})
          case f do
            {nil, _ } -> acc
            {_, nil} -> acc
            x -> Map.update(acc, x, 1, &(&1 + 1))
          end
        end |> Map.values() |> Enum.product()
      Map.put(seen, i, safety)
  end
  )
q |> Enum.min_by(&elem(&1, 1))

I solved part two by a mix of searching frames around the min safety factor. The below cell renders the frames in a range near the min.

for i <- 6200..6300 do
  for {{x, y}, {px, py}} <- points,
    reduce: MapSet.new do
      acc ->
        f = {rem(x + i*px, w), rem(y + i*py, h)}
        MapSet.put(acc, f)
  end |> draw.()
end