day 19
This commit is contained in:
30
inputs/day19.txt
Normal file
30
inputs/day19.txt
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
Blueprint 1: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 7 clay. Each geode robot costs 2 ore and 19 obsidian.
|
||||||
|
Blueprint 2: Each ore robot costs 2 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 20 clay. Each geode robot costs 4 ore and 18 obsidian.
|
||||||
|
Blueprint 3: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 20 clay. Each geode robot costs 2 ore and 10 obsidian.
|
||||||
|
Blueprint 4: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 19 clay. Each geode robot costs 2 ore and 12 obsidian.
|
||||||
|
Blueprint 5: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 20 clay. Each geode robot costs 3 ore and 14 obsidian.
|
||||||
|
Blueprint 6: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 15 clay. Each geode robot costs 3 ore and 7 obsidian.
|
||||||
|
Blueprint 7: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 19 clay. Each geode robot costs 2 ore and 20 obsidian.
|
||||||
|
Blueprint 8: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 13 clay. Each geode robot costs 2 ore and 20 obsidian.
|
||||||
|
Blueprint 9: Each ore robot costs 2 ore. Each clay robot costs 2 ore. Each obsidian robot costs 2 ore and 8 clay. Each geode robot costs 2 ore and 14 obsidian.
|
||||||
|
Blueprint 10: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 11 clay. Each geode robot costs 3 ore and 14 obsidian.
|
||||||
|
Blueprint 11: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 5 clay. Each geode robot costs 4 ore and 8 obsidian.
|
||||||
|
Blueprint 12: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 16 clay. Each geode robot costs 2 ore and 18 obsidian.
|
||||||
|
Blueprint 13: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 11 clay. Each geode robot costs 2 ore and 10 obsidian.
|
||||||
|
Blueprint 14: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 14 clay. Each geode robot costs 3 ore and 17 obsidian.
|
||||||
|
Blueprint 15: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 19 clay. Each geode robot costs 3 ore and 17 obsidian.
|
||||||
|
Blueprint 16: Each ore robot costs 2 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 20 clay. Each geode robot costs 2 ore and 17 obsidian.
|
||||||
|
Blueprint 17: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 14 clay. Each geode robot costs 4 ore and 8 obsidian.
|
||||||
|
Blueprint 18: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 9 clay. Each geode robot costs 3 ore and 9 obsidian.
|
||||||
|
Blueprint 19: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 10 clay. Each geode robot costs 3 ore and 14 obsidian.
|
||||||
|
Blueprint 20: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 13 clay. Each geode robot costs 3 ore and 12 obsidian.
|
||||||
|
Blueprint 21: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 4 ore and 15 clay. Each geode robot costs 4 ore and 9 obsidian.
|
||||||
|
Blueprint 22: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 20 clay. Each geode robot costs 2 ore and 12 obsidian.
|
||||||
|
Blueprint 23: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 4 ore and 19 clay. Each geode robot costs 4 ore and 12 obsidian.
|
||||||
|
Blueprint 24: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 15 clay. Each geode robot costs 3 ore and 8 obsidian.
|
||||||
|
Blueprint 25: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 11 clay. Each geode robot costs 2 ore and 16 obsidian.
|
||||||
|
Blueprint 26: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 17 clay. Each geode robot costs 3 ore and 7 obsidian.
|
||||||
|
Blueprint 27: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 7 clay. Each geode robot costs 3 ore and 20 obsidian.
|
||||||
|
Blueprint 28: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 10 clay. Each geode robot costs 2 ore and 10 obsidian.
|
||||||
|
Blueprint 29: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 17 clay. Each geode robot costs 2 ore and 13 obsidian.
|
||||||
|
Blueprint 30: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 4 ore and 20 clay. Each geode robot costs 4 ore and 8 obsidian.
|
||||||
8
src/bin/day19_1.rs
Normal file
8
src/bin/day19_1.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
use std::fs;
|
||||||
|
|
||||||
|
use aoc2022::day19::process_part_1;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let file = fs::read_to_string("./inputs/day19.txt").unwrap();
|
||||||
|
println!("{}", process_part_1(&file));
|
||||||
|
}
|
||||||
8
src/bin/day19_2.rs
Normal file
8
src/bin/day19_2.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
use std::fs;
|
||||||
|
|
||||||
|
use aoc2022::day19::process_part_2;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let file = fs::read_to_string("./inputs/day19.txt").unwrap();
|
||||||
|
println!("{}", process_part_2(&file));
|
||||||
|
}
|
||||||
214
src/day19.rs
Normal file
214
src/day19.rs
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use nom::bytes::complete::tag;
|
||||||
|
use nom::character::complete;
|
||||||
|
use nom::character::complete::newline;
|
||||||
|
use nom::combinator::map;
|
||||||
|
use nom::multi::separated_list1;
|
||||||
|
use nom::sequence::{delimited, preceded, separated_pair, tuple};
|
||||||
|
use nom::IResult;
|
||||||
|
use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
|
||||||
|
|
||||||
|
pub fn process_part_1(input: &str) -> usize {
|
||||||
|
let blueprints = parse_input(input).unwrap().1;
|
||||||
|
blueprints
|
||||||
|
.par_iter()
|
||||||
|
.map(|bp| bp.id * find_best_geodes(bp, 24))
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_part_2(input: &str) -> usize {
|
||||||
|
let blueprints = parse_input(input).unwrap().1;
|
||||||
|
blueprints
|
||||||
|
.par_iter()
|
||||||
|
.take(3)
|
||||||
|
.map(|bp| find_best_geodes(bp, 32))
|
||||||
|
.product()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Blueprint {
|
||||||
|
id: usize,
|
||||||
|
ore_bot_cost: usize,
|
||||||
|
clay_bot_cost: usize,
|
||||||
|
obsidian_bot_cost: (usize, usize),
|
||||||
|
geode_bot_cost: (usize, usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Blueprint {
|
||||||
|
fn max_ore_cost(&self) -> usize {
|
||||||
|
[
|
||||||
|
self.ore_bot_cost,
|
||||||
|
self.clay_bot_cost,
|
||||||
|
self.obsidian_bot_cost.0,
|
||||||
|
self.geode_bot_cost.0,
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.max()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||||
|
struct State {
|
||||||
|
time: usize,
|
||||||
|
|
||||||
|
ore: usize,
|
||||||
|
clay: usize,
|
||||||
|
obsidian: usize,
|
||||||
|
geode: usize,
|
||||||
|
|
||||||
|
ore_bots: usize,
|
||||||
|
clay_bots: usize,
|
||||||
|
obsidian_bots: usize,
|
||||||
|
geode_bots: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
fn upper_limit_geodes(&self) -> usize {
|
||||||
|
// assumes we can create a bot per every minute
|
||||||
|
let build_geode_cap = (self.time * (self.time + 1)) / 2;
|
||||||
|
|
||||||
|
self.geode + build_geode_cap + self.geode_bots * self.time
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tick(&self) -> Self {
|
||||||
|
State {
|
||||||
|
ore: self.ore + self.ore_bots,
|
||||||
|
clay: self.clay + self.clay_bots,
|
||||||
|
obsidian: self.obsidian + self.obsidian_bots,
|
||||||
|
geode: self.geode + self.geode_bots,
|
||||||
|
time: self.time - 1,
|
||||||
|
..*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_best_geodes(bp: &Blueprint, time: usize) -> usize {
|
||||||
|
let mut visited = HashSet::new();
|
||||||
|
let mut stack = vec![State {
|
||||||
|
ore_bots: 1,
|
||||||
|
time,
|
||||||
|
..Default::default()
|
||||||
|
}];
|
||||||
|
let max_ore_cost = bp.max_ore_cost();
|
||||||
|
let mut max_geodes = 0;
|
||||||
|
|
||||||
|
while let Some(state) = stack.pop() {
|
||||||
|
if state.time == 0 {
|
||||||
|
max_geodes = max_geodes.max(state.geode);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prune states where its immediately obvious we won't beat the maximum
|
||||||
|
if state.upper_limit_geodes() < max_geodes {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Best case, we can build a geode bot:
|
||||||
|
if state.ore >= bp.geode_bot_cost.0 && state.obsidian >= bp.geode_bot_cost.1 {
|
||||||
|
let next = State {
|
||||||
|
geode_bots: state.geode_bots + 1,
|
||||||
|
ore: state.ore - bp.geode_bot_cost.0 + state.ore_bots,
|
||||||
|
obsidian: state.obsidian - bp.geode_bot_cost.1 + state.obsidian_bots,
|
||||||
|
..state.tick()
|
||||||
|
};
|
||||||
|
if visited.insert(next.clone()) {
|
||||||
|
stack.push(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obsidian bots are nice too
|
||||||
|
if state.ore >= bp.obsidian_bot_cost.0 && state.clay >= bp.obsidian_bot_cost.1 {
|
||||||
|
let next = State {
|
||||||
|
obsidian_bots: state.obsidian_bots + 1,
|
||||||
|
ore: state.ore - bp.obsidian_bot_cost.0 + state.ore_bots,
|
||||||
|
clay: state.clay - bp.obsidian_bot_cost.1 + state.clay_bots,
|
||||||
|
..state.tick()
|
||||||
|
};
|
||||||
|
if visited.insert(next.clone()) {
|
||||||
|
stack.push(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clay?
|
||||||
|
if state.ore >= bp.clay_bot_cost {
|
||||||
|
let next = State {
|
||||||
|
clay_bots: state.clay_bots + 1,
|
||||||
|
ore: state.ore - bp.clay_bot_cost + state.ore_bots,
|
||||||
|
..state.tick()
|
||||||
|
};
|
||||||
|
if visited.insert(next.clone()) {
|
||||||
|
stack.push(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ore? But only if we don't make enough to make any new bot per round we want
|
||||||
|
if state.ore >= bp.ore_bot_cost && state.ore_bots < max_ore_cost {
|
||||||
|
let next = State {
|
||||||
|
ore_bots: state.ore_bots + 1,
|
||||||
|
ore: state.ore - bp.ore_bot_cost + state.ore_bots,
|
||||||
|
..state.tick()
|
||||||
|
};
|
||||||
|
if visited.insert(next.clone()) {
|
||||||
|
stack.push(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final option, do nothing
|
||||||
|
let next = state.tick();
|
||||||
|
if visited.insert(next.clone()) {
|
||||||
|
stack.push(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
max_geodes
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_input(input: &str) -> IResult<&str, Vec<Blueprint>> {
|
||||||
|
separated_list1(newline, parse_blueprint)(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_blueprint(input: &str) -> IResult<&str, Blueprint> {
|
||||||
|
map(
|
||||||
|
tuple((
|
||||||
|
preceded(tag("Blueprint "), complete::u64),
|
||||||
|
preceded(tag(": Each ore robot costs "), complete::u64),
|
||||||
|
preceded(tag(" ore. Each clay robot costs "), complete::u64),
|
||||||
|
preceded(
|
||||||
|
tag(" ore. Each obsidian robot costs "),
|
||||||
|
separated_pair(complete::u64, tag(" ore and "), complete::u64),
|
||||||
|
),
|
||||||
|
delimited(
|
||||||
|
tag(" clay. Each geode robot costs "),
|
||||||
|
separated_pair(complete::u64, tag(" ore and "), complete::u64),
|
||||||
|
tag(" obsidian."),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
|(id, ore_bot, clay_bot, (ob_ore, ob_clay), (gb_ore, gb_obs))| Blueprint {
|
||||||
|
id: id as usize,
|
||||||
|
ore_bot_cost: ore_bot as usize,
|
||||||
|
clay_bot_cost: clay_bot as usize,
|
||||||
|
obsidian_bot_cost: (ob_ore as usize, ob_clay as usize),
|
||||||
|
geode_bot_cost: (gb_ore as usize, gb_obs as usize),
|
||||||
|
},
|
||||||
|
)(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const INPUT: &str = "Blueprint 1: Each ore robot costs 4 ore. Each clay robot costs 2 ore. Each obsidian robot costs 3 ore and 14 clay. Each geode robot costs 2 ore and 7 obsidian.
|
||||||
|
Blueprint 2: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 8 clay. Each geode robot costs 3 ore and 12 obsidian.";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn day1() {
|
||||||
|
assert_eq!(process_part_1(INPUT), 33);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn day2() {
|
||||||
|
assert_eq!(process_part_2(INPUT), 3472);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,3 +19,4 @@ pub mod day15;
|
|||||||
pub mod day16;
|
pub mod day16;
|
||||||
pub mod day17;
|
pub mod day17;
|
||||||
pub mod day18;
|
pub mod day18;
|
||||||
|
pub mod day19;
|
||||||
|
|||||||
Reference in New Issue
Block a user