Add 2022/19

This commit is contained in:
kageru 2022-12-19 12:29:55 +01:00
parent d303f55a4e
commit ea4c15668f
3 changed files with 168 additions and 0 deletions

@ -9,6 +9,7 @@ impl_ops = "0.1.1"
itertools = "0.10.5"
paste = "1.0"
rayon = "1.6.0"
scanf = "1.2.1"
[profile.bench]
lto = true

30
2022/inputs/day19 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 14 clay. Each geode robot costs 2 ore and 16 obsidian.
Blueprint 2: Each ore robot costs 2 ore. Each clay robot costs 2 ore. Each obsidian robot costs 2 ore and 15 clay. Each geode robot costs 2 ore and 7 obsidian.
Blueprint 3: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 14 clay. Each geode robot costs 2 ore and 7 obsidian.
Blueprint 4: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 17 clay. Each geode robot costs 3 ore and 16 obsidian.
Blueprint 5: Each ore robot costs 2 ore. Each clay robot costs 2 ore. Each obsidian robot costs 2 ore and 17 clay. Each geode robot costs 2 ore and 10 obsidian.
Blueprint 6: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 17 clay. Each geode robot costs 4 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 9 clay. Each geode robot costs 2 ore and 20 obsidian.
Blueprint 8: 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 9: 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 10: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 16 clay. Each geode robot costs 4 ore and 16 obsidian.
Blueprint 11: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 5 clay. Each geode robot costs 3 ore and 15 obsidian.
Blueprint 12: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 14 clay. Each geode robot costs 3 ore and 8 obsidian.
Blueprint 13: Each ore robot costs 2 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 20 clay. Each geode robot costs 2 ore and 17 obsidian.
Blueprint 14: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 19 clay. Each geode robot costs 3 ore and 10 obsidian.
Blueprint 15: 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 16: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 14 clay. Each geode robot costs 3 ore and 20 obsidian.
Blueprint 17: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 12 clay. Each geode robot costs 3 ore and 15 obsidian.
Blueprint 18: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 19 clay. Each geode robot costs 3 ore and 13 obsidian.
Blueprint 19: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 18 clay. Each geode robot costs 2 ore and 19 obsidian.
Blueprint 20: Each ore robot costs 2 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 16 clay. Each geode robot costs 2 ore and 9 obsidian.
Blueprint 21: 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 22: 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 23: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 6 clay. Each geode robot costs 4 ore and 11 obsidian.
Blueprint 24: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 5 clay. Each geode robot costs 2 ore and 10 obsidian.
Blueprint 25: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 14 clay. Each geode robot costs 3 ore and 14 obsidian.
Blueprint 26: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 15 clay. Each geode robot costs 4 ore and 16 obsidian.
Blueprint 27: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 4 ore and 18 clay. Each geode robot costs 4 ore and 11 obsidian.
Blueprint 28: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 14 clay. Each geode robot costs 4 ore and 17 obsidian.
Blueprint 29: 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 3 ore and 14 obsidian.
Blueprint 30: 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 4 ore and 14 obsidian.

137
2022/src/bin/day19.rs Normal file

