adventofcode

git clone https://git.ce9e.org/adventofcode.git

commit
51e3359997a003402bf9f171eb2bfc680764962d
parent
fd924f36e44c59a8e79d91dd7854ce3f5ab9dc2e
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2022-12-19 14:40
2022-12-19

Diffstat

A 2022/19/input.txt 30 ++++++++++++++++++++++++++++++
A 2022/19/solution.rs 188 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A 2022/19/test.txt 2 ++

3 files changed, 220 insertions, 0 deletions


diff --git a/2022/19/input.txt b/2022/19/input.txt

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

diff --git a/2022/19/solution.rs b/2022/19/solution.rs

@@ -0,0 +1,188 @@
   -1     1 use std::collections::HashSet;
   -1     2 use std::thread;
   -1     3 
   -1     4 #[path = "../lib.rs"] mod lib;
   -1     5 
   -1     6 fn get_blueprints() -> Vec<[[usize; 4]; 4]> {
   -1     7     let mut blueprints = vec![];
   -1     8     for line in lib::iter_input() {
   -1     9         let parts = line.split(" ").collect::<Vec<&str>>();
   -1    10         let ore_robot_ore_cost = parts[6].parse().unwrap();
   -1    11         let clay_robot_ore_cost = parts[12].parse().unwrap();
   -1    12         let obsidian_robot_ore_cost = parts[18].parse().unwrap();
   -1    13         let obsidian_robot_clay_cost = parts[21].parse().unwrap();
   -1    14         let geode_robot_ore_cost = parts[27].parse().unwrap();
   -1    15         let geode_robot_obsidian_cost = parts[30].parse().unwrap();
   -1    16         blueprints.push([
   -1    17             [ore_robot_ore_cost, 0, 0, 0],
   -1    18             [clay_robot_ore_cost, 0, 0, 0],
   -1    19             [obsidian_robot_ore_cost, obsidian_robot_clay_cost, 0, 0],
   -1    20             [geode_robot_ore_cost, 0, geode_robot_obsidian_cost, 0],
   -1    21         ]);
   -1    22     }
   -1    23     return blueprints;
   -1    24 }
   -1    25 
   -1    26 fn get_baseline(blueprint: &[[usize; 4]; 4], turns: usize) -> usize {
   -1    27     // thought up strategy that should yield good results.
   -1    28     // I am not sure if this is ideal though, so we will do a thorough search afterwards.
   -1    29 
   -1    30     let mut n = turns;
   -1    31     let mut resources = [0, 0, 0, 0];
   -1    32     let mut robots = [1, 0, 0, 0];
   -1    33 
   -1    34     // build enough ore robots to sustain geode robot production
   -1    35     while n > 0 && robots[0] < blueprint[3][0] {
   -1    36         let build = (0..4).all(|j| resources[j] >= blueprint[0][j]);
   -1    37         n -= 1;
   -1    38         for j in 0..4 {
   -1    39             resources[j] += robots[j];
   -1    40         }
   -1    41         if build {
   -1    42             for j in 0..4 {
   -1    43                 resources[j] -= blueprint[0][j];
   -1    44             }
   -1    45             robots[0] += 1;
   -1    46         }
   -1    47     }
   -1    48 
   -1    49     // build enough obsidian robots to sustain geode robot production
   -1    50     // and then only build geode robots
   -1    51     while n > 0 {
   -1    52         let can_clay = (0..4).all(|j| resources[j] >= blueprint[1][j]);
   -1    53         let can_obsidian = (0..4).all(|j| resources[j] >= blueprint[2][j]);
   -1    54         let can_geode = (0..4).all(|j| resources[j] >= blueprint[3][j]);
   -1    55 
   -1    56         let need_obsidian = robots[2] < blueprint[3][2];
   -1    57         let need_clay = need_obsidian && robots[1] < blueprint[2][1] && resources[1] < blueprint[2][1];
   -1    58 
   -1    59         n -= 1;
   -1    60         for j in 0..4 {
   -1    61             resources[j] += robots[j];
   -1    62         }
   -1    63 
   -1    64         if can_geode {
   -1    65             for j in 0..4 {
   -1    66                 resources[j] -= blueprint[3][j];
   -1    67             }
   -1    68             robots[3] += 1;
   -1    69         } else if need_obsidian && can_obsidian {
   -1    70             for j in 0..4 {
   -1    71                 resources[j] -= blueprint[2][j];
   -1    72             }
   -1    73             robots[2] += 1;
   -1    74         } else if need_clay && can_clay {
   -1    75             for j in 0..4 {
   -1    76                 resources[j] -= blueprint[1][j];
   -1    77             }
   -1    78             robots[1] += 1;
   -1    79         }
   -1    80     }
   -1    81 
   -1    82     return resources[3];
   -1    83 }
   -1    84 
   -1    85 fn get_max_geodes(blueprint: &[[usize; 4]; 4], turns: usize) -> usize {
   -1    86     let mut best_geodes = get_baseline(blueprint, turns);
   -1    87     let mut queue = vec![
   -1    88         ([0, 0, 0, 0], [1, 0, 0, 0], turns),
   -1    89     ];
   -1    90     let mut used = HashSet::new();
   -1    91 
   -1    92     while let Some((resources, robots, n)) = queue.pop() {
   -1    93         let min_geodes = resources[3] + robots[3] * n;
   -1    94         let potential = n * (n - 1) / 2;
   -1    95         if min_geodes + potential <= best_geodes {
   -1    96             continue;
   -1    97         }
   -1    98 
   -1    99         if used.contains(&(resources, robots, n)) {
   -1   100             continue;
   -1   101         } else {
   -1   102             used.insert((resources, robots, n));
   -1   103         }
   -1   104 
   -1   105         let mut build = vec![];
   -1   106 
   -1   107         // if it is possible to build a geode, always do that
   -1   108         if (0..4).all(|j| resources[j] >= blueprint[3][j]) {
   -1   109             build.push(3);
   -1   110         } else {
   -1   111             let need_obsidian = robots[2] < blueprint[3][2];
   -1   112             let need_clay = need_obsidian && robots[1] < blueprint[2][1];
   -1   113             let need_ore = robots[0] < blueprint[3][0] || (
   -1   114                 need_obsidian && robots[0] < blueprint[2][0]
   -1   115             ) || (
   -1   116                 need_clay && robots[0] < blueprint[1][0]
   -1   117             );
   -1   118 
   -1   119             build.push(4);
   -1   120             if need_ore && (0..4).all(|j| resources[j] >= blueprint[0][j]) {
   -1   121                 build.push(0);
   -1   122             }
   -1   123             if need_clay && (0..4).all(|j| resources[j] >= blueprint[1][j]) {
   -1   124                 build.push(1);
   -1   125             }
   -1   126             if need_obsidian && (0..4).all(|j| resources[j] >= blueprint[2][j]) {
   -1   127                 build.push(2);
   -1   128             }
   -1   129         }
   -1   130 
   -1   131         for i in build {
   -1   132             let mut next_resources = [
   -1   133                 resources[0] + robots[0],
   -1   134                 resources[1] + robots[1],
   -1   135                 resources[2] + robots[2],
   -1   136                 resources[3] + robots[3],
   -1   137             ];
   -1   138             let mut next_robots = robots.clone();
   -1   139             if i < 4 {
   -1   140                 for j in 0..4 {
   -1   141                     next_resources[j] -= blueprint[i][j];
   -1   142                 }
   -1   143                 next_robots[i] += 1;
   -1   144             }
   -1   145 
   -1   146             let min_geodes = next_resources[3] + next_robots[3] * (n - 1);
   -1   147             if min_geodes > best_geodes {
   -1   148                 best_geodes = min_geodes;
   -1   149             }
   -1   150 
   -1   151             queue.push((next_resources, next_robots, n - 1));
   -1   152         }
   -1   153     }
   -1   154 
   -1   155     return best_geodes;
   -1   156 }
   -1   157 
   -1   158 fn part1(blueprints: &Vec<[[usize; 4]; 4]>) -> usize {
   -1   159     let mut handles = vec![];
   -1   160     for (i, blueprint) in blueprints.iter().enumerate() {
   -1   161         let b = blueprint.clone();
   -1   162         handles.push(thread::spawn(move || {
   -1   163             return get_max_geodes(&b, 24) * (i + 1);
   -1   164         }));
   -1   165     }
   -1   166     return handles.into_iter()
   -1   167         .map(|handle| handle.join().unwrap())
   -1   168         .sum();
   -1   169 }
   -1   170 
   -1   171 fn part2(blueprints: &Vec<[[usize; 4]; 4]>) -> usize {
   -1   172     let mut handles = vec![];
   -1   173     for blueprint in blueprints.iter().take(3) {
   -1   174         let b = blueprint.clone();
   -1   175         handles.push(thread::spawn(move || {
   -1   176             return get_max_geodes(&b, 32);
   -1   177         }));
   -1   178     }
   -1   179     return handles.into_iter()
   -1   180         .map(|handle| handle.join().unwrap())
   -1   181         .product();
   -1   182 }
   -1   183 
   -1   184 fn main() {
   -1   185     let blueprints = get_blueprints();
   -1   186     println!("part1: {}", part1(&blueprints));
   -1   187     println!("part2: {}", part2(&blueprints));
   -1   188 }

diff --git a/2022/19/test.txt b/2022/19/test.txt

@@ -0,0 +1,2 @@
   -1     1 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.
   -1     2 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.