2022 - Day 19
This commit is contained in:
parent
8915fb135e
commit
9e3de56bdf
30
2022/input/19/input
Normal file
30
2022/input/19/input
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 20 clay. Each geode robot costs 2 ore and 12 obsidian.
|
||||
Blueprint 2: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 15 clay. Each geode robot costs 3 ore and 9 obsidian.
|
||||
Blueprint 3: 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 4: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 20 clay. Each geode robot costs 2 ore and 20 obsidian.
|
||||
Blueprint 5: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 20 clay. Each geode robot costs 4 ore and 16 obsidian.
|
||||
Blueprint 6: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 11 clay. Each geode robot costs 2 ore and 8 obsidian.
|
||||
Blueprint 7: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 8 clay. Each geode robot costs 2 ore and 18 obsidian.
|
||||
Blueprint 8: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 20 clay. Each geode robot costs 2 ore and 8 obsidian.
|
||||
Blueprint 9: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 9 clay. Each geode robot costs 4 ore and 16 obsidian.
|
||||
Blueprint 10: 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 11: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 4 ore and 16 clay. Each geode robot costs 2 ore and 15 obsidian.
|
||||
Blueprint 12: 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 3 ore and 8 obsidian.
|
||||
Blueprint 13: 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 4 ore and 20 obsidian.
|
||||
Blueprint 14: Each ore robot costs 2 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 17 clay. Each geode robot costs 3 ore and 11 obsidian.
|
||||
Blueprint 15: 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 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 16 obsidian.
|
||||
Blueprint 17: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 10 clay. Each geode robot costs 2 ore and 14 obsidian.
|
||||
Blueprint 18: 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 3 ore and 12 obsidian.
|
||||
Blueprint 19: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 20 clay. Each geode robot costs 3 ore and 18 obsidian.
|
||||
Blueprint 20: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 18 clay. Each geode robot costs 3 ore and 13 obsidian.
|
||||
Blueprint 21: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 7 clay. Each geode robot costs 4 ore and 18 obsidian.
|
||||
Blueprint 22: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 8 clay. Each geode robot costs 3 ore and 19 obsidian.
|
||||
Blueprint 23: 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 4 ore and 11 obsidian.
|
||||
Blueprint 24: 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 2 ore and 9 obsidian.
|
||||
Blueprint 25: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 6 clay. Each geode robot costs 2 ore and 16 obsidian.
|
||||
Blueprint 26: 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 27: 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 4 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 20 clay. Each geode robot costs 2 ore and 19 obsidian.
|
||||
Blueprint 29: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 14 clay. Each geode robot costs 3 ore and 16 obsidian.
|
||||
Blueprint 30: 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 16 obsidian.
|
2
2022/input/19/test-1
Normal file
2
2022/input/19/test-1
Normal file
|
@ -0,0 +1,2 @@
|
|||
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.
|
312
2022/src/bin/day19.rs
Normal file
312
2022/src/bin/day19.rs
Normal file
|
@ -0,0 +1,312 @@
|
|||
#![feature(test)]
|
||||
use std::{ops::{AddAssign, SubAssign}, str::FromStr};
|
||||
|
||||
use anyhow::Result;
|
||||
use aoc::Solver;
|
||||
|
||||
// -- Runners --
|
||||
fn main() -> Result<()> {
|
||||
Day::solve()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_test1() -> Result<()> {
|
||||
Day::test(Day::part1, "test-1", 33)
|
||||
}
|
||||
#[test]
|
||||
fn part1_solution() -> Result<()> {
|
||||
Day::test(Day::part1, "input", 600)
|
||||
}
|
||||
#[test]
|
||||
fn part2_test1() -> Result<()> {
|
||||
Day::test(Day::part2, "test-1", 56*62)
|
||||
}
|
||||
#[test]
|
||||
fn part2_solution() -> Result<()> {
|
||||
Day::test(Day::part2, "input", 6000)
|
||||
}
|
||||
|
||||
// Benchmarks
|
||||
extern crate test;
|
||||
#[bench]
|
||||
#[ignore]
|
||||
fn part1_bench(b: &mut test::Bencher) {
|
||||
Day::benchmark(Day::part1, b)
|
||||
}
|
||||
#[bench]
|
||||
#[ignore]
|
||||
fn part2_bench(b: &mut test::Bencher) {
|
||||
Day::benchmark(Day::part2, b)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
struct Resources {
|
||||
ore: isize,
|
||||
clay: isize,
|
||||
obsidian: isize,
|
||||
}
|
||||
|
||||
impl Resources {
|
||||
fn new(ore: isize, clay: isize, obsidian: isize) -> Self {
|
||||
Self { ore, clay, obsidian }
|
||||
}
|
||||
|
||||
fn enough_for(&self, other: &Resources) -> bool {
|
||||
self.ore >= other.ore &&
|
||||
self.clay >= other.clay &&
|
||||
self.obsidian >= other.obsidian
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Resources {
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
self.ore += rhs.ore;
|
||||
self.clay += rhs.clay;
|
||||
self.obsidian += rhs.obsidian;
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign for Resources {
|
||||
fn sub_assign(&mut self, rhs: Self) {
|
||||
self.ore -= rhs.ore;
|
||||
self.clay -= rhs.clay;
|
||||
self.obsidian -= rhs.obsidian;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Blueprint {
|
||||
ore_robot_cost: Resources,
|
||||
clay_robot_cost: Resources,
|
||||
obsidian_robot_cost: Resources,
|
||||
geode_robot_cost: Resources,
|
||||
max: Resources,
|
||||
}
|
||||
|
||||
impl Blueprint {
|
||||
fn new(ore_robot_cost: Resources, clay_robot_cost: Resources, obsidian_robot_cost: Resources, geode_robot_cost: Resources) -> Self {
|
||||
// Calculate how much of each resource we need at most every minute in order to construct
|
||||
// every robot
|
||||
let mut max = Resources::new(0, 0, 0);
|
||||
|
||||
max.ore = max.ore.max(ore_robot_cost.ore).max(clay_robot_cost.ore).max(obsidian_robot_cost.ore).max(geode_robot_cost.ore);
|
||||
max.clay = max.clay.max(ore_robot_cost.clay).max(clay_robot_cost.clay).max(obsidian_robot_cost.clay).max(geode_robot_cost.clay);
|
||||
max.obsidian = max.obsidian.max(ore_robot_cost.obsidian).max(clay_robot_cost.obsidian).max(obsidian_robot_cost.obsidian).max(geode_robot_cost.obsidian);
|
||||
|
||||
Self { ore_robot_cost, clay_robot_cost, obsidian_robot_cost, geode_robot_cost, max }
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Blueprint {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut split = s.split(" ");
|
||||
|
||||
let ore = Resources::new(
|
||||
split.nth(6).unwrap().parse()?,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
|
||||
let clay = Resources::new(
|
||||
split.nth(5).unwrap().parse()?,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
|
||||
let obsidian = Resources::new(
|
||||
split.nth(5).unwrap().parse()?,
|
||||
split.nth(2).unwrap().parse()?,
|
||||
0,
|
||||
);
|
||||
|
||||
let geode = Resources::new(
|
||||
split.nth(5).unwrap().parse()?,
|
||||
0,
|
||||
split.nth(2).unwrap().parse()?,
|
||||
);
|
||||
|
||||
Ok(Blueprint::new(ore, clay, obsidian, geode))
|
||||
}
|
||||
}
|
||||
|
||||
impl Blueprint {
|
||||
fn visit(&self, state: State, mut best: isize) -> isize {
|
||||
// If we have run out of time return the amount of geodes we have broken in total
|
||||
if state.time_remaining <= 0 {
|
||||
return state.geodes;
|
||||
}
|
||||
|
||||
// Using n(n+1)/2 calculate the maximum possible geodes that we can crack
|
||||
// If we can not improve the best so far, we end here
|
||||
// !!! This make a massive difference, from who knows how long to less then a second
|
||||
if state.geodes + (state.time_remaining-1) * state.time_remaining / 2 < best {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Given the remaining time, calculate how much of the resource we still need in the worst
|
||||
// case scenerio. If current stockpile + future production if lower we will attempt to
|
||||
// construct a robot, otherwise we do not need them anymore
|
||||
// === Ore ===
|
||||
if state.robots.ore * state.time_remaining + state.resources.ore < self.max.ore * state.time_remaining {
|
||||
// Check if we can construct one right now
|
||||
if state.resources.enough_for(&self.ore_robot_cost) {
|
||||
// Create a new state
|
||||
let mut next = state.next();
|
||||
// Substract the resources
|
||||
next.resources -= self.ore_robot_cost;
|
||||
// Add the robot
|
||||
next.robots.ore += 1;
|
||||
|
||||
best = best.max(self.visit(next, best));
|
||||
} else {
|
||||
// If we can not construct one right now, skip to the next point in time that we
|
||||
// can build one
|
||||
let mut next = state.next();
|
||||
while !next.resources.enough_for(&self.ore_robot_cost) && next.time_remaining >= 0 {
|
||||
next = next.next();
|
||||
}
|
||||
|
||||
best = best.max(self.visit(next, best));
|
||||
}
|
||||
}
|
||||
|
||||
// === Clay ===
|
||||
if state.robots.clay * state.time_remaining + state.resources.clay < self.max.clay * state.time_remaining {
|
||||
if state.resources.enough_for(&self.clay_robot_cost) {
|
||||
// Create a new state
|
||||
let mut next = state.next();
|
||||
// Substract the resources
|
||||
next.resources -= self.clay_robot_cost;
|
||||
// Add the robot
|
||||
next.robots.clay += 1;
|
||||
|
||||
best = best.max(self.visit(next, best));
|
||||
} else {
|
||||
// If we can not construct one right now, skip to the next point in time that we
|
||||
// can build one
|
||||
let mut next = state.next();
|
||||
while !next.resources.enough_for(&self.clay_robot_cost) && next.time_remaining >= 0 {
|
||||
next = next.next();
|
||||
}
|
||||
|
||||
best = best.max(self.visit(next, best));
|
||||
}
|
||||
}
|
||||
|
||||
// === Obsidian ===
|
||||
if state.robots.obsidian * state.time_remaining + state.resources.obsidian < self.max.obsidian * state.time_remaining {
|
||||
if state.resources.enough_for(&self.obsidian_robot_cost) {
|
||||
// Create a new state
|
||||
let mut next = state.next();
|
||||
// Substract the resources
|
||||
next.resources -= self.obsidian_robot_cost;
|
||||
// Add the robot
|
||||
next.robots.obsidian += 1;
|
||||
|
||||
best = best.max(self.visit(next, best));
|
||||
} else {
|
||||
// If we can not construct one right now, skip to the next point in time that we
|
||||
// can build one
|
||||
let mut next = state.next();
|
||||
while !next.resources.enough_for(&self.obsidian_robot_cost) && next.time_remaining >= 0 {
|
||||
next = next.next();
|
||||
}
|
||||
|
||||
best = best.max(self.visit(next, best));
|
||||
}
|
||||
}
|
||||
|
||||
// === Geode ==
|
||||
// There is no upper limit to producing robots, so only check if we can make one
|
||||
if state.resources.enough_for(&self.geode_robot_cost) {
|
||||
// Create a new state
|
||||
let mut next = state.next();
|
||||
// Substract the resources
|
||||
next.resources -= self.geode_robot_cost;
|
||||
// Since we never use the geodes we can add all that this robot will mine at once
|
||||
next.geodes += state.time_remaining - 1;
|
||||
|
||||
best = best.max(self.visit(next, best));
|
||||
} else {
|
||||
// If we can not construct one right now, skip to the next point in time that we
|
||||
// can build one
|
||||
let mut next = state.next();
|
||||
while !next.resources.enough_for(&self.geode_robot_cost) && next.time_remaining >= 0 {
|
||||
next = next.next();
|
||||
}
|
||||
|
||||
best = best.max(self.visit(next, best));
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||
struct State {
|
||||
resources: Resources,
|
||||
robots: Resources,
|
||||
geodes: isize,
|
||||
time_remaining: isize,
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn new(time_remaining: isize) -> Self {
|
||||
Self {
|
||||
resources: Resources::new(0, 0, 0),
|
||||
robots: Resources::new(1, 0, 0),
|
||||
geodes: 0,
|
||||
time_remaining,
|
||||
}
|
||||
}
|
||||
|
||||
fn next(&self) -> Self {
|
||||
let mut next = self.clone();
|
||||
|
||||
// Update the time
|
||||
next.time_remaining -= 1;
|
||||
// Collect resources
|
||||
next.resources += self.robots;
|
||||
|
||||
return next;
|
||||
}
|
||||
}
|
||||
|
||||
// -- Solution --
|
||||
pub struct Day;
|
||||
impl aoc::Solver for Day {
|
||||
type Output1 = isize;
|
||||
type Output2 = isize;
|
||||
|
||||
fn day() -> u8 {
|
||||
19
|
||||
}
|
||||
|
||||
fn part1(input: &str) -> Self::Output1 {
|
||||
let blueprints = input.trim().lines().flat_map(Blueprint::from_str).collect::<Vec<_>>();
|
||||
|
||||
let state = State::new(24);
|
||||
blueprints.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, blueprint)| {
|
||||
blueprint.visit(state.clone(), 0) * (idx as isize + 1)
|
||||
}).sum()
|
||||
}
|
||||
|
||||
fn part2(input: &str) -> Self::Output2 {
|
||||
let blueprints = input.trim().lines().flat_map(Blueprint::from_str).collect::<Vec<_>>();
|
||||
let state = State::new(32);
|
||||
blueprints.iter()
|
||||
.take(3)
|
||||
.fold(1, |acc, blueprint| {
|
||||
acc * blueprint.visit(state.clone(), 0)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user