ingredients = File.stream!("inputs/day15.txt") |> Enum.filter(fn line -> String.trim(line) != "" end) |> Enum.map(fn line -> Regex.run( ~r/\w+: capacity ([+-]?\d+), durability ([+-]?\d+), flavor ([+-]?\d+), texture ([+-]?\d+), calories ([+-]?\d+)/, line ) |> Stream.drop(1) |> Stream.map(&Integer.parse/1) |> Enum.map(fn {i, _} -> i end) end) ingredientsWithoutCalories = Enum.map(ingredients, fn line -> {_calories, withoutCalories} = List.pop_at(line, -1) withoutCalories end) ingredientsWithCalories = Enum.map(ingredients, fn line -> List.pop_at(line, -1) end) defmodule RecipeFinder do def findTastiestRecipe([lastIngredient], remainingSpoons, currentMixture) do Enum.zip(lastIngredient, currentMixture) |> Enum.map(fn {next, current} -> next * remainingSpoons + current end) |> Enum.map(&max(&1, 0)) |> Enum.product() end def findTastiestRecipe([nextIngredient | remainingIngredients], remainingSpoons, currentMixture) do Enum.map(0..remainingSpoons, fn nextSpoons -> findTastiestRecipe( remainingIngredients, remainingSpoons - nextSpoons, Enum.zip(nextIngredient, currentMixture) |> Enum.map(fn {next, current} -> next * nextSpoons + current end) ) end) |> Enum.max() end def findTastiestRecipeWithExactCalories( [{lastIngredientCalories, _}], remainingSpoons, remainingCalories, _ ) when lastIngredientCalories * remainingSpoons != remainingCalories do nil end def findTastiestRecipeWithExactCalories( [{_, lastIngredient}], remainingSpoons, _, currentMixture ) do Enum.zip(lastIngredient, currentMixture) |> Enum.map(fn {next, current} -> next * remainingSpoons + current end) |> Enum.map(&max(&1, 0)) |> Enum.product() end def findTastiestRecipeWithExactCalories( [{nextIngredientCalories, nextIngredient} | remainingIngredients], remainingSpoons, remainingCalories, currentMixture ) do maxSpoons = min(remainingSpoons, div(remainingCalories, nextIngredientCalories)) Enum.map(0..maxSpoons, fn nextSpoons -> findTastiestRecipeWithExactCalories( remainingIngredients, remainingSpoons - nextSpoons, remainingCalories - nextIngredientCalories * nextSpoons, Enum.zip(nextIngredient, currentMixture) |> Enum.map(fn {next, current} -> next * nextSpoons + current end) ) end) |> Enum.filter(&(!is_nil(&1))) |> Enum.max(&>=/2, fn -> nil end) end end tastiest = RecipeFinder.findTastiestRecipe(ingredientsWithoutCalories, 100, [0, 0, 0, 0]) IO.puts("Tastiest: #{tastiest}") healthiest = RecipeFinder.findTastiestRecipeWithExactCalories(ingredientsWithCalories, 100, 500, [0, 0, 0, 0]) IO.puts("Healthiest: #{healthiest}")