|
| 1 | +use aoc_rust_common::Solution; |
| 2 | +use std::fmt::Display; |
| 3 | +use std::collections::VecDeque; |
| 4 | + |
| 5 | +pub struct Day19; |
| 6 | + |
| 7 | +#[derive(Clone, Copy, Debug)] |
| 8 | +struct State { |
| 9 | + inventory: [u16; 4], |
| 10 | + bots: [u16; 4], |
| 11 | + elapsed: u16, |
| 12 | +} |
| 13 | + |
| 14 | +fn max_geodes(blueprint: &[[u16; 4]; 4], max_time: u16) -> u16 { |
| 15 | + let mut max_robots = [u16::MAX; 4]; |
| 16 | + for i in 0..3 { |
| 17 | + max_robots[i] = blueprint.iter().map(|cost| cost[i]).max().unwrap(); |
| 18 | + } |
| 19 | + let mut max_geodes = 0; |
| 20 | + |
| 21 | + let mut q = VecDeque::new(); |
| 22 | + q.push_back(State { |
| 23 | + inventory: [0, 0, 0, 0], |
| 24 | + bots: [1, 0, 0, 0], |
| 25 | + elapsed: 0, |
| 26 | + }); |
| 27 | + |
| 28 | + while let Some(State { inventory, bots, elapsed }) = q.pop_front() { |
| 29 | + for i in 0..blueprint.len() { |
| 30 | + if bots[i] == max_robots[i] { continue; } |
| 31 | + |
| 32 | + let costs = &blueprint[i]; |
| 33 | + let wait_time = (0..3) |
| 34 | + .map(|idx| { |
| 35 | + match costs[idx] { |
| 36 | + cost if cost <= inventory[idx] => 0, |
| 37 | + _ if bots[idx] == 0 => max_time + 1, |
| 38 | + cost => (cost - inventory[idx] + bots[idx] - 1) / bots[idx], |
| 39 | + } |
| 40 | + }) |
| 41 | + .max() |
| 42 | + .unwrap(); |
| 43 | + |
| 44 | + let new_elapsed = elapsed + wait_time + 1; |
| 45 | + if new_elapsed >= max_time { continue; } |
| 46 | + |
| 47 | + let mut new_inventory = [0; 4]; |
| 48 | + for idx in 0..bots.len() { |
| 49 | + new_inventory[idx] = inventory[idx] + bots[idx] * (wait_time + 1) - costs[idx]; |
| 50 | + } |
| 51 | + |
| 52 | + let mut new_bots = bots; |
| 53 | + new_bots[i] += 1; |
| 54 | + |
| 55 | + let remaining_time = max_time - new_elapsed; |
| 56 | + if ((remaining_time - 1) * remaining_time) / 2 + new_inventory[3] + remaining_time * new_bots[3] < max_geodes { |
| 57 | + continue; |
| 58 | + } |
| 59 | + |
| 60 | + q.push_back(State { inventory: new_inventory, bots: new_bots, elapsed: new_elapsed }) |
| 61 | + } |
| 62 | + max_geodes = max_geodes.max(inventory[3] + bots[3] * (max_time - elapsed)); |
| 63 | + } |
| 64 | + max_geodes |
| 65 | +} |
| 66 | + |
| 67 | +fn parse(input: &str) -> Vec<[[u16; 4]; 4]> { |
| 68 | + input.lines().map(|line| { |
| 69 | + let mut iter = line.split_ascii_whitespace(); |
| 70 | + let ore_bot_costs = [iter.nth(6).unwrap().parse().unwrap(), 0, 0, 0]; |
| 71 | + let clay_bot_costs = [iter.nth(5).unwrap().parse().unwrap(), 0, 0, 0]; |
| 72 | + let obsidian_bot_costs = [iter.nth(5).unwrap().parse().unwrap(), iter.nth(2).unwrap().parse().unwrap(), 0, 0]; |
| 73 | + let geode_bot_costs = [iter.nth(5).unwrap().parse().unwrap(), 0, iter.nth(2).unwrap().parse().unwrap(), 0]; |
| 74 | + [ore_bot_costs, clay_bot_costs, obsidian_bot_costs, geode_bot_costs] |
| 75 | + }).collect() |
| 76 | +} |
| 77 | + |
| 78 | +impl Solution for Day19 { |
| 79 | + fn year(&self) -> u32 { 2022 } |
| 80 | + fn day(&self) -> u32 { 19 } |
| 81 | + |
| 82 | + fn part1(&self, input: &str) -> Box<dyn Display> { |
| 83 | + let blueprints = parse(input); |
| 84 | + Box::new(blueprints.iter().enumerate().map(|(idx, blueprint)| (idx + 1) as u16 * max_geodes(blueprint, 24)).sum::<u16>() as i64) |
| 85 | + } |
| 86 | + |
| 87 | + fn part2(&self, input: &str) -> Box<dyn Display> { |
| 88 | + let blueprints = parse(input); |
| 89 | + Box::new(blueprints.iter().take(3).map(|blueprint| max_geodes(blueprint, 32) as u64).product::<u64>() as i64) |
| 90 | + } |
| 91 | +} |
0 commit comments