2023 Day 13
Mix.install([
{:req, "~> 0.4.5"},
{:vega_lite, "~> 0.1.8"},
{:kino_vega_lite, "~> 0.1.11"}
])
alias VegaLite, as: Vl
defmodule AOC do
@aoc_session System.fetch_env!("LB_AOC_SESSION")
def day(day, year) when day in 1..25 do
headers = [cookie: "session=#{@aoc_session}"]
req = Req.new(base_url: "https://adventofcode.com/#{year}/day/#{day}", headers: headers)
input = Req.get!(req, url: "/input").body
if input =~ "Please don't repeatedly request this endpoint before it unlocks",
do: {:error, "Day #{day} hasn't been unlocked yet"},
else: {:ok, %{req: req, input: input}}
end
def submit(answer, %{req: req}, part \\ :part_1) when part in [:part_1, :part_2] do
part = if part == :part_2, do: 2, else: 1
if !part_already_completed?(part, req) do
body = Req.post!(req, url: "/answer", form: [level: part, answer: answer]).body
cond do
body =~ "That's the right answer" -> {:correct, answer}
body =~ "your answer is too low" -> {:too_low, answer}
body =~ "your answer is too high" -> {:too_high, answer}
:else -> {:incorrect, answer}
end
else
{:already_completed_part, answer}
end
end
defp part_already_completed?(part, req) do
body = Req.get!(req).body
cond do
body =~ "Both parts of this puzzle are complete!" -> true
body =~ "The first half of this puzzle is complete!" -> part == 1
:else -> false
end
end
def nums(str) do
Regex.scan(~r/\d+/, str) |> Enum.map(fn [x] -> String.to_integer(x) end)
end
end
{:ok, day} = AOC.day(13, 2023)
Part 1
test_input = """
#.##..##.
..#.##.#.
##......#
##......#
..#.##.#.
..##..##.
#.#.##.#.
#...##..#
#....#..#
..##..###
#####.##.
#####.##.
..##..###
#....#..#
"""
defmodule P1 do
def solve(input) do
input
|> String.split("\n\n", trim: true)
|> Enum.map(&String.split(&1, "\n", trim: true))
|> Enum.map(fn rows ->
find_reflection(rows, :row) || rows |> transpose() |> find_reflection(:col)
end)
|> Enum.reduce({0, 0}, fn {type, n}, {r, c} ->
if type == :row, do: {r + n, c}, else: {r, c + n}
end)
|> then(fn {r, c} -> 100 * r + c end)
end
def transpose(rows) do
rows
|> Enum.map(&String.graphemes/1)
|> Enum.zip()
|> Enum.map(&(&1 |> Tuple.to_list() |> Enum.join()))
end
def find_reflection(rows, row_or_col) do
rows
|> Enum.chunk_every(2, 1, :discard)
|> Enum.with_index()
|> Enum.filter(fn {[a, b], _i} -> a == b end)
|> Enum.find_value(fn {_, i} ->
{a, b} = Enum.split(rows, i + 1)
if Enum.zip(Enum.reverse(a), b) |> Enum.drop(1) |> Enum.all?(fn {a, b} -> a == b end),
do: {row_or_col, i + 1},
else: nil
end)
end
end
test_input |> P1.solve()
# day.input |> P1.solve() |> AOC.submit(day)
Part 2
defmodule P2 do
def solve(input) do
input
|> String.split("\n\n", trim: true)
|> Enum.map(&String.split(&1, "\n", trim: true))
|> Enum.map(fn rows ->
find_reflection(rows, :row) || rows |> transpose() |> find_reflection(:col)
end)
|> Enum.reduce({0, 0}, fn {type, n}, {r, c} ->
if type == :row, do: {r + n, c}, else: {r, c + n}
end)
|> then(fn {r, c} -> 100 * r + c end)
end
def transpose(rows) do
rows
|> Enum.map(&String.graphemes/1)
|> Enum.zip()
|> Enum.map(&(&1 |> Tuple.to_list() |> Enum.join()))
end
def find_reflection(rows, row_or_col) do
rows
|> Enum.chunk_every(2, 1, :discard)
|> Enum.with_index()
|> Enum.filter(fn {[a, b], _i} ->
1 ==
Enum.zip(String.graphemes(a), String.graphemes(b))
|> Enum.count(fn {a, b} -> a != b end)
end)
|> Enum.find_value(fn {_, i} ->
{a, b} = Enum.split(rows, i + 1)
if Enum.zip(Enum.reverse(a), b) |> Enum.drop(1) |> Enum.all?(fn {a, b} -> a == b end),
do: {row_or_col, i + 1},
else: nil
end)
end
end
test_input |> P2.solve()
# day.input |> P2.solve()