Day 12, trying to catch up
parent
b1fc5a4790
commit
88b10160b5
@ -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…
Reference in New Issue