advent_of_code/elixir/livebook/aoc_day_21.livemd
2024-11-15 21:03:49 -05:00

6.5 KiB
Executable file

aoc 21 2023

Mix.install([
  {:kino_aoc, "~> 0.1"}
])

Section

{:ok, puzzle_input} =
  KinoAOC.download_puzzle("2023", "21", System.fetch_env!("LB_AOC_SESSION"))
ex = ~s"...........
.....###.#.
.###.##..#.
..#.#...#..
....#.#....
.##..S####.
.##..#...#.
.......##..
.##.#.####.
.##..##.##.
..........."
defmodule Coordinates2D do
  def polar_to_cartesian_int(r, theta) do
    {r * :math.cos(theta), r * :math.sin(theta)}
  end

  def window_centered_at(p, delta_x, delta_y) do
    {x, y} = p
    dx = delta_x / 2
    dy = delta_y / 2

    {
      {
        x - dx,
        y - dy
      },
      {
        x + dx,
        y + dy
      }
    }
  end
end
{x, y} = Coordinates2D.polar_to_cartesian_int(26_501_365, 2 * :math.pi())
Coordinates2D.window_centered_at({x, y}, 10, 10)

defmodule Grid2D do
  defstruct type: :repeating, inner: nil, w: 0, h: 0, origin: {0, 0}

  def from_repeating_text(i, mapper \\ fn i -> i end, origin \\ {0, 0}) do
    grid =
      i
      |> String.split("\n")
      |> Stream.with_index()
      |> Enum.flat_map(fn {line, row} ->
        line
        |> String.to_charlist()
        |> Stream.with_index()
        |> Enum.map(fn {letter, index} ->
          {{row, index}, mapper.(letter)}
        end)
      end)
      |> Map.new()

    {{h, w}, _} = grid |> Enum.max_by(fn {c, _} -> c end)
    %Grid2D{type: :repeating, inner: grid, h: h, w: w, origin: origin}
  end

  def display(grid, nil, mapper) do
    display(grid, {{0, 0}, {9, 9}}, mapper)
  end

  def display(grid, rect, mapper \\ fn i -> i end) do
    window(
      grid,
      rect
    )
    |> Enum.map(&mapper.(&1))
  end

  def center(grid) do
  end

  def elem(grid, x, y) when grid.type == :repeating do
    x =
      case rem(x, grid.w) do
        n when n < 0 -> n + grid.w
        n -> n
      end

    y =
      case rem(y, grid.h) do
        n when n < 0 -> n + grid.h
        n -> n
      end

    elem_inner(grid.inner, x, y)
  end

  def window(grid, r) do
    IO.puts(inspect(r))
    {ox, oy} = grid.origin
    {{min_x, min_y}, {max_x, max_y}} = r

    for y <- min_y..max_y,
        x <- min_x..max_x do
      Grid2D.elem(grid, ox + x, oy + y)
    end
  end

  defp elem_inner(inner, x, y) do
    IO.puts("retriving elt at index #{inspect({x, y})}")
    ret = inner |> Map.get({x, y})
    IO.puts("returning #{inspect(ret)}")
    ret
  end
end
grid =
  Grid2D.from_repeating_text(
    ex,
    fn letter ->
      case letter do
        ?. -> :garden
        ?# -> :rock
        ?S -> :elf
      end
    end,
    {5, -5}
  )
Coordinates2D.polar_to_cartesian_int(0, 0)
|> Coordinates2D.window_centered_at(3, 3)
|> then(fn {{minx, miny}, {maxx, maxy}} ->
  {{floor(minx), floor(miny)}, {ceil(maxx), ceil(maxy)}}
end)
rem(-3, 5) + 5
Grid2D.display(
  grid,
  Coordinates2D.polar_to_cartesian_int(5, 3 * :math.pi() / 2)
  |> Coordinates2D.window_centered_at(3, 3)
  |> then(fn {{minx, miny}, {maxx, maxy}} ->
    {{floor(minx), floor(miny)}, {ceil(maxx), ceil(maxy)}}
  end),
  fn l ->
    case l do
      :garden -> ?.
      :rock -> ?#
      :elf -> ?S
    end
  end
)
|> Enum.chunk_every(5)
|> Enum.join("\n")
|> IO.puts()

