Day 11
Mix.install([
{:kino, "~> 0.7.0"}
])
IEx.Helpers.c("/Users/johnb/dev/2023adventOfCode/advent_of_code.ex")
alias AdventOfCode, as: AOC
alias Kino.Input
# Note: when making the next template, something like this works well:
# `cat day04.livemd | sed 's/03/04/' > day04.livemd`
#
Installation and Data
input_p1example = Kino.Input.textarea("Example Data")
input_p1puzzleInput = Kino.Input.textarea("Puzzle Input")
input_source_select =
Kino.Input.select("Source", [{:example, "example"}, {:puzzle_input, "puzzle input"}])
p1data = fn ->
(Kino.Input.read(input_source_select) == :example &&
Kino.Input.read(input_p1example)) ||
Kino.Input.read(input_p1puzzleInput)
end
Part 1
defmodule Day11 do
def grid_cells(grid) do
0..grid.last_cell
end
def grid_rows(grid) do
grid_cells(grid)
|> Enum.chunk_every(grid.grid_width)
end
def display_grid(grid, text \\ nil) do
text && IO.puts("--- #{text}")
0..grid.last_cell
|> Enum.chunk_every(grid.grid_width)
# |> IO.inspect(label: "Grid chunks")
|> Enum.map(fn indexes ->
indexes
|> Enum.map(fn index ->
# For a known-printable grid:
grid[index]
# For a somewhat-printable grid:
# (grid[index] >= @max_display) && "." || (@ascii_zero + grid[index])
end)
|> Enum.join("")
|> IO.puts()
end)
grid
end
def grid_x(grid, cell), do: rem(cell, grid.grid_width)
def grid_y(grid, cell), do: div(cell, grid.grid_width)
def invert(grid) do
grid_cells(grid)
|> Enum.reduce(
%{
grid
| grid_width: grid.grid_height,
grid_height: grid.grid_width,
last_cell: grid.last_cell
},
fn cell, acc ->
Map.put(acc, grid_x(grid, cell) * grid.grid_height + grid_y(grid, cell), grid[cell])
end
)
end
def distance(x1, x2, y1, y2) do
abs(x1 - x2) + abs(y1 - y2)
end
def distances(grid, list, cell) do
x = grid_x(grid, cell)
y = div(cell, grid.grid_width)
list
|> Enum.map(fn galaxy ->
distance(x, galaxy.x, y, galaxy.y)
end)
end
def add_galaxy(grid, list_of_galaxies, cell) do
distances = distances(grid, list_of_galaxies, cell)
new_galaxy = %{
cell: cell,
x: grid_x(grid, cell),
y: grid_y(grid, cell),
distances: distances
}
[new_galaxy | list_of_galaxies]
end
@empty_space "."
def dotted_line?(line) do
line
|> String.split("", trim: true)
|> Enum.all?(fn char -> char == @empty_space end)
end
def to_text_grid(grid) do
grid_rows(grid)
|> Enum.map(fn row ->
Enum.map(row, fn x -> grid[x] end)
|> Enum.join("")
end)
|> Enum.join("\n")
end
def expand_rows(text) do
AOC.as_single_lines(text)
|> Enum.map(fn line ->
if dotted_line?(line) do
[line, line]
else
line
end
end)
|> List.flatten()
|> Enum.join("\n")
end
def solve(text) do
grid =
text
|> expand_rows()
|> AOC.as_grid()
# |> display_grid("before invert")
|> invert()
|> to_text_grid()
# |> IO.inspect(label: "before 2nd expansion")
|> expand_rows()
# |> IO.inspect(label: "after 2nd expansion")
|> AOC.as_grid()
|> invert()
# |> display_grid("after 2nd invert")
galaxies =
grid
# |> IO.inspect(label: "before collecting galaxies")
|> grid_cells()
|> Enum.reduce([], fn cell, list_of_galaxies ->
case grid[cell] do
"." -> list_of_galaxies
"#" -> add_galaxy(grid, list_of_galaxies, cell)
end
end)
galaxies
|> Enum.map(fn galaxy -> galaxy.distances end)
|> List.flatten()
# |> IO.inspect(label: "before sum")
|> Enum.sum()
end
def add_galaxy2(grid, list_of_galaxies, cell) do
new_galaxy = %{
x: grid_x(grid, cell),
y: grid_y(grid, cell)
}
[new_galaxy | list_of_galaxies]
end
def reverse_expansions(text) do
AOC.as_single_lines(text)
|> Enum.with_index()
|> Enum.reduce([], fn {line, index}, acc ->
if dotted_line?(line) do
[index | acc]
else
acc
end
end)
# |> IO.inspect()
end
def solve2(text) do
grid = AOC.as_grid(text)
# just collect x,y values for each galaxy
galaxies =
grid
# |> IO.inspect(label: "before collecting galaxies")
|> grid_cells()
|> Enum.reduce([], fn cell, list_of_galaxies ->
case grid[cell] do
"." -> list_of_galaxies
"#" -> add_galaxy2(grid, list_of_galaxies, cell)
end
end)
# |> IO.inspect(label: "galaxies")
# for each blank row, add N to everything below its Y value
# For N of 10, 100, 1_000_000
n = 1_000_000 - 1
rows_to_add = reverse_expansions(text)
# |> IO.inspect(label: "rows_to_add")
cols_to_add =
text
|> AOC.as_grid()
|> invert()
|> to_text_grid()
|> reverse_expansions()
# |> IO.inspect(label: "cols_to_add")
far_galaxies =
Enum.map(galaxies, fn gal ->
Enum.reduce(rows_to_add, gal, fn row, galaxy ->
(galaxy.y >= row && %{galaxy | y: n + galaxy.y}) || galaxy
end)
end)
# |> IO.inspect(label: "far_galaxies1")
far_galaxies =
Enum.map(far_galaxies, fn gal ->
Enum.reduce(cols_to_add, gal, fn col, galaxy ->
(galaxy.x >= col && %{galaxy | x: n + galaxy.x}) || galaxy
end)
end)
# |> IO.inspect(label: "far_galaxies2")
gals = for gal1 <- far_galaxies, gal2 <- far_galaxies, do: {gal1, gal2}
Enum.reduce(gals, 0, fn {g1, g2}, acc -> acc + distance(g1.x, g2.x, g1.y, g2.y) end)
|> div(2)
end
end
p1data.()
|> Day11.solve()
|> IO.inspect(label: "\n*** Part 1 solution (example: 374)")
# 9536038
p1data.()
|> Day11.solve2()
|> IO.inspect(label: "\n*** Part 2 solution (example: 1030/8410)")
# 53414166 is too low
# 447744640566