day 19 finished
This commit is contained in:
parent
b00835c25e
commit
062ede1df1
4 changed files with 631 additions and 1 deletions
11
data/day19/example01.txt
Normal file
11
data/day19/example01.txt
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
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.
|
||||
30
data/day19/input.txt
Normal file
30
data/day19/input.txt
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
Blueprint 1: 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 4 ore and 11 obsidian.
|
||||
Blueprint 2: 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 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 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 5: 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 6: Each ore robot costs 2 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 19 clay. Each geode robot costs 2 ore and 18 obsidian.
|
||||
Blueprint 7: 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 8: Each ore robot costs 2 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 16 clay. Each geode robot costs 4 ore and 17 obsidian.
|
||||
Blueprint 9: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 11 clay. Each geode robot costs 4 ore and 7 obsidian.
|
||||
Blueprint 10: 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 11: Each ore robot costs 2 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 19 clay. Each geode robot costs 4 ore and 12 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 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 14: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 16 clay. Each geode robot costs 3 ore and 14 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 16 obsidian.
|
||||
Blueprint 16: 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 17: 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 19 obsidian.
|
||||
Blueprint 18: 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 19: 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 3 ore and 10 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 4 ore and 12 obsidian.
|
||||
Blueprint 21: 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 22: 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 23: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 7 clay. Each geode robot costs 3 ore and 9 obsidian.
|
||||
Blueprint 24: 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 25: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 15 clay. Each geode robot costs 2 ore and 8 obsidian.
|
||||
Blueprint 26: 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 27: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 19 clay. Each geode robot costs 3 ore and 8 obsidian.
|
||||
Blueprint 28: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 8 clay. Each geode robot costs 2 ore and 12 obsidian.
|
||||
Blueprint 29: 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 30: 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.
|
||||
587
src/days/day19/mod.rs
Normal file
587
src/days/day19/mod.rs
Normal file
|
|
@ -0,0 +1,587 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use crate::common::parser::{extract_result, ignore, trim0, trim1, trim_left1};
|
||||
use itertools::Itertools;
|
||||
use nom::{
|
||||
branch::alt,
|
||||
bytes::complete::tag,
|
||||
character::complete::{char, i64, multispace0, u32},
|
||||
combinator::{value, verify},
|
||||
error::Error,
|
||||
multi::{count, many0, separated_list1},
|
||||
sequence::{preceded, tuple},
|
||||
Err, IResult, Parser,
|
||||
};
|
||||
use std::{
|
||||
collections::{BinaryHeap, HashMap},
|
||||
mem,
|
||||
ops::{Add, IndexMut, Sub},
|
||||
sync::mpsc,
|
||||
thread,
|
||||
};
|
||||
use std::{ops::Index, str::FromStr};
|
||||
use thiserror::Error;
|
||||
|
||||
const DAY_NUMBER: usize = 19;
|
||||
|
||||
const NUMBER_MATERIALS: usize = 4;
|
||||
const USE_THREADED: bool = true;
|
||||
|
||||
pub struct Day;
|
||||
|
||||
impl DayTrait for Day {
|
||||
fn get_day_number(&self) -> usize {
|
||||
DAY_NUMBER
|
||||
}
|
||||
|
||||
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||
let cabinet: Cabinet = lines.parse()?;
|
||||
let result = if USE_THREADED {
|
||||
cabinet.threaded_quality_level(24)?
|
||||
} else {
|
||||
cabinet.quality_level(24)?
|
||||
};
|
||||
Ok(ResultType::Integer(result))
|
||||
}
|
||||
|
||||
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||
let cabinet: Cabinet = lines.parse()?;
|
||||
let result = if USE_THREADED {
|
||||
cabinet.threaded_reduced_quality(3, 32)?
|
||||
} else {
|
||||
cabinet.reduced_quality(3, 32)?
|
||||
};
|
||||
Ok(ResultType::Integer(result))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
enum RobotError {
|
||||
#[error("Not a valid description: {0}")]
|
||||
ParsingError(String),
|
||||
|
||||
#[error("No optimum was found")]
|
||||
NoOptimumFound,
|
||||
}
|
||||
|
||||
impl From<Err<Error<&str>>> for RobotError {
|
||||
fn from(error: Err<Error<&str>>) -> Self {
|
||||
RobotError::ParsingError(error.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Ord, Eq, Hash)]
|
||||
enum Material {
|
||||
Geode = 0,
|
||||
Obsidian = 1,
|
||||
Clay = 2,
|
||||
Ore = 3,
|
||||
}
|
||||
|
||||
impl Material {
|
||||
pub fn prev(&self) -> Option<Material> {
|
||||
match self {
|
||||
Material::Geode => None,
|
||||
Material::Obsidian => Some(Material::Geode),
|
||||
Material::Clay => Some(Material::Obsidian),
|
||||
Material::Ore => Some(Material::Clay),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next(&self) -> Option<Material> {
|
||||
match self {
|
||||
Material::Geode => Some(Material::Obsidian),
|
||||
Material::Obsidian => Some(Material::Clay),
|
||||
Material::Clay => Some(Material::Ore),
|
||||
Material::Ore => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(input: &str) -> IResult<&str, Material> {
|
||||
alt((
|
||||
value(Material::Ore, tag("ore")),
|
||||
value(Material::Clay, tag("clay")),
|
||||
value(Material::Obsidian, tag("obsidian")),
|
||||
value(Material::Geode, tag("geode")),
|
||||
))(input)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
struct Ingredients([i64; NUMBER_MATERIALS]);
|
||||
|
||||
impl Ingredients {
|
||||
pub fn new(items: Vec<(i64, Material)>) -> Self {
|
||||
let mut ingredients = [0; NUMBER_MATERIALS];
|
||||
for (amount, material) in items {
|
||||
ingredients[material as usize] = amount;
|
||||
}
|
||||
Ingredients(ingredients)
|
||||
}
|
||||
|
||||
pub fn is_non_negative(&self) -> bool {
|
||||
self.0.iter().all(|item| *item >= 0)
|
||||
}
|
||||
|
||||
fn inc(&self, mat: Material) -> Ingredients {
|
||||
let mut next = self.0.clone();
|
||||
next[mat as usize] += 1;
|
||||
Ingredients(next)
|
||||
}
|
||||
|
||||
fn pos_max(&self, other: &Ingredients) -> Self {
|
||||
let next = self
|
||||
.0
|
||||
.iter()
|
||||
.zip(other.0.iter())
|
||||
.map(|(a, b)| *a.max(b))
|
||||
.collect_vec()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
Ingredients(next)
|
||||
}
|
||||
|
||||
fn none_smaller(&self, other: &Ingredients) -> bool {
|
||||
self.0.iter().zip(other.0.iter()).all(|(a, b)| a >= b)
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<Material> for Ingredients {
|
||||
type Output = i64;
|
||||
|
||||
fn index(&self, index: Material) -> &Self::Output {
|
||||
&self.0[index as usize]
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<Material> for Ingredients {
|
||||
fn index_mut(&mut self, index: Material) -> &mut Self::Output {
|
||||
&mut self.0[index as usize]
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<&Ingredients> for Ingredients {
|
||||
type Output = Ingredients;
|
||||
|
||||
fn add(self, rhs: &Ingredients) -> Self::Output {
|
||||
let mut result = self.clone();
|
||||
for pos in 0..NUMBER_MATERIALS {
|
||||
result.0[pos] += rhs.0[pos];
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<&Ingredients> for &Ingredients {
|
||||
type Output = Ingredients;
|
||||
|
||||
fn add(self, rhs: &Ingredients) -> Self::Output {
|
||||
let mut result = self.clone();
|
||||
for pos in 0..NUMBER_MATERIALS {
|
||||
result.0[pos] += rhs.0[pos];
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<&Ingredients> for Ingredients {
|
||||
type Output = Ingredients;
|
||||
|
||||
fn sub(self, rhs: &Self) -> Self::Output {
|
||||
let mut result = self.clone();
|
||||
for pos in 0..NUMBER_MATERIALS {
|
||||
result.0[pos] -= rhs.0[pos];
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<&Ingredients> for &Ingredients {
|
||||
type Output = Ingredients;
|
||||
|
||||
fn sub(self, rhs: &Ingredients) -> Self::Output {
|
||||
let mut result = self.clone();
|
||||
for pos in 0..NUMBER_MATERIALS {
|
||||
result.0[pos] -= rhs.0[pos];
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct Blueprint {
|
||||
id: usize,
|
||||
material: [Ingredients; NUMBER_MATERIALS],
|
||||
max_robots: Ingredients,
|
||||
}
|
||||
|
||||
impl Blueprint {
|
||||
pub fn new(id: usize, material: [Ingredients; NUMBER_MATERIALS]) -> Self {
|
||||
let mut max_robots = material[Material::Geode as usize]
|
||||
.pos_max(&material[Material::Obsidian as usize])
|
||||
.pos_max(&material[Material::Clay as usize]);
|
||||
max_robots[Material::Geode] = i32::MAX as i64;
|
||||
|
||||
Blueprint {
|
||||
id,
|
||||
material,
|
||||
max_robots,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ingredients_for(&self, mat: Material) -> &Ingredients {
|
||||
&self.material[mat as usize]
|
||||
}
|
||||
|
||||
fn parse_line(input: &str) -> IResult<&str, (Material, Ingredients)> {
|
||||
let input = ignore(tag("Each"))(input)?;
|
||||
let (input, robot) = trim1(Material::parse)(input)?;
|
||||
let input = ignore(tag("robot costs"))(input)?;
|
||||
let (input, ingredients) =
|
||||
separated_list1(tag("and"), tuple((trim0(i64), trim0(Material::parse))))(input)?;
|
||||
let input = ignore(char('.'))(input)?;
|
||||
Ok((input, (robot, Ingredients::new(ingredients))))
|
||||
}
|
||||
|
||||
fn parse(start: &str) -> IResult<&str, Self> {
|
||||
let input = ignore(tag("Blueprint"))(start)?;
|
||||
let (input, id) = trim_left1(u32.map(|v| v as usize))(input)?;
|
||||
let input = ignore(char(':'))(input)?;
|
||||
let (input, robots) = verify(
|
||||
count(
|
||||
preceded(multispace0, Blueprint::parse_line),
|
||||
NUMBER_MATERIALS,
|
||||
),
|
||||
|robots: &[(Material, Ingredients)]| {
|
||||
robots.iter().map(|(material, _)| material).all_unique()
|
||||
},
|
||||
)(input)?;
|
||||
|
||||
let material: [Ingredients; NUMBER_MATERIALS] = robots
|
||||
.into_iter()
|
||||
.sorted_by_key(|(material, _)| *material)
|
||||
.map(|(_, ingreditents)| ingreditents)
|
||||
.collect_vec()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
Ok((input, Blueprint::new(id, material)))
|
||||
}
|
||||
|
||||
pub fn simulate(&self, max_time: usize) -> Result<(usize, i64), RobotError> {
|
||||
let simulation = Simulation::new(max_time);
|
||||
let mut queue = BinaryHeap::new();
|
||||
let mut seen: HashMap<(Ingredients, usize), Ingredients> = HashMap::new();
|
||||
queue.push(simulation);
|
||||
|
||||
while let Some(current) = queue.pop() {
|
||||
if current.time == 0 {
|
||||
return Ok((self.id, current.material(Material::Geode)));
|
||||
}
|
||||
let key = (current.all_robots().clone(), current.time);
|
||||
if let Some(last) = seen.get(&key) {
|
||||
if last.none_smaller(current.all_material()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
seen.insert(key, current.all_material().clone());
|
||||
|
||||
queue.extend(current.next_round(self));
|
||||
}
|
||||
|
||||
Err(RobotError::NoOptimumFound)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Blueprint {
|
||||
type Err = RobotError;
|
||||
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
Ok(extract_result(Blueprint::parse)(input)?)
|
||||
}
|
||||
}
|
||||
|
||||
struct Cabinet(Vec<Blueprint>);
|
||||
|
||||
impl FromStr for Cabinet {
|
||||
type Err = RobotError;
|
||||
|
||||
fn from_str(line: &str) -> Result<Self, Self::Err> {
|
||||
let blueprints = extract_result(many0(preceded(multispace0, Blueprint::parse)))(line)?;
|
||||
Ok(Cabinet(blueprints))
|
||||
}
|
||||
}
|
||||
|
||||
impl Cabinet {
|
||||
pub fn quality_level(&self, max_time: usize) -> Result<i64, RobotError> {
|
||||
self.0
|
||||
.iter()
|
||||
.map(|blueprint| blueprint.simulate(max_time))
|
||||
.map_ok(|(id, geodes)| id as i64 * geodes)
|
||||
.fold_ok(0, |a, b| a + b)
|
||||
}
|
||||
|
||||
pub fn reduced_quality(&self, count: usize, max_time: usize) -> Result<i64, RobotError> {
|
||||
self.0
|
||||
.iter()
|
||||
.take(count)
|
||||
.map(|blueprint| blueprint.simulate(max_time))
|
||||
.map_ok(|(_, geodes)| geodes)
|
||||
.fold_ok(1, |a, b| a * b)
|
||||
}
|
||||
|
||||
pub fn threaded_quality_level(self, max_time: usize) -> Result<i64, RobotError> {
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
for blueprint in self.0 {
|
||||
let sender = sender.clone();
|
||||
thread::spawn(move || {
|
||||
let result = blueprint.simulate(max_time);
|
||||
let _ = sender.send(result);
|
||||
});
|
||||
}
|
||||
mem::drop(sender);
|
||||
|
||||
receiver
|
||||
.into_iter()
|
||||
.map_ok(|(id, geodes)| id as i64 * geodes)
|
||||
.fold_ok(0, |a, b| a + b)
|
||||
}
|
||||
|
||||
pub fn threaded_reduced_quality(
|
||||
self,
|
||||
count: usize,
|
||||
max_time: usize,
|
||||
) -> Result<i64, RobotError> {
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
let mut done = 0;
|
||||
for blueprint in self.0 {
|
||||
let sender = sender.clone();
|
||||
thread::spawn(move || {
|
||||
let result = blueprint.simulate(max_time);
|
||||
let _ = sender.send(result);
|
||||
});
|
||||
|
||||
done = done + 1;
|
||||
if done >= count {
|
||||
break;
|
||||
}
|
||||
}
|
||||
mem::drop(sender);
|
||||
|
||||
receiver
|
||||
.into_iter()
|
||||
.map_ok(|(_, geodes)| geodes)
|
||||
.fold_ok(1, |a, b| a * b)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Simulation {
|
||||
time: usize,
|
||||
robots: Ingredients,
|
||||
material: Ingredients,
|
||||
}
|
||||
|
||||
impl Simulation {
|
||||
pub fn new(time: usize) -> Self {
|
||||
let _robots = Ingredients::new(vec![(1, Material::Ore)]);
|
||||
let _material = Ingredients::new(vec![]);
|
||||
Self {
|
||||
time,
|
||||
robots: _robots,
|
||||
material: _material,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn all_robots(&self) -> &Ingredients {
|
||||
&self.robots
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn robots_for(&self, mat: Material) -> i64 {
|
||||
self.robots[mat]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn all_material(&self) -> &Ingredients {
|
||||
&self.material
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn material(&self, mat: Material) -> i64 {
|
||||
self.material[mat]
|
||||
}
|
||||
|
||||
pub fn no_production(mut self) -> Self {
|
||||
self.time = self.time - 1;
|
||||
self.material = self.material + &self.robots;
|
||||
self
|
||||
}
|
||||
|
||||
fn check_create(&self, blueprint: &Blueprint, mat: Material) -> Option<Simulation> {
|
||||
let rest = self.all_material() - blueprint.ingredients_for(mat);
|
||||
if rest.is_non_negative() {
|
||||
let robots = self.all_robots().inc(mat);
|
||||
let material = rest + self.all_robots();
|
||||
Some(Simulation {
|
||||
time: self.time - 1,
|
||||
robots,
|
||||
material,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_round(self, blueprint: &Blueprint) -> Vec<Simulation> {
|
||||
let mut result = vec![];
|
||||
if let Some(next) = self.check_create(blueprint, Material::Geode) {
|
||||
result.push(next);
|
||||
}
|
||||
|
||||
let mut current = Some(Material::Obsidian);
|
||||
while let Some(mat) = current {
|
||||
if self.robots_for(mat) < blueprint.max_robots[mat]
|
||||
&& self.material(mat) <= 4 * blueprint.max_robots[mat] / 3
|
||||
{
|
||||
if let Some(next) = self.check_create(blueprint, mat) {
|
||||
result.push(next);
|
||||
}
|
||||
}
|
||||
current = mat.next();
|
||||
}
|
||||
if result.len() <= 1 {
|
||||
result.push(self.no_production());
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Simulation {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.time == other.time && self.material == other.material
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Simulation {}
|
||||
|
||||
impl PartialOrd for Simulation {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Simulation {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
match self.time.cmp(&other.time) {
|
||||
core::cmp::Ordering::Equal => {}
|
||||
ord => return ord,
|
||||
}
|
||||
self.material.cmp(&other.material)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::common::file::read_string;
|
||||
use anyhow::Result;
|
||||
|
||||
#[test]
|
||||
fn test_part1() -> Result<()> {
|
||||
let day = Day {};
|
||||
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||
let expected = ResultType::Integer(33);
|
||||
let result = day.part1(&lines)?;
|
||||
assert_eq!(result, expected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part2() -> Result<()> {
|
||||
let day = Day {};
|
||||
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||
let expected = ResultType::Integer(56 * 62);
|
||||
let result = day.part2(&lines)?;
|
||||
assert_eq!(result, expected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse() -> Result<()> {
|
||||
let line = "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.";
|
||||
let expected = Blueprint::new(
|
||||
1,
|
||||
[
|
||||
Ingredients([0, 7, 0, 2]),
|
||||
Ingredients([0, 0, 14, 3]),
|
||||
Ingredients([0, 0, 0, 2]),
|
||||
Ingredients([0, 0, 0, 4]),
|
||||
],
|
||||
);
|
||||
let result: Blueprint = line.parse()?;
|
||||
assert_eq!(result, expected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_faulty() {
|
||||
let line = "Blueprint 1: Each ore robot costs 4 ore. Each ore robot costs 2 ore. Each obsidian robot costs 3 ore and 14 clay. Each geode robot costs 2 ore and 7 obsidian.";
|
||||
let result: Result<Blueprint, _> = line.parse();
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_many() -> Result<()> {
|
||||
let day = Day {};
|
||||
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||
let cabinet: Cabinet = lines.parse()?;
|
||||
assert_eq!(cabinet.0.len(), 2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simulate_one() -> Result<()> {
|
||||
let line = "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.";
|
||||
let blueprint: Blueprint = line.parse()?;
|
||||
let result = blueprint.simulate(24)?;
|
||||
assert_eq!(result, (1, 9));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simulate_two() -> Result<()> {
|
||||
let line = "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.";
|
||||
let blueprint: Blueprint = line.parse()?;
|
||||
let result = blueprint.simulate(24)?;
|
||||
assert_eq!(result, (2, 12));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simulate_one_two() -> Result<()> {
|
||||
let line = "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.";
|
||||
let blueprint: Blueprint = line.parse()?;
|
||||
let result = blueprint.simulate(32)?;
|
||||
assert_eq!(result, (1, 56));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simulate_two_two() -> Result<()> {
|
||||
let line = "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.";
|
||||
|
||||
let blueprint: Blueprint = line.parse()?;
|
||||
let result = blueprint.simulate(32)?;
|
||||
assert_eq!(result, (2, 62));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ mod day15;
|
|||
mod day16;
|
||||
mod day17;
|
||||
mod day18;
|
||||
mod day19;
|
||||
mod template;
|
||||
|
||||
pub use template::DayTrait;
|
||||
|
|
@ -25,7 +26,7 @@ pub mod day_provider {
|
|||
use super::*;
|
||||
use thiserror::Error;
|
||||
|
||||
const MAX_DAY: usize = 18;
|
||||
const MAX_DAY: usize = 19;
|
||||
|
||||
pub fn get_day(day_num: usize) -> Result<Box<dyn DayTrait>, ProviderError> {
|
||||
match day_num {
|
||||
|
|
@ -47,6 +48,7 @@ pub mod day_provider {
|
|||
16 => Ok(Box::new(day16::Day)),
|
||||
17 => Ok(Box::new(day17::Day)),
|
||||
18 => Ok(Box::new(day18::Day)),
|
||||
19 => Ok(Box::new(day19::Day)),
|
||||
_ => Err(ProviderError::InvalidNumber(day_num)),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue