diff --git a/README.md b/README.md index 935209f..3648048 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ My Elixir solutions for [Advent of Code](https://adventofcode.com/) (all years). -

444 stars

-

25 stars
+

445 stars

+

26 stars
44 stars
50 stars
46 stars
diff --git a/lib/y2024/README.md b/lib/y2024/README.md index 766d5fe..eac4a5b 100644 --- a/lib/y2024/README.md +++ b/lib/y2024/README.md @@ -2,7 +2,7 @@ My Elixir solutions for [Advent of Code 2024](https://adventofcode.com/2024). -25 stars +26 stars ## Benchmarks @@ -38,5 +38,6 @@ day 11, part 1 838.21 1.19 ms ±4.86% 1.21 ms 1. day 11, part 2 19.52 51.22 ms ±1.86% 51.26 ms 53.37 ms day 12, part 1 7.16 139.70 ms ±1.82% 139.22 ms 144.95 ms day 12, part 2 6.84 146.22 ms ±1.32% 145.82 ms 151.25 ms -day 13, part 1 4.91 203.63 ms ±0.46% 203.49 ms 206.75 ms +day 13, part 1 1.31 K 765.25 μs ±3.72% 754 μs 837.77 μs +day 13, part 2 1.33 K 753.73 μs ±3.42% 744.42 μs 822.51 μs ``` diff --git a/lib/y2024/day13.ex b/lib/y2024/day13.ex index 7df8a0f..503df24 100644 --- a/lib/y2024/day13.ex +++ b/lib/y2024/day13.ex @@ -2,7 +2,10 @@ defmodule Y2024.Day13 do use Advent.Day, no: 13 @doc """ - iex> Day13.part1([%{buttons: %{a: %{x: 94, y: 34, cost: 3}, b: %{x: 22, y: 67, cost: 1}}, prize: %{x: 8400, y: 5400}}]) + iex> Day13.part1([ + ...> %{buttons: %{a: %{x: 94, y: 34, cost: 3}, b: %{x: 22, y: 67, cost: 1}}, + ...> prize: %{x: 8400, y: 5400}} + ...> ]) 280 """ def part1(machines) do @@ -14,51 +17,63 @@ defmodule Y2024.Day13 do end) end - # @doc """ - # iex> Day13.part2("update or delete me") - # "update or delete me" - # """ - # def part2(input) do - # input - # end + @doc """ + # iex> Day13.part2([ + # ...> %{buttons: %{a: %{x: 26, y: 66, cost: 3}, b: %{x: 67, y: 21, cost: 1}}, + # ...> prize: %{x: 10000000012748, y: 10000000012176}} + # ...> ]) + # 459236326669 + """ + def part2(machines) do + machines + |> Enum.map(&find_solution/1) + |> Enum.reduce(0, fn + %{a: a, b: b}, acc -> acc + a.tokens + b.tokens + nil, acc -> acc + end) + end @doc """ iex> Day13.find_solution(%{buttons: %{a: %{x: 94, y: 34, cost: 3}, b: %{x: 22, y: 67, cost: 1}}, prize: %{x: 8400, y: 5400}}) %{a: %{presses: 80, tokens: 240}, b: %{presses: 40, tokens: 40}} """ - def find_solution(%{buttons: %{a: a, b: b}, prize: prize}) do - a_presses = Enum.min([100, div(prize.x, a.x), div(prize.y, a.y)]) - b_presses = Enum.min([100, div(prize.x, b.x), div(prize.y, b.y)]) + def find_solution(%{buttons: %{a: button_a, b: button_b}, prize: prize}) do + # There are two simultaneous equations that need solving = flip the + # button formulas we have to solve for a and b. + a = %{one: button_a.x * button_b.cost, two: button_a.y * button_b.cost} + b = %{one: button_b.x * button_a.cost, two: button_b.y * button_a.cost} + c = %{one: prize.x, two: prize.y} + + a_presses = (b.two * c.one - b.one * c.two) / (b.two * a.one - b.one * a.two) - result = - for( - a_press <- 0..a_presses, - b_press <- 0..b_presses, - do: {a_press, b_press, a.cost * a_press + b.cost * b_press} - ) - |> Enum.sort_by(fn {_, _, cost} -> cost end) - |> Enum.find(fn {a_press, b_press, _cost} -> - a_press * a.x + b_press * b.x == prize.x && a_press * a.y + b_press * b.y == prize.y - end) + # The last part should really be b.one but doesn't need the cost factor applied + b_presses = (a.one * trunc(a_presses) - c.one) / (-1 * button_b.x) - if result do + # Fractional values aren't valid - you can't press a button 0.5 times + if a_presses == trunc(a_presses) && b_presses == trunc(b_presses) do %{ - a: %{tokens: a.cost * elem(result, 0), presses: elem(result, 0)}, - b: %{tokens: b.cost * elem(result, 1), presses: elem(result, 1)} + a: %{presses: trunc(a_presses), tokens: trunc(a_presses) * button_a.cost}, + b: %{presses: trunc(b_presses), tokens: trunc(b_presses) * button_b.cost} } + else + nil end end @doc """ - iex> Day13.parse_input("Button A: X+94, Y+34\\nButton B: X+22, Y+67\\nPrize: X=8400, Y=5400") + iex> Day13.parse_input("Button A: X+94, Y+34\\nButton B: X+22, Y+67\\nPrize: X=8400, Y=5400", 0) [%{buttons: %{a: %{x: 94, y: 34, cost: 3}, b: %{x: 22, y: 67, cost: 1}}, prize: %{x: 8400, y: 5400}}] """ - def parse_input(input) do + def parse_input(input, offset) do input |> String.split("\n\n", trim: true) |> Enum.map(fn machine -> [a, b, prize] = String.split(machine, "\n", trim: true) - %{buttons: %{a: parse_button(a, 3), b: parse_button(b, 1)}, prize: parse_prize(prize)} + + %{ + buttons: %{a: parse_button(a, 3), b: parse_button(b, 1)}, + prize: parse_prize(prize, offset) + } end) end @@ -67,11 +82,11 @@ defmodule Y2024.Day13 do %{cost: cost, x: String.to_integer(x), y: String.to_integer(y)} end - defp parse_prize(string) do + defp parse_prize(string, offset) do [[x], [y]] = Regex.scan(~r/\d+/, string) - %{x: String.to_integer(x), y: String.to_integer(y)} + %{x: String.to_integer(x) + offset, y: String.to_integer(y) + offset} end - def part1_verify, do: input() |> parse_input() |> part1() - # def part2_verify, do: input() |> parse_input() |> part2() + def part1_verify, do: input() |> parse_input(0) |> part1() + def part2_verify, do: input() |> parse_input(10_000_000_000_000) |> part2() end diff --git a/test/y2024/day13_test.exs b/test/y2024/day13_test.exs index 0bcdf78..3dfbd01 100644 --- a/test/y2024/day13_test.exs +++ b/test/y2024/day13_test.exs @@ -4,5 +4,5 @@ defmodule Y2024.Day13Test do doctest Day13 test "verification, part 1", do: assert(Day13.part1_verify() == 27157) - # test "verification, part 2", do: assert(Day13.part2_verify() == "update or delete me") + test "verification, part 2", do: assert(Day13.part2_verify() == 104_015_411_578_548) end