advent_of_code/year2015/
day15.rs1use crate::input::Input;
2
3struct Ingredient {
4 capacity: i32,
5 durability: i32,
6 flavour: i32,
7 texture: i32,
8 calories: i32,
9}
10
11fn score_recipe(ingredients: &[Ingredient], teaspoons: &[i32], part2: bool) -> i32 {
12 if teaspoons.iter().sum::<i32>() != 100 {
13 return 0;
14 }
15
16 let mut capacity = 0;
17 let mut durability = 0;
18 let mut flavour = 0;
19 let mut texture = 0;
20 let mut calories = 0;
21
22 for i in 0..ingredients.len() {
23 capacity += ingredients[i].capacity * teaspoons[i];
24 durability += ingredients[i].durability * teaspoons[i];
25 flavour += ingredients[i].flavour * teaspoons[i];
26 texture += ingredients[i].texture * teaspoons[i];
27 calories += ingredients[i].calories * teaspoons[i];
28 }
29
30 if capacity <= 0 || durability <= 0 || flavour <= 0 || texture <= 0 {
31 return 0;
34 }
35
36 if part2 && calories != 500 {
37 return 0;
38 }
39
40 capacity * durability * flavour * texture
41}
42
43fn highest_score(
44 ingredients: &[Ingredient],
45 teaspoons: &mut [i32],
46 index: usize,
47 part2: bool,
48) -> i32 {
49 if index == teaspoons.len() {
50 return score_recipe(ingredients, teaspoons, part2);
51 }
52 let spoons_used_so_far = teaspoons.iter().take(index).sum::<i32>();
53 let mut max_score = 0;
54 for i in 0..=(100 - spoons_used_so_far) {
55 teaspoons[index] = i;
56 let score = highest_score(ingredients, teaspoons, index + 1, part2);
57 max_score = std::cmp::max(max_score, score);
58 }
59 max_score
60}
61
62pub fn solve(input: &Input) -> Result<i32, String> {
63 let error_mapper = |_| "Invalid number";
64
65 let mut ingredients = Vec::new();
66 for line in input.text.lines() {
67 let words = line.split(' ').collect::<Vec<_>>();
68 if words.len() != 11 || words.iter().any(|s| s.is_empty()) {
69 return Err("Invalid line not consisting of 11 words".to_string());
70 }
71
72 let capacity = words[2][0..words[2].len() - 1]
73 .parse::<i32>()
74 .map_err(error_mapper)?;
75 let durability = words[4][0..words[4].len() - 1]
76 .parse::<i32>()
77 .map_err(error_mapper)?;
78 let flavour = words[6][0..words[6].len() - 1]
79 .parse::<i32>()
80 .map_err(error_mapper)?;
81 let texture = words[8][0..words[8].len() - 1]
82 .parse::<i32>()
83 .map_err(error_mapper)?;
84 let calories = words[10].parse::<i32>().map_err(error_mapper)?;
85 let ingredient = Ingredient {
86 capacity,
87 durability,
88 flavour,
89 texture,
90 calories,
91 };
92 ingredients.push(ingredient);
93 }
94
95 let mut teaspoons = vec![0; ingredients.len()];
96
97 Ok(highest_score(
98 &ingredients,
99 &mut teaspoons,
100 0,
101 input.is_part_two(),
102 ))
103}
104
105#[test]
106pub fn tests() {
107 let example = "Butterscotch: capacity -1, durability -2, flavor 6, texture 3, calories 8
108Cinnamon: capacity 2, durability 3, flavor -2, texture -1, calories 3";
109 test_part_one!(example => 62_842_880);
110 test_part_two!(example => 57_600_000);
111
112 let real_input = include_str!("day15_input.txt");
113 test_part_one!(real_input => 18_965_440);
114 test_part_two!(real_input => 15_862_900);
115}