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 day17;
|
||||
pub mod day18;
|
||||
pub mod day19;
|
||||
|
||||
Reference in New Issue
Block a user