mruoss
21
My approach in a nutshell: convert lines to list of points and find points that occur more than once in the (flattened) list of points.
Part 1: ignore diagonals
Part 2: include diagonals
defmodule AOC2021.Day05.Solver do
def solve(stream, :first), do: solve(stream, fn _ -> [] end)
def solve(stream, :second), do: solve(stream, fn [x1, y1, x2, y2] -> Enum.zip(x1..x2, y1..y2) end)
def solve(stream, get_diagonal) do
stream
|> Stream.map(&String.trim/1)
|> Stream.map(&String.split(&1, [" -> ", ","]))
|> Stream.map(fn line -> Enum.map(line, &String.to_integer/1) end)
|> Stream.flat_map(fn
[x, y1, x, y2] -> Enum.map(y1..y2, fn y -> {x, y} end)
[x1, y, x2, y] -> Enum.map(x1..x2, fn x -> {x, y} end)
diagonal_line -> get_diagonal.(diagonal_line)
end)
|> Enum.group_by(&Function.identity/1)
|> Enum.count(fn {_, val} -> Enum.count(val) >= 2 end)
end
end
1 Like
TIL Function.identity. Thanks!
1 Like
My solution. Think it’s pretty similar to others.
Believe my diagonal approach is too naive, but happened to work for the input.
defmodule Advent.Y2021.D05 do
@spec part_one(Enumerable.t()) :: non_neg_integer()
def part_one(input) do
input
|> parse_input()
|> Stream.filter(fn {{x1, y1}, {x2, y2}} ->
x1 == x2 || y1 == y2
end)
|> count_overlapping_coords()
end
@spec part_two(Enumerable.t()) :: non_neg_integer()
def part_two(input) do
input
|> parse_input()
|> count_overlapping_coords()
end
@spec parse_input(Enumerable.t()) :: Enumerable.t()
defp parse_input(input) do
Stream.map(input, fn line ->
[x1, y1, x2, y2] =
Regex.run(~r/(\d+),(\d+)\s+\->\s+(\d+),(\d+)/, line, capture: :all_but_first)
|> Enum.map(&String.to_integer/1)
{{x1, y1}, {x2, y2}}
end)
end
@spec count_overlapping_coords(Enumerable.t()) :: non_neg_integer()
defp count_overlapping_coords(coords) do
coords
|> Stream.flat_map(fn
{{x, y1}, {x, y2}} -> Enum.map(y1..y2, fn y -> {x, y} end)
{{x1, y}, {x2, y}} -> Enum.map(x1..x2, fn x -> {x, y} end)
{{x1, y1}, {x2, y2}} -> Enum.zip(x1..x2, y1..y2)
end)
|> Enum.frequencies()
|> Enum.count(fn {_, count} -> count > 1 end)
end
end
input =
File.stream!("d05_input.txt")
|> Stream.map(&String.trim/1)
IO.inspect(Advent.Y2021.D05.part_one(input))
IO.inspect(Advent.Y2021.D05.part_two(input))
A bit late, but my solution for day 5:
defmodule Advent5 do
def calc do
{:ok, contents} = File.read("input5.txt")
moves = contents |> String.split("\n", trim: true) |> Enum.map(&pars_move(&1))
hight_points = build_map_points(moves, []) |> Enum.frequencies() |> Enum.to_list() |> Enum.filter(fn {_,hight} -> hight >= 2 end)
Enum.count(hight_points)
end
def build_map_points([],points), do: points
def build_map_points([move|tail],points), do: build_map_points(tail, points ++ build_path(move))
def pars_move(move) do
move |> String.replace(" ", "") |> String.split("->", trim: true) |> Enum.map(fn cord ->
[x,y] = String.split(cord, ",")
{ix,_} = Integer.parse(x)
{iy,_} = Integer.parse(y)
{ix,iy}
end)
end
def build_path([{xs,ys},{xe,ye}]) when xs == xe ,do: Enum.to_list(ys..ye) |> Enum.map(fn y -> {xs,y} end)
def build_path([{xs,ys},{xe,ye}]) when ys == ye ,do: Enum.to_list(xs..xe) |> Enum.map(fn x -> {x,ys} end)
def build_path([{xs,ys},{xe,ye}]) ,do: Enum.zip(Enum.to_list(xs..xe), Enum.to_list(ys..ye))
end
unsek
26