Solutions for 2022, as well as 2015-2018 and 2019 up to day 11
This commit is contained in:
commit
1895197c49
722 changed files with 375457 additions and 0 deletions
8
2015/day19-medicine_for_rudolph/Cargo.toml
Normal file
8
2015/day19-medicine_for_rudolph/Cargo.toml
Normal file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "day19-medicine_for_rudolph"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
71
2015/day19-medicine_for_rudolph/challenge.txt
Normal file
71
2015/day19-medicine_for_rudolph/challenge.txt
Normal file
|
@ -0,0 +1,71 @@
|
|||
\--- Day 19: Medicine for Rudolph ---
|
||||
----------
|
||||
|
||||
Rudolph the Red-Nosed Reindeer is sick! His nose isn't shining very brightly, and he needs medicine.
|
||||
|
||||
Red-Nosed Reindeer biology isn't similar to regular reindeer biology; Rudolph is going to need custom-made medicine. Unfortunately, Red-Nosed Reindeer chemistry isn't similar to regular reindeer chemistry, either.
|
||||
|
||||
The North Pole is equipped with a Red-Nosed Reindeer nuclear fusion/fission plant, capable of constructing any Red-Nosed Reindeer molecule you need. It works by starting with some input molecule and then doing a series of *replacements*, one per step, until it has the right molecule.
|
||||
|
||||
However, the machine has to be calibrated before it can be used. Calibration involves determining the number of molecules that can be generated in one step from a given starting point.
|
||||
|
||||
For example, imagine a simpler machine that supports only the following replacements:
|
||||
|
||||
```
|
||||
H => HO
|
||||
H => OH
|
||||
O => HH
|
||||
|
||||
```
|
||||
|
||||
Given the replacements above and starting with `HOH`, the following molecules could be generated:
|
||||
|
||||
* `HOOH` (via `H => HO` on the first `H`).
|
||||
* `HOHO` (via `H => HO` on the second `H`).
|
||||
* `OHOH` (via `H => OH` on the first `H`).
|
||||
* `HOOH` (via `H => OH` on the second `H`).
|
||||
* `HHHH` (via `O => HH`).
|
||||
|
||||
So, in the example above, there are `4` *distinct* molecules (not five, because `HOOH` appears twice) after one replacement from `HOH`. Santa's favorite molecule, `HOHOHO`, can become `7` *distinct* molecules (over nine replacements: six from `H`, and three from `O`).
|
||||
|
||||
The machine replaces without regard for the surrounding characters. For example, given the string `H2O`, the transition `H => OO` would result in `OO2O`.
|
||||
|
||||
Your puzzle input describes all of the possible replacements and, at the bottom, the medicine molecule for which you need to calibrate the machine. *How many distinct molecules can be created* after all the different ways you can do one replacement on the medicine molecule?
|
||||
|
||||
Your puzzle answer was `535`.
|
||||
|
||||
\--- Part Two ---
|
||||
----------
|
||||
|
||||
Now that the machine is calibrated, you're ready to begin molecule fabrication.
|
||||
|
||||
Molecule fabrication always begins with just a single electron, `e`, and applying replacements one at a time, just like the ones during calibration.
|
||||
|
||||
For example, suppose you have the following replacements:
|
||||
|
||||
```
|
||||
e => H
|
||||
e => O
|
||||
H => HO
|
||||
H => OH
|
||||
O => HH
|
||||
|
||||
```
|
||||
|
||||
If you'd like to make `HOH`, you start with `e`, and then make the following replacements:
|
||||
|
||||
* `e => O` to get `O`
|
||||
* `O => HH` to get `HH`
|
||||
* `H => OH` (on the second `H`) to get `HOH`
|
||||
|
||||
So, you could make `HOH` after *`3` steps*. Santa's favorite molecule, `HOHOHO`, can be made in *`6` steps*.
|
||||
|
||||
How long will it take to make the medicine? Given the available *replacements* and the *medicine molecule* in your puzzle input, what is the *fewest number of steps* to go from `e` to the medicine molecule?
|
||||
|
||||
Your puzzle answer was `212`.
|
||||
|
||||
Both parts of this puzzle are complete! They provide two gold stars: \*\*
|
||||
|
||||
At this point, all that is left is for you to [admire your Advent calendar](/2015).
|
||||
|
||||
If you still want to see it, you can [get your puzzle input](19/input).
|
135
2015/day19-medicine_for_rudolph/src/lib.rs
Normal file
135
2015/day19-medicine_for_rudolph/src/lib.rs
Normal file
|
@ -0,0 +1,135 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
pub fn run(input: &str) -> (usize, usize) {
|
||||
let (replacements, initial) = parse_input(input);
|
||||
let first = get_replacements(&replacements, initial).len();
|
||||
let second = find_reduction(&replacements, initial.to_string()).expect("Unable to construct target molecule");
|
||||
// let second = a_star_search(initial, "e", &replacements, &str::len);
|
||||
(first, second)
|
||||
}
|
||||
|
||||
fn parse_input(input: &str) -> (Vec<(&str, &str)>, &str) {
|
||||
let (replacements_str, initial) = input.split_once("\n\n").expect("Unable to split input by blank line");
|
||||
|
||||
(
|
||||
replacements_str.lines()
|
||||
.map(|line| line.split_once(" => ").unwrap_or_else(|| panic!("unable to split {} by \" => \"", line)))
|
||||
.collect(),
|
||||
initial.trim()
|
||||
)
|
||||
}
|
||||
/*
|
||||
fn a_star_search(start: &str, goal: &str, replacements: &[(&str, &str)], h: &dyn Fn(&str)->usize) -> usize {
|
||||
// The set of discovered nodes. Initially only the start node is known.
|
||||
let mut open_set = HashSet::from([start.to_string()]);
|
||||
// A map from a node to its predecessor on its cheapest known path.
|
||||
let mut came_from = HashMap::new();
|
||||
// A map from a node to its lowest known costs.
|
||||
let mut g_score = HashMap::from([(start.to_string(), 0)]);
|
||||
// Estimated costs of each path (f = g+h)
|
||||
let mut f_score = HashMap::from([(start.to_string(), h(start))]);
|
||||
|
||||
loop {
|
||||
let current = open_set.iter()
|
||||
.min_by(|&a, &b| f_score.get(a).unwrap()
|
||||
.cmp(f_score.get(b).unwrap()))
|
||||
.unwrap().to_owned();
|
||||
if current == goal {
|
||||
return reconstruct_path(came_from, ¤t);
|
||||
}
|
||||
open_set.remove(¤t);
|
||||
for neighbour in get_reductions(replacements, current.to_string()) {
|
||||
let tentative_g_score = g_score.get(¤t).unwrap() + 1;
|
||||
let current_g_score = *g_score.get(&neighbour[..]).unwrap_or(&usize::MAX);
|
||||
if tentative_g_score < current_g_score {
|
||||
came_from.insert(neighbour.to_owned(), current.to_string());
|
||||
g_score.insert(neighbour.to_owned(), tentative_g_score);
|
||||
f_score.insert(neighbour.to_owned(), tentative_g_score + h(&neighbour));
|
||||
open_set.insert(neighbour);
|
||||
}
|
||||
}
|
||||
if open_set.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
panic!("Open Set is empty, but goal was never reached.")
|
||||
}
|
||||
|
||||
fn reconstruct_path(came_from: HashMap<String, String>, goal: &str) -> usize {
|
||||
let mut total_path_len = 0;
|
||||
let mut current = goal;
|
||||
while let Some(predecessor) = came_from.get(current) {
|
||||
total_path_len += 1;
|
||||
current = predecessor;
|
||||
}
|
||||
total_path_len
|
||||
}
|
||||
*/
|
||||
|
||||
// Always returns the first reduction it finds by trying to shorten the string as much as possible
|
||||
// as early as possible. This yields the correct results for me, but more hostile inputs probably
|
||||
// require a more thorough approach, such as the A* algorithm shown above.
|
||||
fn find_reduction(replacements: &[(&str, &str)], target: String) -> Option<usize> {
|
||||
if target == *"e" {
|
||||
Some(0)
|
||||
} else {
|
||||
let mut next_step: Vec<_> = get_reductions(replacements, target).into_iter().collect();
|
||||
|
||||
next_step.sort_by_key(|a| a.len());
|
||||
for next_attemt in next_step {
|
||||
let this_attempt = find_reduction(replacements, next_attemt);
|
||||
if let Some(score) = this_attempt {
|
||||
return Some(score + 1);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_reductions(replacements: &[(&str, &str)], target: String) -> HashSet<String> {
|
||||
let mut res = HashSet::new();
|
||||
for idx in 0..target.len() {
|
||||
for (from, to) in replacements {
|
||||
if target[idx..].find(*to) == Some(0) {
|
||||
res.insert(format!("{}{}{}", &target[..idx].to_string(), from, &target[idx+to.len()..].to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
fn get_replacements(replacements: &[(&str, &str)], initial: &str) -> HashSet<String> {
|
||||
let mut res = HashSet::new();
|
||||
for idx in 0..initial.len() {
|
||||
for (from, to) in replacements {
|
||||
if initial[idx..].find(*from) == Some(0) {
|
||||
res.insert(format!("{}{}{}", &initial[..idx].to_string(), to, &initial[idx+from.len()..].to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::fs::read_to_string;
|
||||
|
||||
fn read_file(name: &str) -> String {
|
||||
read_to_string(name).expect(&format!("Unable to read file: {}", name)[..])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sample() {
|
||||
let sample_input = read_file("tests/sample_input");
|
||||
assert_eq!(run(&sample_input), (4, 3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_challenge() {
|
||||
let challenge_input = read_file("tests/challenge_input");
|
||||
assert_eq!(run(&challenge_input), (535, 212));
|
||||
}
|
||||
}
|
45
2015/day19-medicine_for_rudolph/tests/challenge_input
Normal file
45
2015/day19-medicine_for_rudolph/tests/challenge_input
Normal file
|
@ -0,0 +1,45 @@
|
|||
Al => ThF
|
||||
Al => ThRnFAr
|
||||
B => BCa
|
||||
B => TiB
|
||||
B => TiRnFAr
|
||||
Ca => CaCa
|
||||
Ca => PB
|
||||
Ca => PRnFAr
|
||||
Ca => SiRnFYFAr
|
||||
Ca => SiRnMgAr
|
||||
Ca => SiTh
|
||||
F => CaF
|
||||
F => PMg
|
||||
F => SiAl
|
||||
H => CRnAlAr
|
||||
H => CRnFYFYFAr
|
||||
H => CRnFYMgAr
|
||||
H => CRnMgYFAr
|
||||
H => HCa
|
||||
H => NRnFYFAr
|
||||
H => NRnMgAr
|
||||
H => NTh
|
||||
H => OB
|
||||
H => ORnFAr
|
||||
Mg => BF
|
||||
Mg => TiMg
|
||||
N => CRnFAr
|
||||
N => HSi
|
||||
O => CRnFYFAr
|
||||
O => CRnMgAr
|
||||
O => HP
|
||||
O => NRnFAr
|
||||
O => OTi
|
||||
P => CaP
|
||||
P => PTi
|
||||
P => SiRnFAr
|
||||
Si => CaSi
|
||||
Th => ThCa
|
||||
Ti => BP
|
||||
Ti => TiTi
|
||||
e => HF
|
||||
e => NAl
|
||||
e => OMg
|
||||
|
||||
CRnCaCaCaSiRnBPTiMgArSiRnSiRnMgArSiRnCaFArTiTiBSiThFYCaFArCaCaSiThCaPBSiThSiThCaCaPTiRnPBSiThRnFArArCaCaSiThCaSiThSiRnMgArCaPTiBPRnFArSiThCaSiRnFArBCaSiRnCaPRnFArPMgYCaFArCaPTiTiTiBPBSiThCaPTiBPBSiRnFArBPBSiRnCaFArBPRnSiRnFArRnSiRnBFArCaFArCaCaCaSiThSiThCaCaPBPTiTiRnFArCaPTiBSiAlArPBCaCaCaCaCaSiRnMgArCaSiThFArThCaSiThCaSiRnCaFYCaSiRnFYFArFArCaSiRnFYFArCaSiRnBPMgArSiThPRnFArCaSiRnFArTiRnSiRnFYFArCaSiRnBFArCaSiRnTiMgArSiThCaSiThCaFArPRnFArSiRnFArTiTiTiTiBCaCaSiRnCaCaFYFArSiThCaPTiBPTiBCaSiThSiRnMgArCaF
|
7
2015/day19-medicine_for_rudolph/tests/sample_input
Normal file
7
2015/day19-medicine_for_rudolph/tests/sample_input
Normal file
|
@ -0,0 +1,7 @@
|
|||
e => H
|
||||
e => O
|
||||
H => HO
|
||||
H => OH
|
||||
O => HH
|
||||
|
||||
HOH
|
Loading…
Add table
Add a link
Reference in a new issue