Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Day 18: Lavaduct Lagoon – Advent of Code 2023

2023/18.livemd

Day 18: Lavaduct Lagoon – Advent of Code 2023

Mix.install([{:topo, "~> 1.0"}])

input =
  File.stream!("/Users/pw/src/weiland/adventofcode/2023/input/18.txt")
  |> Stream.map(&String.trim/1)

test_input =
  "R 6 (#70c710)
D 5 (#0dc571)
L 2 (#5713f0)
D 2 (#d2c081)
R 2 (#59c680)
D 2 (#411b91)
L 5 (#8ceee2)
U 2 (#caa173)
L 1 (#1b58a2)
U 2 (#caa171)
R 2 (#7807d2)
U 3 (#a77fa3)
L 2 (#015232)
U 2 (#7a21e3)" |> String.split("\n")

Parsing

delta_map = %{"D" => [1, 0], "U" => [-1, 0], "L" => [0, -1], "R" => [0, 1]}

parse_one = fn input ->
  input
  |> Enum.map(&String.split(&1, " "))
  |> Enum.map(fn [dir, steps, _color] -> [delta_map[dir], String.to_integer(steps)] end)
end

dir_map = %{"0" => "R", "1" => "D", "2" => "L", "3" => "U"}

parse_two = fn input ->
  input
  |> Enum.map(&String.split(&1, " "))
  |> Enum.map(fn [_, _, "(#" <> string] ->
    [
      delta_map[dir_map[String.slice(string, -2, 1)]],
      Integer.parse(String.slice(string, 0, 5), 16) |> elem(0)
    ]
  end)
end

parse_two.(test_input)

Part One

dig = fn input ->
  {_, perimeter, coords} =
    input
    |> Enum.reduce({{0, 0}, 0, []}, fn [[row_delta, col_delta], steps],
                                       {{prev_row, prev_col}, perimeter, coords} ->
      pos = {prev_row + row_delta * steps, prev_col + col_delta * steps}
      {pos, perimeter + steps, [pos | coords]}
    end)

  area =
    coords
    |> Enum.reverse()
    |> Enum.chunk_every(2, 1, :discard)
    |> Enum.reduce(0, fn [{row, col}, {next_row, next_col}], acc ->
      (row + next_row) * (col - next_col) / 2 + acc
    end)

  trunc(area + perimeter / 2 + 1)
end
IO.inspect(dig.(test_input |> parse_one.()) == 62, label: "Test part one")

dig.(input |> parse_one.())

Part Two

IO.inspect(dig.(test_input |> parse_two.()) == 952_408_144_115, label: "Test part two")

dig.(input |> parse_two.())