Advent of Code 2023 - Day 18
Mix.install([
{:kino, "~> 0.11.0"},
{:kino_aoc, github: "ljgago/kino_aoc"}
])
Input
{:ok, puzzle_input} =
KinoAOC.download_puzzle("2023", "18", System.fetch_env!("LB_AOC_SESSION"))
input =
puzzle_input
|> String.split("\n", trim: true)
|> Enum.map(fn line ->
String.split(line, [" ", " (#", ")"], trim: true)
|> List.to_tuple()
end)
defmodule LavaductLagoon do
@doc """
Get total area filled with lava from input dig plan.
"""
def lava_area(plan) do
path = dig(plan)
area = shoelace(path)
boundary = boundary(plan)
boundary + picks(area, boundary)
end
@doc """
Get the path from input dig plan.
"""
def dig(plan) do
for {{dr, dc}, meters} <- plan, reduce: [{0, 0}] do
[{r, c} | _] = acc -> [{r + dr * meters, c + dc * meters} | acc]
end
end
@doc """
Get the length of boundary meters from input dig plan.
"""
def boundary(plan) do
plan
|> Enum.map(&elem(&1, 1))
|> Enum.sum()
end
@doc """
Get the area of input polygon via Shoelace formula.
see https://en.wikipedia.org/wiki/Shoelace_formula
see https://rosettacode.org/wiki/Shoelace_formula_for_polygonal_area#Elixir
"""
def shoelace(points) do
points
|> Enum.reduce({0, List.last(points)}, fn {x1, y1}, {sum, {x0, y0}} ->
{sum + (y0 * x1 - x0 * y1), {x1, y1}}
end)
|> elem(0)
|> abs()
|> div(2)
end
@doc """
Get number of inside points via Pick's theorem.
see https://en.wikipedia.org/wiki/Pick%27s_theorem
"""
def picks(area, boundary) do
area - div(boundary, 2) + 1
end
end
import LavaductLagoon
Part 1
input
|> Enum.map(fn {d, m, _} ->
case {d, String.to_integer(m)} do
{"L", m} -> {{-1, 0}, m}
{"R", m} -> {{1, 0}, m}
{"U", m} -> {{0, -1}, m}
{"D", m} -> {{0, 1}, m}
end
end)
|> lava_area()
Part 2
input
|> Enum.map(fn {_, _, <> <> d} ->
case {d, Integer.parse(hex, 16)} do
{"0", {m, _}} -> {{0, 1}, m}
{"1", {m, _}} -> {{1, 0}, m}
{"2", {m, _}} -> {{0, -1}, m}
{"3", {m, _}} -> {{-1, 0}, m}
end
end)
|> lava_area()