From a7beb06a289b9f9749b99c6ee151f923dbe89542 Mon Sep 17 00:00:00 2001 From: bluepython508 Date: Thu, 14 Dec 2023 09:38:23 +0000 Subject: [PATCH] Day 14 --- lib/aoc2023.ex | 11 ++++--- lib/day14.ex | 77 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/mix_tasks.ex | 20 ++++++++++++- tests/day14/1 | 10 +++++++ tests/day14/1.1 | 1 + tests/day14/1.2 | 1 + 6 files changed, 113 insertions(+), 7 deletions(-) create mode 100644 lib/day14.ex create mode 100644 tests/day14/1 create mode 100644 tests/day14/1.1 create mode 100644 tests/day14/1.2 diff --git a/lib/aoc2023.ex b/lib/aoc2023.ex index 4a60b2f..42c1c83 100644 --- a/lib/aoc2023.ex +++ b/lib/aoc2023.ex @@ -4,12 +4,11 @@ defmodule Aoc2023 do import Aoc2023.Common def main(input) do - {took, _} = :timer.tc fn -> - parsed = parse(input) - IO.puts("Part 1: #{part1(parsed)}") - IO.puts("Part 2: #{part2(parsed)}") - end - IO.puts("Took #{took / 1_000}ms") + {t_parse, parsed} = :timer.tc fn -> parse(input) end + {t_p1, _} = :timer.tc fn -> IO.puts("Part 1: #{part1(parsed)}") end + + {t_p2, _} = :timer.tc fn -> IO.puts("Part 2: #{part2(parsed)}") end + IO.puts("Took #{(t_parse + t_p1 + t_p2) / 1_000}ms (parse: #{t_parse / 1_000}ms, part1: #{t_p1 / 1_000}ms, part2: #{t_p2 / 1_000}ms)") end end end diff --git a/lib/day14.ex b/lib/day14.ex new file mode 100644 index 0000000..1a92811 --- /dev/null +++ b/lib/day14.ex @@ -0,0 +1,77 @@ +defmodule Aoc2023.Day14 do + use Aoc2023 + + def parse(input) do + input + |> lines + |> Enum.map(&String.to_charlist/1) + end + + defp replace_many_at(lst, idxs, val) do + idxs |> Enum.reduce(lst, &List.replace_at(&2, &1, val)) + end + + defp slide(grid) do + iter = fn recur, grid -> + case grid do + [last | [next | rest]] -> + idxs = + last + |> Enum.with_index() + |> Enum.filter(pipe(elem(0) |> Kernel.==(?O))) + |> Enum.map(pipe(elem(1))) + |> Enum.filter(&(Enum.at(next, &1) == ?.)) + + [ + last |> replace_many_at(idxs, ?.) + | recur.(recur, [next |> replace_many_at(idxs, ?O) | rest]) + ] + + [last] -> + [last] + end + end + + loop = fn recur, grid -> + grid_ = iter.(iter, grid) + if grid_ == grid, do: grid, else: recur.(recur, grid_) + end + + loop.(loop, grid |> Enum.reverse()) + end + + defp cycle(grid) do + for _ <- 1..4, reduce: grid do + # reverse |> transpose is rotation clockwise, but slide already reverses + grid -> grid |> slide |> transpose + end + end + + defp cycles(grid, n \\ 1_000_000_000, {cache_to_n, cache_from_n} \\ {%{}, %{}}) when n > 0 do + rep_n = Map.get(cache_to_n, grid) + if rep_n do + n_ = rep_n - Integer.mod(n, rep_n - n) + cache_from_n |> Map.get(n_) + else + grid_ = cycle(grid) + cycles(grid_, n - 1, {Map.put(cache_to_n, grid, n), Map.put(cache_from_n, n, grid)}) + end + end + + def part1(input) do + input + |> slide + |> Enum.with_index(1) + |> Enum.map(fn {row, i} -> Enum.count(row, &(&1 == ?O)) * i end) + |> Enum.sum() + end + + def part2(input) do + input + |> cycles() + |> Enum.reverse + |> Enum.with_index(1) + |> Enum.map(fn {row, i} -> Enum.count(row, &(&1 == ?O)) * i end) + |> Enum.sum() + end +end diff --git a/lib/mix_tasks.ex b/lib/mix_tasks.ex index 4029411..147f708 100644 --- a/lib/mix_tasks.ex +++ b/lib/mix_tasks.ex @@ -15,6 +15,7 @@ defmodule Mix.Tasks.Aoc do defp module(11), do: Aoc2023.Day11 defp module(12), do: Aoc2023.Day12 defp module(13), do: Aoc2023.Day13 + defp module(14), do: Aoc2023.Day14 # [MODULE INSERTION POINT] defp base_dir(), do: System.get_env("AOC_BASE") @@ -107,6 +108,10 @@ defmodule Mix.Tasks.Aoc do create_file("#{tests_dir(day)}#{last + 1}", IO.read(:stdio, :eof)) end + defp run(day, ["test", "expected", test, part, value]) do + create_file("#{tests_dir(day)}#{test}.#{part}", value) + end + defp run(day, ["test", "expected", test, part]) do create_file("#{tests_dir(day)}#{test}.#{part}", IO.read(:stdio, :eof)) end @@ -162,6 +167,19 @@ defmodule Mix.Tasks.Aoc do run_file(day, "#{base_dir()}/inputs/day#{day}") end + defp run(day, ["run", "submit", part]) do + run(day, ["fetch"]) + mod = module(day) + parsed = mod.parse(read("#{base_dir()}/inputs/day#{day}")) + val = case part do + "1" -> mod.part1(parsed) + "2" -> mod.part2(parsed) + end + HTTPoison.start() + resp = HTTPoison.post!("https://adventofcode.com/2023/day/#{day}/answer", {:form, [level: part, answer: stringify(val)]}, "user-agent": "aoc-ex by ben@soroos.net", cookie: "session=#{System.get_env("AOC_SESSION")}") + resp |> dbg + end + defp run(day, ["run", "-"]) do run_file(day) end @@ -209,4 +227,4 @@ defmodule Mix.Tasks.Aoc do {:noreply, state} end end -end \ No newline at end of file +end diff --git a/tests/day14/1 b/tests/day14/1 new file mode 100644 index 0000000..5a24dce --- /dev/null +++ b/tests/day14/1 @@ -0,0 +1,10 @@ +O....#.... +O.OO#....# +.....##... +OO.#O....O +.O.....O#. +O.#..O.#.# +..O..#O..O +.......O.. +#....###.. +#OO..#.... diff --git a/tests/day14/1.1 b/tests/day14/1.1 new file mode 100644 index 0000000..d55f9f7 --- /dev/null +++ b/tests/day14/1.1 @@ -0,0 +1 @@ +136 \ No newline at end of file diff --git a/tests/day14/1.2 b/tests/day14/1.2 new file mode 100644 index 0000000..4b6f9c3 --- /dev/null +++ b/tests/day14/1.2 @@ -0,0 +1 @@ +64 \ No newline at end of file