diff --git a/lib/common.ex b/lib/common.ex index 6882171..3c6419b 100644 --- a/lib/common.ex +++ b/lib/common.ex @@ -12,4 +12,24 @@ defmodule Aoc2023.Common do fn (val) -> val |> unquote(expr) end end end + + defmacro l &&& r do + quote do + fn val -> + l = val |> unquote(l) + r = val |> unquote(r) + {l, r} + end + end + end + + def runs(lst, f) do + lst + |> Enum.with_index() + |> Enum.chunk_by(pipe(elem(0) |> f.())) + |> Enum.filter(pipe(Enum.at(0) |> elem(0) |> f.())) + |> Enum.map(Enum.at(0) |> elem(1) &&& length) + end + + def id(x), do: x end diff --git a/lib/day3.ex b/lib/day3.ex new file mode 100644 index 0000000..70ca224 --- /dev/null +++ b/lib/day3.ex @@ -0,0 +1,110 @@ +defmodule Aoc2023.Day3 do + use Aoc2023 + + @symbols [ + ?$, + ?%, + ?&, + ?*, + ?+, + ?-, + ?/, + ?=, + ?@, + ?# + ] + + defp adjacents({y, x, len}, grid) do + [ + {y, x - 1}, + {y, x + len} + | for(y <- [y - 1, y + 1], x <- (x - 1)..(x + len), do: {y, x}) + ] + |> Enum.filter(fn {y, x} -> + 0 <= y and y < length(grid) and 0 <= x and x < length(Enum.at(grid, y)) + end) + end + + defp adjacents({y, x}, grid), do: adjacents({y, x, 1}, grid) + + defp get({y, x}, grid) do + grid |> Enum.at(y) |> Enum.at(x) + end + + defp get({y, x, len}, grid) do + x..(x + len - 1) |> Enum.map(&get({y, &1}, grid)) + end + + defp contains({y, x}, numbers) do + numbers + |> Enum.filter(fn {ny, nx, nlen} -> ny == y and nx <= x and x < nx + nlen end) + end + + defp number(num, grid) do + num + |> get(grid) + |> Enum.map(&(&1 - ?0)) + |> Integer.undigits() + end + + def parse(input) do + grid = + input + |> String.split("\n") + |> Enum.map(&String.to_charlist/1) + + { + grid, + grid + |> Enum.with_index() + |> Enum.flat_map(fn {row, y} -> + row + |> runs(&(?0 <= &1 && &1 <= ?9)) + |> Enum.map(fn {x, len} -> {y, x, len} end) + end) + } + end + + def part1({grid, numbers}) do + numbers + |> Enum.map(id &&& adjacents(grid)) + |> Enum.filter( + pipe( + elem(1) + |> Enum.any?(&(get(&1, grid) in @symbols)) + ) + ) + |> Enum.map( + pipe( + elem(0) + |> number(grid) + ) + ) + |> Enum.sum() + end + + def part2({grid, numbers}) do + grid + |> Enum.with_index() + |> Enum.flat_map(fn {row, y} -> + row + |> Enum.with_index() + |> Enum.filter(&(elem(&1, 0) == ?*)) + |> Enum.map(&{y, elem(&1, 1)}) + |> Enum.map(id &&& adjacents(grid)) + |> Enum.map(pipe( + elem(1) + |> Enum.flat_map(pipe(contains(numbers))) + |> Enum.uniq() + )) + |> Enum.filter(pipe( + length() + |> then(&(&1 == 2))) + ) + |> Enum.map(pipe( + Enum.map(pipe(number(grid))) |> Enum.product + )) + end) + |> Enum.sum + end +end diff --git a/lib/mix_tasks.ex b/lib/mix_tasks.ex index add3140..2482d83 100644 --- a/lib/mix_tasks.ex +++ b/lib/mix_tasks.ex @@ -4,6 +4,7 @@ defmodule Mix.Tasks.Aoc do defp module(1), do: Aoc2023.Day1 defp module(2), do: Aoc2023.Day2 + defp module(3), do: Aoc2023.Day3 # [MODULE INSERTION POINT] defp base_dir(), do: System.get_env("AOC_BASE") @@ -110,6 +111,7 @@ defmodule Mix.Tasks.Aoc do :code.purge(module(day)) :code.purge(Aoc2023.Common) :code.load_file(module(day)) + :code.load_file(Aoc2023.Common) run(day, ["test"]) receive do :update -> loop.(loop) diff --git a/tests/day3/1 b/tests/day3/1 new file mode 100644 index 0000000..b20187f --- /dev/null +++ b/tests/day3/1 @@ -0,0 +1,10 @@ +467..114.. +...*...... +..35..633. +......#... +617*...... +.....+.58. +..592..... +......755. +...$.*.... +.664.598.. diff --git a/tests/day3/1.1 b/tests/day3/1.1 new file mode 100644 index 0000000..4b0190a --- /dev/null +++ b/tests/day3/1.1 @@ -0,0 +1 @@ +4361 diff --git a/tests/day3/1.2 b/tests/day3/1.2 new file mode 100644 index 0000000..34eb74c --- /dev/null +++ b/tests/day3/1.2 @@ -0,0 +1 @@ +467835