Day 12, trying to catch up

master
Dustin Swan 3 weeks ago
parent b1fc5a4790
commit 88b10160b5
Signed by: dustinswan
GPG Key ID: AB49BD6B2B3A6377

@ -0,0 +1,155 @@
defmodule Day12 do
def run do
garden =
File.read!("data.txt")
|> String.trim()
|> String.split("\n")
|> Enum.map(fn el -> String.graphemes(el) end)
height = length(garden)
width = length(hd(garden))
# generate a list of regions (a region is a lists of coords)
regions =
Enum.reduce(0..(height - 1), [], fn row, acc1 ->
Enum.reduce(0..(width - 1), acc1, fn col, acc2 ->
# does this coordinate already exist in a region
if Enum.any?(acc2, fn x -> Enum.member?(x, {row, col}) end) do
acc2
else
char = element_at({row, col}, garden)
res = findRegion(char, {row, col}, garden, [])
if is_nil(res), do: acc2, else: [res | acc2]
end
end)
end)
part1 = Enum.reduce(regions, 0, fn r, acc -> acc + perimeter(r, garden) * length(r) end)
dbg(part1)
# part2 = Enum.map(regions, fn r -> sides(r, garden) end)
part2 = Enum.reduce(regions, 0, fn r, acc -> acc + sides(r, garden) * length(r) end)
dbg(part2)
end
defp sides(region, garden) do
# scan from north to south, west to east
height = length(garden)
width = length(hd(garden))
left_to_right =
Enum.reduce(0..(height - 1), %{top: false, bottom: false, sides: 0}, fn row, acc ->
Enum.reduce(0..(width - 1), acc, fn col, acc2 ->
el = element_at({row, col}, garden)
in_region = Enum.member?(region, {row, col})
if in_region do
# if the cell above is different, and it wasn't before, we're starting a new side
upper_cell_different = element_at({row - 1, col}, garden) != el
up_side = if upper_cell_different and !acc2.top, do: 1, else: 0
# same with cell below
lower_cell_different = element_at({row + 1, col}, garden) != el
down_side = if lower_cell_different and !acc2.bottom, do: 1, else: 0
%{
top: upper_cell_different,
bottom: lower_cell_different,
sides: acc2.sides + up_side + down_side
}
else
# we are not in a region
%{top: false, bottom: false, sides: acc2.sides}
end
end)
end)
top_to_bottom =
Enum.reduce(0..(width - 1), %{left: false, right: false, sides: 0}, fn col, acc ->
Enum.reduce(0..(height - 1), acc, fn row, acc2 ->
el = element_at({row, col}, garden)
in_region = Enum.member?(region, {row, col})
if in_region do
# if the cell to the left is different, and it wasn't before, we're starting a new side
left_cell_different = element_at({row, col - 1}, garden) != el
left_side = if left_cell_different and !acc2.left, do: 1, else: 0
# same with cell to the right
right_cell_different = element_at({row, col + 1}, garden) != el
right_side = if right_cell_different and !acc2.right, do: 1, else: 0
%{
left: left_cell_different,
right: right_cell_different,
sides: acc2.sides + left_side + right_side
}
else
# we are not in a region
%{left: false, right: false, sides: acc2.sides}
end
end)
end)
left_to_right.sides + top_to_bottom.sides
end
defp perimeter(region, garden) do
# scan from north to south, west to east, when we enter or leave a region, add 1
height = length(garden)
width = length(hd(garden))
left_to_right =
Enum.reduce(0..height, %{in_region: false, edges: 0}, fn row, acc ->
Enum.reduce(0..width, acc, fn col, acc2 ->
# el = element_at({row, col}, garden)
in_region = Enum.member?(region, {row, col})
edge = if in_region != acc2.in_region, do: 1, else: 0
%{in_region: in_region, edges: acc2.edges + edge}
end)
end)
top_to_bottom =
Enum.reduce(0..width, %{in_region: false, edges: 0}, fn col, acc ->
Enum.reduce(0..height, acc, fn row, acc2 ->
# el = element_at({row, col}, garden)
in_region = Enum.member?(region, {row, col})
edge = if in_region != acc2.in_region, do: 1, else: 0
%{in_region: in_region, edges: acc2.edges + edge}
end)
end)
left_to_right.edges + top_to_bottom.edges
end
defp findRegion(char, {row, col}, garden, region) do
# find the char at coord
el = element_at({row, col}, garden)
# if its out of bounds, it is a different char, or it's already in this region
if is_nil(el) or char != el or Enum.member?(region, {row, col}) do
nil
else
# its in the region! add it
new_region = [{row, col} | region]
# check the 4 neighbors
[{row - 1, col}, {row, col - 1}, {row + 1, col}, {row, col + 1}]
|> Enum.reject(fn el -> Enum.member?(new_region, el) end)
|> Enum.reduce(new_region, fn c, acc ->
res = findRegion(char, c, garden, acc)
if is_nil(res), do: acc, else: res
end)
end
end
defp element_at({x, y}, garden) do
if x < 0 or y < 0 do
nil
else
row = Enum.at(garden, x)
if is_nil(row), do: nil, else: Enum.at(row, y)
end
end
end
Day12.run()
Loading…
Cancel
Save