day10
This commit is contained in:
parent
d798c72cc3
commit
71af749206
2 changed files with 268 additions and 0 deletions
229
lib/2023/day10.ex
Normal file
229
lib/2023/day10.ex
Normal file
|
@ -0,0 +1,229 @@
|
|||
defmodule PipeModule do
|
||||
@pipe_types %{
|
||||
"|" => {:n, :s},
|
||||
"-" => {:e, :w},
|
||||
"L" => {:n, :e},
|
||||
"J" => {:n, :w},
|
||||
"7" => {:s, :w},
|
||||
"F" => {:s, :e},
|
||||
"." => :ground,
|
||||
"S" => :animal
|
||||
}
|
||||
|
||||
def parse(input) do
|
||||
for u <-
|
||||
input
|
||||
|> String.split("\n")
|
||||
|> Stream.with_index()
|
||||
|> Enum.flat_map(fn {line, line_idx} ->
|
||||
line
|
||||
|> String.graphemes()
|
||||
|> Stream.with_index()
|
||||
|> Enum.map(fn {pipe_char, index} ->
|
||||
{{index, line_idx}, Map.get(@pipe_types, pipe_char)}
|
||||
end)
|
||||
end),
|
||||
into: %{} do
|
||||
u
|
||||
end
|
||||
end
|
||||
|
||||
def connects?(pipe1, pipe2, _) when pipe1 == :ground or pipe2 == :ground do
|
||||
false
|
||||
end
|
||||
|
||||
def connects?(pipe1, pipe2, orientation) do
|
||||
can_connect?(pipe1, orientation) and can_connect?(pipe2, flip(orientation))
|
||||
end
|
||||
|
||||
def flip(orientation) do
|
||||
case orientation do
|
||||
:n -> :s
|
||||
:s -> :n
|
||||
:w -> :e
|
||||
:e -> :w
|
||||
end
|
||||
end
|
||||
|
||||
def can_connect?(:animal, _) do
|
||||
true
|
||||
end
|
||||
|
||||
def can_connect?({d1, d2}, orientation) do
|
||||
d1 == orientation or d2 == orientation
|
||||
end
|
||||
|
||||
def to_orientation({x1, x2}, {y1, y2}) do
|
||||
{z1, z2} = {y1 - x1, y2 - x2}
|
||||
|
||||
case z1 do
|
||||
0 ->
|
||||
case z2 do
|
||||
-1 -> :n
|
||||
1 -> :s
|
||||
end
|
||||
|
||||
1 ->
|
||||
case z2 do
|
||||
0 -> :e
|
||||
end
|
||||
|
||||
-1 ->
|
||||
case z2 do
|
||||
0 -> :w
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def furthest_from_animal(loop) do
|
||||
loop
|
||||
|> Enum.reduce(
|
||||
%{},
|
||||
fn {pipe, steps}, acc ->
|
||||
Map.update(acc, pipe, [steps], fn p -> [steps | p] end)
|
||||
end
|
||||
)
|
||||
|> Stream.filter(fn {_, a} -> a |> Enum.count() == 2 end)
|
||||
|> Enum.reject(fn {_, [a, b]} -> a != b or a == 0 end)
|
||||
|> Enum.at(0)
|
||||
end
|
||||
|
||||
def find_animal(grid) do
|
||||
grid
|
||||
|> Enum.find(nil, fn {_, pipe} -> pipe == :animal end)
|
||||
|> elem(0)
|
||||
end
|
||||
def find_loop({i, j}, pipes, grid) do
|
||||
pipe = Map.get(grid, {i, j})
|
||||
connected_neighbors =
|
||||
for ii <- -1..1,
|
||||
jj <- -1..1,
|
||||
(ii == 0 or jj == 0) and !(ii == 0 and jj == 0),
|
||||
{z1, z2} = {i + ii, j + jj},
|
||||
z1 != -1 and z2 != -1,
|
||||
neighbor = Map.get(grid, {z1, z2}),
|
||||
connects?(pipe, neighbor, to_orientation({i, j}, {z1, z2})) do
|
||||
{z1, z2}
|
||||
end
|
||||
|
||||
connected_pipes_checked =
|
||||
Enum.filter(
|
||||
connected_neighbors,
|
||||
fn n -> Enum.any?(pipes, fn {p, _} -> n == p end) end
|
||||
)
|
||||
|
||||
if connected_pipes_checked
|
||||
|> Enum.count() == 2 do
|
||||
pipes
|
||||
else
|
||||
step = pipes |> Enum.count()
|
||||
|
||||
Enum.flat_map(
|
||||
connected_neighbors
|
||||
|> Enum.reject(fn n -> Enum.any?(pipes, fn {p, _} -> p == n end) end),
|
||||
fn {ii, jj} ->
|
||||
find_loop({ii, jj}, [{{i, j}, step} | pipes], grid)
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
defp loop_as_set(loop) do
|
||||
loop |> Stream.map(&elem(&1, 0)) |> MapSet.new()
|
||||
end
|
||||
|
||||
def to_string(pipe) do
|
||||
case pipe do
|
||||
:ground -> "."
|
||||
:animal -> "S"
|
||||
# List.to_string([])
|
||||
{:n, :s} -> <<0x2551::8>>
|
||||
{:e, :w} -> List.to_string([0x2550])
|
||||
{:n, :e} -> List.to_string([0x255A])
|
||||
{:n, :w} -> List.to_string([0x255D])
|
||||
{:s, :w} -> List.to_string([0x2557])
|
||||
{:s, :e} -> List.to_string([0x2554])
|
||||
_ -> ""
|
||||
end
|
||||
end
|
||||
|
||||
def get_tile_under_animal(loop) do
|
||||
# TODO
|
||||
end
|
||||
|
||||
def get_inside_count(grid, loop) do
|
||||
loop_set = loop_as_set(loop)
|
||||
|
||||
for room <-
|
||||
grid
|
||||
|> Enum.sort_by(fn {{x, y}, _} -> {y, x} end)
|
||||
|> Enum.map_reduce(
|
||||
%{n: false, s: false, o: 0, i: 0},
|
||||
fn {{x, y}, tile}, %{n: n, s: s, o: o, i: i} ->
|
||||
tile =
|
||||
if tile == :animal do
|
||||
get_tile_under_animal(loop)
|
||||
tile
|
||||
else
|
||||
tile
|
||||
end
|
||||
|
||||
{n, s, o, i} =
|
||||
if loop_set |> MapSet.member?({x, y}) do
|
||||
n = if PipeModule.opens_north?(tile), do: !n, else: n
|
||||
s = if PipeModule.opens_south?(tile), do: !s, else: s
|
||||
{n, s, o, i}
|
||||
else
|
||||
{i, o} =
|
||||
if n || s do
|
||||
i = i + 1
|
||||
{i, o}
|
||||
else
|
||||
o = o + 1
|
||||
{i, o}
|
||||
end
|
||||
|
||||
{n, s, o, i}
|
||||
end
|
||||
|
||||
tile_location =
|
||||
if loop_set |> MapSet.member?({x, y}),
|
||||
do: :loop,
|
||||
else:
|
||||
(case n || s do
|
||||
true -> :inside
|
||||
false -> :outside
|
||||
end)
|
||||
|
||||
{{{x, y}, {tile, tile_location}}, %{n: n, s: s, o: o, i: i}}
|
||||
end
|
||||
)
|
||||
|> elem(0),
|
||||
into: %{} do
|
||||
room
|
||||
end
|
||||
|> Map.values()
|
||||
|> Enum.filter(&(elem(&1, 1) == :inside))
|
||||
|> Enum.count()
|
||||
end
|
||||
|
||||
def opens_north?({a, b}) when a == :n or b == :n do
|
||||
true
|
||||
end
|
||||
|
||||
def opens_north?(_) do
|
||||
false
|
||||
end
|
||||
|
||||
def opens_south?({a, b}) when a == :s or b == :s do
|
||||
true
|
||||
end
|
||||
|
||||
def opens_south?(:animal) do
|
||||
true
|
||||
end
|
||||
|
||||
def opens_south?(_) do
|
||||
false
|
||||
end
|
||||
end
|
39
test/2023_day10_test.exs
Normal file
39
test/2023_day10_test.exs
Normal file
|
@ -0,0 +1,39 @@
|
|||
defmodule Day102023Tests do
|
||||
use ExUnit.Case
|
||||
|
||||
@test_input ~s"..F7.
|
||||
.FJ|.
|
||||
SJ.L7
|
||||
|F--J
|
||||
LJ..."
|
||||
|
||||
@pt_2_input ~s".F----7F7F7F7F-7....
|
||||
.|F--7||||||||FJ....
|
||||
.||.FJ||||||||L7....
|
||||
FJL7L7LJLJ||LJ.L-7..
|
||||
L--J.L7...LJS7F-7L7.
|
||||
....F-J..F7FJ|L7L7L7
|
||||
....L7.F7||L7|.L7L7|
|
||||
.....|FJLJ|FJ|F7|.LJ
|
||||
....FJL-7.||.||||...
|
||||
....L---J.LJ.LJLJ..."
|
||||
|
||||
|
||||
@grid PipeModule.parse(@test_input)
|
||||
@pt_2_grid PipeModule.parse(@pt_2_input)
|
||||
|
||||
assert "solves part1" do
|
||||
animal_coords = PipeModule.find_animal(@grid)
|
||||
loop = PipeModule.find_loop(animal_coords, [], @grid)
|
||||
{coord, [count, count]} = PipeModule.furthest_from_animal(loop)
|
||||
assert count == 8
|
||||
assert coord = {4, 2}
|
||||
end
|
||||
|
||||
assert "solves part 2" do
|
||||
animal_coords = PipeModule.find_animal(@pt_2_grid)
|
||||
loop = PipeModule.find_loop(animal_coords, [], @pt_2_grid)
|
||||
count = PipeModule.get_inside_count(@pt_2_grid, loop)
|
||||
assert count == 8
|
||||
end
|
||||
end
|
Loading…
Add table
Reference in a new issue