advent_of_code/elixir/lib/Y2023/2023_day6.ex
2024-11-15 21:03:49 -05:00

74 lines
1.9 KiB
Elixir

defmodule Mix.Tasks.Day6 do
use Mix.Task
def parse(input, :part1) do
input
|> Enum.map(fn s ->
String.split(s, " ")
|> Enum.reject(fn s -> s === "" or s === "Time:" or s === "Distance:" end)
end)
|> Enum.map(fn i ->
i
|> Enum.map(fn s ->
s |> String.trim() |> String.to_integer()
end)
end)
end
def parse(input, :part2) do
input
|> Enum.map(fn s ->
String.split(s, " ")
|> Enum.reject(fn s -> s === "" or s === "Time:" or s === "Distance:" end)
end)
|> Enum.map(fn u ->
u
|> Enum.join("")
|> String.trim()
|> String.to_integer()
end)
end
def distance_traveled_in_n_seconds(seconds, duration) do
seconds * (duration - seconds)
end
def polynomial_roots(a, b, c) do
[
(-b + :math.sqrt(b ** 2 - 4 * a * c)) / (2 * a),
(-b - :math.sqrt(b ** 2 - 4 * a * c)) / (2 * a)
]
end
def solve([time, distance], p) when p === :part2 do
# number of negative integer solutions to the polynomial
# x^2 - tx + d < 0
# where x is time spent holding button, t is the duration of the race (seconds), and d is the distance to beat
roots = polynomial_roots(1, -time, distance)
max = roots |> Enum.max()
min = roots |> Enum.min()
floor(max) - floor(min)
end
def solve([times, distances], p) when p === :part1 do
for {time, index} <- times |> Enum.with_index(),
seconds <- 1..(time - 1),
distance_traveled_in_n_seconds(seconds, time) > distances |> Enum.at(index),
reduce: %{} do
acc ->
acc |> Map.update(time, [seconds], fn u -> [seconds | u] end)
end
|> Map.values()
|> Enum.reduce(
1,
fn a, acc ->
acc * (a |> Enum.count())
end
)
end
def run(args) do
part = if args |> Enum.at(0) == "1", do: :part1, else: :part2
IO.stream() |> parse(part) |> solve(part) |> IO.puts()
end
end