Advent Of Code — Science for Hungry People — Puzzle 15

Part 1

The Problem

The full version of this problem can be found directly on the Advent of Code website, I will only describe the essence of the problem here:

Butterscotch: capacity -1, durability -2, flavor 6, texture 3, calories 8
Cinnamon: capacity 2, durability 3, flavor -2, texture -1, calories 3

Solution

The perfect recipe for cookies, I’m hungry just thinking about it ! 🍪

constexpr auto numberOfProperties = 4;
using PropertyValue = int;
using Ingredient = std::array<PropertyValue, numberOfProperties>;
Ingredient extractIngredientFrom (const std::string& line)
{
std::istringstream stream(line);
std::string ingredientName, capacity, comma, durability, flavor, texture;
PropertyValue capacityValue, durabilityValue, flavorValue, textureValue;
stream >> ingredientName >> capacity >> capacityValue >> comma >> durability >> durabilityValue >> comma >> flavor >> flavorValue >> comma >> texture >> textureValue;
return Ingredient{capacityValue, durabilityValue, flavorValue, textureValue};
}
std::vector<Proportions> createAllProportionPossibles(size_t numberOfIngredients, size_t teaspoonNumber)
{
if (numberOfIngredients == 1)
{
return {Proportions{teaspoonNumber}};
}
std::vector<Proportions> proportions;
for(auto i = size_t{1}; i < teaspoonNumber; ++i)
{
const auto proportionsOfLessIngredients =
createAllProportionPossibles (numberOfIngredients - 1, teaspoonNumber - i);
for(const auto& proportionsOfLessIngredient : proportionsOfLessIngredients)
{
Proportions p {i};
p.insert(p.end(), proportionsOfLessIngredient.begin(), proportionsOfLessIngredient.end());
proportions.push_back(p);
}
}
return proportions;
}
constexpr auto teaspoonsNumber = 100;
const auto allProportionsPossible = createAllProportionPossibles (ingredients.size(), teaspoonsNumber);
const auto winningProportion = std::max_element(
std::begin(allProportionsPossible),
std::end(allProportionsPossible),
[&ingredients](const auto& firstProportion, const auto& secondProportion)
{
return calculateTotalScore(ingredients, firstProportion) < calculateTotalScore(ingredients, secondProportion);
});
const auto bestTotalScore = calculateTotalScore(ingredients, *winningProportion);
int calculateTotalScore(std::vector<Ingredient> ingredients, Proportions proportion)
{
auto score{1};
for(auto propertyIndex = size_t{0}; propertyIndex < numberOfProperties; ++propertyIndex)
{
auto propertyScore{0};
for(auto indexIngredient = size_t{0}; indexIngredient < ingredients.size(); ++indexIngredient)
{
const auto& ingredient = ingredients[indexIngredient]; propertyScore += ingredient[propertyIndex] * proportion[indexIngredient];
}
if(propertyScore < 0)
{
score = 0;
break;
}
score *= propertyScore;
}
return score;
}

Part 2

Problem

Great, now our recipe is popular and we want to make the best recipe for a 500 calories cookie.

Solution

The logic to resolve this part is the same as the one used in the first part. First we collect the information, then we generate all the possible proportions and finally we found the winning proportion of ingredient. The generation of the proportions stays the same as in the first part. But the information collection, now we collect the number of calories which was useless before. So that it looks like that:

constexpr auto numberOfProperties = 5;
using PropertyValue = int;
using Ingredient = std::array<PropertyValue, numberOfProperties>;
Ingredient extractIngredientFrom (const std::string& line)
{
std::istringstream stream(line);
std::string ingredientName, capacity, comma, durability, flavor, texture, calories;
PropertyValue capacityValue, durabilityValue, flavorValue, textureValue, caloriesValue;
stream >> ingredientName >> capacity >> capacityValue >> comma >> durability >> durabilityValue >> comma >> flavor >> flavorValue >> comma >> texture >> textureValue >> comma >> calories >> caloriesValue;
return Ingredient{capacityValue, durabilityValue, flavorValue, textureValue, caloriesValue};
}
int calculateTotalScore(std::vector<Ingredient> ingredients, Proportions proportion)
{
auto score{1};
for(auto propertyIndex = size_t{0}; propertyIndex < numberOfProperties; ++propertyIndex)
{
auto propertyScore{0};
for(auto indexIngredient = size_t{0}; indexIngredient < ingredients.size(); ++indexIngredient)
{
const auto& ingredient = ingredients[indexIngredient];
propertyScore += ingredient[propertyIndex] * proportion[indexIngredient];
}
if(propertyIndex == numberOfProperties - 1)
{
return (propertyScore != 500) ? 0 : score;
}
if(propertyScore < 0)
{
return 0;
}
score *= propertyScore;
}
return score;
}

Conclusion

You can note that the solutions, written in this post, don’t include all the sources to make running programs, but only the interesting part of the sources to solve this problem. If you want to see the programs from end to end, you can go on my GitHub account, explore the full solution, add comments or ask questions if you want to, on the platform you read this article, it will also help me improve the quality of my articles.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store