@ -0,0 +1,137 @@
#![feature(test)]
extern crate test;
use std::ops::Sub;
use aoc2022::{boilerplate, common::*};
use scanf::sscanf;
const DAY: usize = 19;
type Parsed = Vec<Blueprint>;
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
struct Factory {
minute: u32,
ore: u32,
ore_robot: u32,
clay: u32,
clay_robot: u32,
obsidian: u32,
obsidian_robot: u32,
geode: u32,
geode_robot: u32,
}
impl Factory {
fn pass_minute(self) -> Self {
Factory {
ore: self.ore + self.ore_robot,
clay: self.clay + self.clay_robot,
obsidian: self.obsidian + self.obsidian_robot,
geode: self.geode + self.geode_robot,
minute: self.minute + 1,
..self
}
}
fn can_afford(&self, price: &Price) -> bool {
self.ore >= price.ore && self.clay >= price.clay && self.obsidian >= price.obsidian
}
}
impl Sub<Price> for Factory {
type Output = Self;
fn sub(self, price: Price) -> Self::Output {
Factory { ore: self.ore - price.ore, clay: self.clay - price.clay, obsidian: self.obsidian - price.obsidian, ..self }
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
struct Blueprint {
number: u32,
ore_robot: Price,
clay_robot: Price,
obsidian_robot: Price,
geode_robot: Price,
max_ore: u32,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
struct Price {
ore: u32,
clay: u32,
obsidian: u32,
}
fn parse_input(raw: &str) -> Parsed {
raw.lines().map(|line| {
let mut bp = Blueprint::default();
let mut number = 0;
let mut ore_robot_ore = 0;
let mut clay_robot_ore = 0;
let mut obsidian_robot_ore = 0;
let mut obsidian_robot_clay = 0;
let mut geode_robot_ore = 0;
let mut geode_robot_obsidian = 0;
// Unfortunately, this macro only takes `ident`, not `expr`, so I have to use temporary
// variables that are assigned to fields of the blueprint later
sscanf!(line, "Blueprint {}: Each ore robot costs {} ore. Each clay robot costs {} ore. Each obsidian robot costs {} ore and {} clay. Each geode robot costs {} ore and {} obsidian.", number,
ore_robot_ore, clay_robot_ore, obsidian_robot_ore, obsidian_robot_clay, geode_robot_ore, geode_robot_obsidian).unwrap();
bp.number = number;
bp.ore_robot.ore = ore_robot_ore;
bp.clay_robot.ore = clay_robot_ore;
bp.obsidian_robot.ore = obsidian_robot_ore;
bp.obsidian_robot.clay = obsidian_robot_clay;
bp.geode_robot.ore = geode_robot_ore;
bp.geode_robot.obsidian = geode_robot_obsidian;
bp.max_ore = clay_robot_ore.max(obsidian_robot_ore).max(geode_robot_ore);
bp
}).collect()
}
fn part1(parsed: &Parsed) -> u32 {
parsed.iter().map(|blueprint| max_geodes(Factory { ore_robot: 1, ..Default::default() }, blueprint, 24) * blueprint.number).sum()
}
fn part2(parsed: &Parsed) -> u32 {
parsed.iter().take(3).map(|blueprint| max_geodes(Factory { ore_robot: 1, ..Default::default() }, blueprint, 32)).product()
}
fn max_geodes(factory: Factory, bp: &Blueprint, limit: u32) -> u32 {
if factory.minute == limit {
return factory.geode;
}
if factory.can_afford(&bp.geode_robot) {
return max_geodes(Factory { geode_robot: factory.geode_robot + 1, ..factory.pass_minute() } - bp.geode_robot, bp, limit);
}
// This assumption holds for my input but not the test input.
// Not entirely fair, but good enough until I figure out a better way.
if factory.obsidian_robot < bp.geode_robot.obsidian && factory.can_afford(&bp.obsidian_robot) {
return max_geodes(Factory { obsidian_robot: factory.obsidian_robot + 1, ..factory.pass_minute() } - bp.obsidian_robot, bp, limit);
}
let mut outcomes = Vec::with_capacity(3);
if factory.ore_robot < bp.max_ore && factory.can_afford(&bp.ore_robot) {
outcomes.push(Factory { ore_robot: factory.ore_robot + 1, ..factory.pass_minute() } - bp.ore_robot);
}
if factory.clay_robot < bp.obsidian_robot.clay && factory.can_afford(&bp.clay_robot) {
outcomes.push(Factory { clay_robot: factory.clay_robot + 1, ..factory.pass_minute() } - bp.clay_robot);
}
// There seem to be steps where doing nothing is the right choice even if we could produce
// another robot according to the rules above. If I could eliminate this, the number of tested
// lines is reduce by a factor of ~1_000_000
outcomes.push(factory.pass_minute());
outcomes.into_iter().map(|f| max_geodes(f, bp, limit)).max().unwrap()
}
boilerplate! {
TEST_INPUT == "\
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.",
tests: {
part1: { TEST_INPUT => 33 },
// part2: { TEST_INPUT => 3472 },
},
bench1 == 1150,
bench2 == 37367,
bench_parse: Vec::len => 30,
}