Start solving day19 and break it after that

This commit is contained in:
Malte Tammena 2022-12-26 20:30:30 +01:00
parent 4443c683a8
commit 066ec8e569
5 changed files with 418 additions and 2 deletions

101
Cargo.lock generated
View file

@ -183,6 +183,49 @@ version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2875caafcad10de47ec09ead140f27a276a25b0b2f553cec9b2152194b96bf5d"
[[package]]
name = "crossbeam-channel"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
dependencies = [
"cfg-if",
]
[[package]]
name = "darling"
version = "0.13.4"
@ -353,6 +396,11 @@ dependencies = [
[[package]]
name = "day19"
version = "0.1.0"
dependencies = [
"lazy_static",
"rayon",
"regex",
]
[[package]]
name = "day20"
@ -384,6 +432,12 @@ dependencies = [
name = "day25"
version = "0.1.0"
[[package]]
name = "either"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]]
name = "errno"
version = "0.2.8"
@ -632,6 +686,25 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memoffset"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "numtoa"
version = "0.1.0"
@ -746,6 +819,28 @@ dependencies = [
"getrandom",
]
[[package]]
name = "rayon"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
"num_cpus",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
@ -804,6 +899,12 @@ dependencies = [
"bytemuck",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.151"

View file

@ -8,3 +8,6 @@ build = true
app = true
[dependencies]
lazy_static = "1.4.0"
rayon = "1.6.1"
regex = "1.7.0"

30
day19/input Normal file
View file

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

2
day19/input.test 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.

View file

@ -1,3 +1,283 @@
fn main() {
println!("Hello, world!");
#![feature(const_trait_impl)]
use std::{
collections::{hash_map::Entry, HashMap},
fs::File,
io::{BufRead, BufReader},
ops::Add,
};
use lazy_static::lazy_static;
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
use regex::Regex;
type Ore = usize;
type Clay = usize;
type Obsidian = usize;
type Geode = usize;
lazy_static! {
static ref BLUEPRINT_RE: Regex = Regex::new(r"Blueprint (\d+): Each ore robot costs (\d+) ore. Each clay robot costs (\d+) ore. Each obsidian robot costs (\d+) ore and (\d+) clay. Each geode robot costs (\d+) ore and (\d+) obsidian.").unwrap();
}
#[derive(Debug, Default, PartialEq, Eq)]
enum SolvePuzzle {
First,
#[default]
Second,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
struct ResourceState {
ore: Ore,
clay: Clay,
obsidian: Obsidian,
geodes: Geode,
}
impl ResourceState {
fn maximize(&mut self, other: Self) {
self.ore = self.ore.max(other.ore);
self.clay = self.clay.max(other.clay);
self.obsidian = self.obsidian.max(other.obsidian);
self.geodes = self.geodes.max(other.geodes);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct RobotState {
ore_robots: usize,
clay_robots: usize,
obisidian_robots: usize,
geode_robots: usize,
}
impl RobotState {
fn successors(
self,
possible_resources: ResourceState,
blueprint: &Blueprint,
) -> Vec<(Self, ResourceState)> {
let mut ret = vec![];
let collected = possible_resources + self.collect_ores_for_one_minute();
if possible_resources.ore >= blueprint.ore_robot_cost {
ret.push((
Self {
ore_robots: self.ore_robots + 1,
..self
},
ResourceState {
ore: collected.ore - blueprint.ore_robot_cost,
..collected
},
));
}
if possible_resources.ore >= blueprint.clay_robot_cost {
ret.push((
Self {
clay_robots: self.clay_robots + 1,
..self
},
ResourceState {
ore: collected.ore - blueprint.clay_robot_cost,
..collected
},
));
}
if possible_resources.ore >= blueprint.obsidian_robot_cost.0
&& possible_resources.clay >= blueprint.obsidian_robot_cost.1
{
ret.push((
Self {
obisidian_robots: self.obisidian_robots + 1,
..self
},
ResourceState {
ore: collected.ore - blueprint.obsidian_robot_cost.0,
clay: collected.clay - blueprint.obsidian_robot_cost.1,
..collected
},
));
}
if possible_resources.ore >= blueprint.geode_robot_cost.0
&& possible_resources.obsidian >= blueprint.geode_robot_cost.1
{
ret.push((
Self {
geode_robots: self.geode_robots + 1,
..self
},
ResourceState {
ore: collected.ore - blueprint.geode_robot_cost.0,
obsidian: collected.obsidian - blueprint.geode_robot_cost.1,
..collected
},
));
}
// let we_can_afford_every_robot_or_will_never = self.ore >= blueprint.ore_robot_cost
// && self.ore >= blueprint.clay_robot_cost
// && self.ore >= blueprint.obsidian_robot_cost.0
// && (self.clay >= blueprint.obsidian_robot_cost.1 || self.clay_robots == 0)
// && self.ore >= blueprint.geode_robot_cost.0
// && (self.obsidian >= blueprint.geode_robot_cost.1 || self.obisidian_robots == 0);
// // Do nothing, just collect ores
// if !we_can_afford_every_robot_or_will_never {
ret.push((self, collected));
// }
ret
}
fn collect_ores_for_one_minute(&self) -> ResourceState {
ResourceState {
ore: self.ore_robots,
clay: self.clay_robots,
obsidian: self.obisidian_robots,
geodes: self.geode_robots,
}
}
}
#[derive(Debug)]
struct Blueprint {
id: usize,
ore_robot_cost: Ore,
clay_robot_cost: Ore,
obsidian_robot_cost: (Ore, Clay),
geode_robot_cost: (Ore, Obsidian),
}
impl Blueprint {
fn parse_line<S: AsRef<str>>(line: S) -> Self {
let matches = BLUEPRINT_RE.captures(line.as_ref()).unwrap();
let match_nr = |nr| matches.get(nr).unwrap().as_str().parse().unwrap();
Self {
id: match_nr(1),
ore_robot_cost: match_nr(2),
clay_robot_cost: match_nr(3),
obsidian_robot_cost: (match_nr(4), match_nr(5)),
geode_robot_cost: (match_nr(6), match_nr(7)),
}
}
fn calculate_max_geode_openable(&self) -> usize {
const MAX_TIME: usize = 24;
let mut states: HashMap<RobotState, ResourceState> = HashMap::new();
states.insert(RobotState::default(), ResourceState::default());
for minute in 1..=MAX_TIME {
states = states
.into_iter()
.flat_map(|(state, res)| state.successors(res, self))
.fold(HashMap::new(), |mut map, (state, res)| {
match map.entry(state) {
Entry::Occupied(mut occ) => occ.get_mut().maximize(res),
Entry::Vacant(vac) => {
vac.insert(res);
}
};
map
});
// if minute == 24 {
eprintln!("{minute} {}", states.len());
if minute < 6 {
states.iter().for_each(|x| eprintln!("{x:#?}"));
}
// }
}
states
.into_values()
.map(|s| s.geodes)
.max()
.unwrap_or_default()
}
}
#[derive(Debug)]
struct Blueprints(Vec<Blueprint>);
impl Blueprints {
fn parse_input<R: BufRead>(reader: R) -> Blueprints {
let inner = reader
.lines()
.map(Result::unwrap)
.map(Blueprint::parse_line)
.collect();
Self(inner)
}
fn total_score(&self) -> usize {
self.0
.par_iter()
.map(|bp| bp.calculate_max_geode_openable() * bp.id)
.sum()
}
}
fn solve_first_puzzle<R: BufRead>(reader: R) -> usize {
Blueprints::parse_input(reader).total_score()
}
fn solve_second_puzzle<R: BufRead>(reader: R) -> usize {
todo!()
}
fn main() {
let mut args = ::std::env::args().skip(1);
let file = args.next().unwrap_or_else(|| String::from("./input"));
let solve = args
.next()
.map(|arg| match arg.as_str() {
"first" => SolvePuzzle::First,
"second" => SolvePuzzle::Second,
_ => unreachable!(),
})
.unwrap_or_default();
let file = BufReader::new(File::open(file).expect("Opening file"));
match solve {
SolvePuzzle::First => println!("{}", solve_first_puzzle(file)),
SolvePuzzle::Second => println!("{}", solve_second_puzzle(file)),
};
}
impl const Default for RobotState {
fn default() -> Self {
Self {
// Just one ore robot
ore_robots: 1,
clay_robots: 0,
obisidian_robots: 0,
geode_robots: 0,
}
}
}
impl const Add for ResourceState {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
ore: self.ore + rhs.ore,
clay: self.clay + rhs.clay,
obsidian: self.obsidian + rhs.obsidian,
geodes: self.geodes + rhs.geodes,
}
}
}
#[cfg(test)]
mod tests {
use std::io::Cursor;
use super::*;
#[test]
fn example_on_first() {
let reader = Cursor::new(include_str!("../input.test"));
assert_eq!(solve_first_puzzle(reader), 33);
}
#[test]
fn example_on_second() {
let reader = Cursor::new(include_str!("../input.test"));
assert_eq!(solve_second_puzzle(reader), 54);
}
}