aoc/year2015/
day15.rs

1//! # Science for Hungry People
2//!
3//! Brute force solution trying every possible combination of ingredients. The loop conditions are
4//! calculated so that the ingredients always sum to 100. Solves part one and two simultaneously.
5//!
6//! As an optimization we check halfway through the loops to see if any ingredient will never be
7//! be greater than zero to skip large numbers of combinations.
8use crate::util::iter::*;
9use crate::util::parse::*;
10use std::array::from_fn;
11
12type Ingredient = [i32; 5];
13type Input = (i32, i32);
14
15pub fn parse(input: &str) -> Input {
16    let recipe: Vec<Ingredient> = input.iter_signed().chunk::<5>().collect();
17    let mut part_one = 0;
18    let mut part_two = 0;
19
20    for a in 0..101 {
21        let first: Ingredient = from_fn(|i| a * recipe[0][i]);
22
23        for b in 0..(101 - a) {
24            let second: Ingredient = from_fn(|i| first[i] + b * recipe[1][i]);
25
26            // Check if any ingredient can never be greater than zero.
27            let check: Ingredient =
28                from_fn(|i| second[i] + recipe[2][i].max(recipe[3][i]) * (100 - a - b));
29            if check.iter().any(|&n| n <= 0) {
30                continue;
31            }
32
33            for c in 0..(101 - a - b) {
34                let d = 100 - a - b - c;
35                let third: Ingredient = from_fn(|i| second[i] + c * recipe[2][i]);
36                let fourth: Ingredient = from_fn(|i| third[i] + d * recipe[3][i]);
37
38                let score = fourth.iter().take(4).map(|&n| n.max(0)).product();
39                let calories = fourth[4];
40
41                part_one = part_one.max(score);
42                if calories == 500 {
43                    part_two = part_two.max(score);
44                }
45            }
46        }
47    }
48
49    (part_one, part_two)
50}
51
52pub fn part1(input: &Input) -> i32 {
53    input.0
54}
55
56pub fn part2(input: &Input) -> i32 {
57    input.1
58}