1
0
Fork 0

day(19): add solution

Signed-off-by: Matej Focko <me@mfocko.xyz>
This commit is contained in:
Matej Focko 2023-07-06 18:59:42 +02:00
parent 94a5b006aa
commit ad42be0cb7
Signed by: mfocko
GPG key ID: 7C47D46246790496
2 changed files with 249 additions and 0 deletions

2
samples/day19.txt Normal file
View 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.

247
src/bin/day19.rs Normal file
View file

@ -0,0 +1,247 @@
use std::{
cmp::max,
collections::{BTreeSet, BinaryHeap},
ops::{Add, Mul, Sub},
};
use aoc_2022::*;
type Input = Vec<Blueprint>;
type Output = i32;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
struct Material {
ore: i32,
clay: i32,
obsidian: i32,
geode: i32,
}
impl Material {
fn new(ore: i32, clay: i32, obsidian: i32, geode: i32) -> Self {
Self {
ore,
clay,
obsidian,
geode,
}
}
fn nothing() -> Material {
Material::new(0, 0, 0, 0)
}
fn ore() -> Material {
Material::new(1, 0, 0, 0)
}
fn clay() -> Material {
Material::new(0, 1, 0, 0)
}
fn obsidian() -> Material {
Material::new(0, 0, 1, 0)
}
fn geode() -> Material {
Material::new(0, 0, 0, 1)
}
fn is_le(&self, rhs: &Material) -> bool {
self.ore <= rhs.ore
&& self.clay <= rhs.clay
&& self.obsidian <= rhs.obsidian
&& self.geode <= rhs.geode
}
}
impl Mul<Material> for i32 {
type Output = Material;
fn mul(self, rhs: Material) -> Self::Output {
Material::new(
self * rhs.ore,
self * rhs.clay,
self * rhs.obsidian,
self * rhs.geode,
)
}
}
impl Add<Material> for Material {
type Output = Material;
fn add(self, rhs: Material) -> Self::Output {
Material::new(
self.ore + rhs.ore,
self.clay + rhs.clay,
self.obsidian + rhs.obsidian,
self.geode + rhs.geode,
)
}
}
impl Sub<Material> for Material {
type Output = Material;
fn sub(self, rhs: Material) -> Self::Output {
Material::new(
self.ore - rhs.ore,
self.clay - rhs.clay,
self.obsidian - rhs.obsidian,
self.geode - rhs.geode,
)
}
}
#[derive(Debug)]
struct Robot {
id: i32,
cost: Material,
produces: Material,
}
#[derive(Debug)]
struct Blueprint {
id: i32,
robots: Vec<Robot>,
}
impl Blueprint {
fn max_cost(&self) -> Material {
Material::new(
self.robots.iter().map(|r| r.cost.ore).max().unwrap(),
self.robots.iter().map(|r| r.cost.clay).max().unwrap(),
self.robots.iter().map(|r| r.cost.obsidian).max().unwrap(),
i32::MAX,
)
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
struct State {
t: i32,
available: Material,
produces: Material,
not_built: i32,
}
impl State {
fn expected_geodes(&self) -> i32 {
self.available.geode + ((2 * self.produces.geode + self.t - 1) * self.t / 2)
}
fn new(t: i32, available: Material, produces: Material, not_built: i32) -> (i32, State) {
let s = State {
t,
available,
produces,
not_built,
};
let priority = s.expected_geodes();
(priority, s)
}
fn worth(&self, bp: &Blueprint, robot: &Robot) -> bool {
(self.not_built & robot.id) == 0 && (self.produces + robot.produces).is_le(&bp.max_cost())
}
}
fn max_geodes(bp: &Blueprint, time: i32) -> i32 {
let mut q: BinaryHeap<(i32, State)> = BinaryHeap::new();
let mut seen: BTreeSet<State> = BTreeSet::new();
q.push(State::new(time, Material::nothing(), Material::ore(), 0));
let mut found_maximum = 0;
while let Some((expected, state)) = q.pop() {
if expected < found_maximum {
break;
}
if !seen.insert(state) {
continue;
}
if state.t == 0 {
found_maximum = max(found_maximum, state.available.geode);
continue;
}
let can_be_built = bp
.robots
.iter()
.filter(|r| r.cost.is_le(&state.available) && state.worth(bp, r))
.collect_vec();
for robot in &can_be_built {
q.push(State::new(
state.t - 1,
state.available + state.produces - robot.cost,
state.produces + robot.produces,
0,
));
}
q.push(State::new(
state.t - 1,
state.available + state.produces,
state.produces,
can_be_built.iter().map(|r| r.id).sum(),
))
}
found_maximum
}
lazy_static! {
static ref BLUEPRINT_REGEX: Regex = Regex::new(r"(\d+)").unwrap();
}
struct Day19;
impl Solution<Input, Output> for Day19 {
fn parse_input<P: AsRef<Path>>(pathname: P) -> Input {
file_to_string(pathname)
.lines()
.map(|l| {
let numbers = BLUEPRINT_REGEX
.find_iter(l)
.map(|n| n.as_str().parse::<i32>().unwrap())
.collect_vec();
let [id, or_ore, c_ore, ob_ore, ob_clay, g_ore, g_obsidian] = numbers[..] else {unreachable!()};
Blueprint {
id,
robots: vec![
Robot {
id: 1,
produces: Material::ore(),
cost: or_ore * Material::ore(),
},
Robot {
id: 2,
produces: Material::clay(),
cost: c_ore * Material::ore(),
},
Robot {
id: 4,
produces: Material::obsidian(),
cost: ob_ore * Material::ore() + ob_clay * Material::clay(),
},
Robot {
id: 8,
produces: Material::geode(),
cost: g_ore * Material::ore() + g_obsidian * Material::obsidian(),
},
],
}
})
.collect_vec()
}
fn part_1(input: &Input) -> Output {
input.iter().map(|bp| bp.id * max_geodes(bp, 24)).sum()
}
fn part_2(input: &Input) -> Output {
input.iter().take(3).map(|bp| max_geodes(bp, 32)).product()
}
}
fn main() -> Result<()> {
// Day19::run("sample")
Day19::main()
}
test_sample!(day_19, Day19, 33, 3472);