From 6043c8169771c3849707dda8cf59d92044f9ddfe Mon Sep 17 00:00:00 2001 From: Neil Smith Date: Fri, 24 Jan 2025 15:21:16 +0000 Subject: [PATCH] Done day 1 in Elixir --- lib/advent_of_code/day_01.ex | 57 +++++++++++- lib/advent_of_code/input.ex | 135 +++++++++++++--------------- mix.exs | 6 +- mix.lock | 2 + test/advent_of_code/day_01_test.exs | 12 +-- 5 files changed, 130 insertions(+), 82 deletions(-) diff --git a/lib/advent_of_code/day_01.ex b/lib/advent_of_code/day_01.ex index 8e532a5..c4933c2 100644 --- a/lib/advent_of_code/day_01.ex +++ b/lib/advent_of_code/day_01.ex @@ -1,7 +1,60 @@ + defmodule AdventOfCode.Day01 do - def part1(_args) do + require Logger + use Flow + + def part1(input \\ nil) do + input = if is_nil(input), do: AdventOfCode.Input.get!(1), else: input + {lefts, rights} = parse_input(input) + s_lefts = Enum.sort(lefts) + s_rights = Enum.sort(rights) + + Enum.zip(s_lefts, s_rights) + |> Enum.map(fn {l, r} -> abs(l - r) end) + |> Enum.sum() + end + + def part2(input \\ nil) do + input = if is_nil(input), do: AdventOfCode.Input.get!(1), else: input + {lefts, rights} = parse_input(input) + + right_counts = rights + |> Flow.from_enumerable() + |> Flow.partition(key: fn x -> rem(x, 100) end) + |> Flow.reduce(fn -> %{} end, fn item, acc -> + Map.update(acc, item, 1, &(&1 + 1)) + end) + |> Enum.reduce(%{}, fn {n, c}, acc -> Map.merge(%{n => c}, acc) end) + + # right_counts = Enum.frequencies(rights) + + similarity = + lefts + |> Enum.map(fn x -> x * Map.get(right_counts, x, 0) end) + |> Enum.sum() + + similarity + end + + defp parse_input(input) do + input + |> String.split("\n") + |> Task.async_stream(&parse_line/1, ordered: false) + |> Enum.map(fn r -> elem(r, 1) end) + |> Enum.unzip() + + # |> String.split("\n") + # |> Enum.map(&String.split/1) + # # |> Enum.map(fn xs -> xs |> Enum.map(&String.to_integer/1) end) + # |> Enum.map(fn xs -> Enum.map(xs, &String.to_integer/1) end) + # |> Enum.map(fn xs -> {Enum.at(xs, 0), Enum.at(xs, 1)} end) + # |> Enum.unzip() end - def part2(_args) do + defp parse_line(inline) do + lp = inline + |> String.split() + |> Enum.map(&String.to_integer/1) + {Enum.at(lp, 0), Enum.at(lp, 1)} end end diff --git a/lib/advent_of_code/input.ex b/lib/advent_of_code/input.ex index 38a8fc8..df956cc 100644 --- a/lib/advent_of_code/input.ex +++ b/lib/advent_of_code/input.ex @@ -13,79 +13,70 @@ defmodule AdventOfCode.Input do cache, it will be retrieved from the server if `allow_network?: true` is configured and your cookie is setup. """ - def get!(day, year \\ nil) - def get!(day, nil), do: get!(day, default_year()) - def get!(day, year) do - cond do - in_cache?(day, year) -> - from_cache!(day, year) - - allow_network?() -> - download!(day, year) - - true -> - raise "Cache miss for day #{day} of year #{year} and `:allow_network?` is not `true`" - end - end - - @doc """ - If, somehow, your input is invalid or mangled and you want to delete it from - your cache so you can re-fetch it, this will save your bacon. - Please don't use this to retrieve the input from the server repeatedly! - """ - def delete!(day, year \\ nil) - def delete!(day, nil), do: delete!(day, default_year()) - def delete!(day, year), do: File.rm!(cache_path(day, year)) - - defp cache_path(day, year), do: Path.join(cache_dir(), "/#{year}/#{day}.aocinput") - defp in_cache?(day, year), do: File.exists?(cache_path(day, year)) - - defp store_in_cache!(day, year, input) do - path = cache_path(day, year) - :ok = path |> Path.dirname() |> File.mkdir_p() - :ok = File.write(path, input) - end - - defp from_cache!(day, year), do: File.read!(cache_path(day, year)) - - defp download!(day, year) do - {:ok, {{~c"HTTP/1.1", 200, ~c"OK"}, _, input}} = - :httpc.request( - :get, - {~c"https://adventofcode.com/#{year}/day/#{day}/input", headers()}, - [], - [] - ) - - store_in_cache!(day, year, input) - - to_string(input) + def get!(day, suffix \\ "") do + day_string = Integer.to_string(day) |> String.pad_leading(2, "0") + filename = "../data/advent#{day_string}#{suffix}.txt" + File.read!(filename) end - defp cache_dir do - config() - |> Keyword.get( - :cache_dir, - Path.join([System.get_env("XDG_CACHE_HOME", "~/.cache"), "/advent_of_code_inputs"]) - ) - |> Path.expand() - end - - defp default_year do - case :calendar.local_time() do - {{y, 12, _}, _} -> y - {{y, _, _}, _} -> y - 1 - end - end - - defp config, do: Application.get_env(:advent_of_code, __MODULE__) - defp allow_network?, do: Keyword.get(config(), :allow_network?, false) - - defp headers, - do: [ - {~c"user-agent", - ~c"github.com/mhanberg/advent-of-code-elixir-starter by aoc@mitchellhanberg.com"}, - {~c"cookie", String.to_charlist("session=" <> Keyword.get(config(), :session_cookie))} - ] + # @doc """ + # If, somehow, your input is invalid or mangled and you want to delete it from + # your cache so you can re-fetch it, this will save your bacon. + # Please don't use this to retrieve the input from the server repeatedly! + # """ + # def delete!(day, year \\ nil) + # def delete!(day, nil), do: delete!(day, default_year()) + # def delete!(day, year), do: File.rm!(cache_path(day, year)) + + # defp cache_path(day, year), do: Path.join(cache_dir(), "/#{year}/#{day}.aocinput") + # defp in_cache?(day, year), do: File.exists?(cache_path(day, year)) + + # defp store_in_cache!(day, year, input) do + # path = cache_path(day, year) + # :ok = path |> Path.dirname() |> File.mkdir_p() + # :ok = File.write(path, input) + # end + + # defp from_cache!(day, year), do: File.read!(cache_path(day, year)) + + # defp download!(day, year) do + # {:ok, {{~c"HTTP/1.1", 200, ~c"OK"}, _, input}} = + # :httpc.request( + # :get, + # {~c"https://adventofcode.com/#{year}/day/#{day}/input", headers()}, + # [], + # [] + # ) + + # store_in_cache!(day, year, input) + + # to_string(input) + # end + + # defp cache_dir do + # config() + # |> Keyword.get( + # :cache_dir, + # Path.join([System.get_env("XDG_CACHE_HOME", "~/.cache"), "/advent_of_code_inputs"]) + # ) + # |> Path.expand() + # end + + # defp default_year do + # case :calendar.local_time() do + # {{y, 12, _}, _} -> y + # {{y, _, _}, _} -> y - 1 + # end + # end + + # defp config, do: Application.get_env(:advent_of_code, __MODULE__) + # defp allow_network?, do: Keyword.get(config(), :allow_network?, false) + + # defp headers, + # do: [ + # {~c"user-agent", + # ~c"github.com/mhanberg/advent-of-code-elixir-starter by aoc@mitchellhanberg.com"}, + # {~c"cookie", String.to_charlist("session=" <> Keyword.get(config(), :session_cookie))} + # ] end diff --git a/mix.exs b/mix.exs index 017ffd1..dd68e9a 100644 --- a/mix.exs +++ b/mix.exs @@ -14,7 +14,7 @@ defmodule AdventOfCode.MixProject do # Run "mix help compile.app" to learn about applications. def application do [ - extra_applications: [:logger, :inets, :public_key, :ssl] + extra_applications: [:logger] ] end @@ -22,7 +22,9 @@ defmodule AdventOfCode.MixProject do defp deps do [ {:benchee, "~> 1.0"}, - {:ssl_verify_fun, "~> 1.1.7", manager: :rebar3, override: true} + {:gen_stage, "~> 1.0"}, + {:flow, "~> 1.0"} + # {:ssl_verify_fun, "~> 1.1.7", manager: :rebar3, override: true} ] end end diff --git a/mix.lock b/mix.lock index 207c2e1..5ca195e 100644 --- a/mix.lock +++ b/mix.lock @@ -1,6 +1,8 @@ %{ "benchee": {:hex, :benchee, "1.1.0", "f3a43817209a92a1fade36ef36b86e1052627fd8934a8b937ac9ab3a76c43062", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}], "hexpm", "7da57d545003165a012b587077f6ba90b89210fd88074ce3c60ce239eb5e6d93"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, + "flow": {:hex, :flow, "1.2.4", "1dd58918287eb286656008777cb32714b5123d3855956f29aa141ebae456922d", [:mix], [{:gen_stage, "~> 1.0", [hex: :gen_stage, repo: "hexpm", optional: false]}], "hexpm", "874adde96368e71870f3510b91e35bc31652291858c86c0e75359cbdd35eb211"}, + "gen_stage": {:hex, :gen_stage, "1.2.1", "19d8b5e9a5996d813b8245338a28246307fd8b9c99d1237de199d21efc4c76a1", [:mix], [], "hexpm", "83e8be657fa05b992ffa6ac1e3af6d57aa50aace8f691fcf696ff02f8335b001"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"}, } diff --git a/test/advent_of_code/day_01_test.exs b/test/advent_of_code/day_01_test.exs index d264e46..ffc96d0 100644 --- a/test/advent_of_code/day_01_test.exs +++ b/test/advent_of_code/day_01_test.exs @@ -3,19 +3,19 @@ defmodule AdventOfCode.Day01Test do import AdventOfCode.Day01 - @tag :skip + # @tag :skip test "part1" do - input = nil + input = AdventOfCode.Input.get!(1, "a") result = part1(input) - assert result + assert result == 11 end - @tag :skip + # @tag :skip test "part2" do - input = nil + input = AdventOfCode.Input.get!(1, "a") result = part2(input) - assert result + assert result == 31 end end -- 2.43.0