grid |> Grid2D.elem(5, 5)
# map =
#   puzzle_input
#   |> String.split("\n")
#   |> Stream.with_index()
#   |> Enum.flat_map(fn {line, row} ->
#     line
#     |> String.to_charlist()
#     |> Stream.with_index()
#     |> Enum.map(fn {letter, index} ->
#       type =
#         case letter do
#           ?. -> :garden
#           ?# -> :rock
#           ?S -> :elf
#         end

#       {{row, index}, type}
#     end)
#   end)
#   |> Map.new()
{map_y, map_x} = map |> Map.keys() |> Enum.max()
{elf, _} = map |> Enum.find(nil, fn {_, value} -> value === :elf end)
steps = [
  {-1, 0},
  {1, 0},
  {0, 1},
  {0, -1}
]
# 1..64 |>
#   Enum.reduce(
#     [elf],
#     fn _, locations ->
#       for {e_y, e_x} <- steps,
#         {l_y, l_x} <- locations,
#         next_loc = {e_y + l_y, e_x + l_x,},
#         cycled_log = {rem(e_y + l_y, map_y), rem(e_x + l_x, map_x)},
#         Map.get(map, cycled_log) === :garden or Map.get(map, cycled_log) === :elf,
#         into: MapSet.new() do
#           next_loc
#       end
#     end
#   ) |> MapSet.size()
elf
# n = 64
# for y <- -n..n,
#  x <- -(n-y)..(n-y),
#  rem(x+y, 2) === rem(n, 2),
#  tile = Map.get(map, {elem(elf, 0) + y , elem(elf, 1) + x}),
#  tile == :garden || tile == :elf,
#  reduce: 0
#  do
#   acc -> acc + 1
#    #{x, y}
#  end
a = [1, 2, 3]
b = [2, 3]
a -- b
step_one =
  for {y, x} <- steps,
      {ey, ex} = elf,
      new = {y + ey, ex + x},
      tile = Map.get(map, new),
      tile == :garden or tile == :elf do
    new
  end
two_steps =
  for y <- -2..2, x <- -2..2, rem(x + y, 2) == rem(2, 2), abs(x) + abs(y) <= 2 do
    {y, x}
  end
steps
# r = Range.new(3, 26501365, 2)
# 26501365, 2)
r = Range.new(3, 26_501_365, 2)

results =
  for i <- r,
      reduce: {MapSet.new([step_one]), MapSet.new([step_one])} do
    {prev_steps, steps} ->
      new_steps =
        for {e_y, e_x} <- steps,
            {l_y, l_x} <- prev_steps,
            next_loc = {e_y + l_y, e_x + l_x},
            cycled_log = {rem(e_y + l_y, map_y), rem(e_x + l_x, map_x)},
            tile = Map.get(map, cycled_log),
            tile === :garden or tile === :elf,
            into: MapSet.new() do
          IO.puts(inspect(next_loc))
          next_loc
        end

      new_steps =
        for {e_y, e_x} <- steps,
            {l_y, l_x} <- new_steps,
            next_loc = {e_y + l_y, e_x + l_x},
            cycled_log = {rem(e_y + l_y, map_y), rem(e_x + l_x, map_x)},
            tile = Map.get(map, cycled_log),
            tile === :garden or tile === :elf,
            into: MapSet.new() do
          next_loc
        end

      # IO.puts(inspect(new_steps))

      # IO.puts("#{i} - #{MapSet.size(new_steps)}")
      {new_steps, MapSet.union(steps, new_steps)}
  end