110 lines
2.6 KiB
Rust
110 lines
2.6 KiB
Rust
use std::collections::{HashSet, VecDeque};
|
|
|
|
use aoc_runner_derive::{aoc, aoc_generator};
|
|
|
|
type Transformation = (String, String);
|
|
|
|
#[aoc_generator(day19)]
|
|
fn parse(input: &str) -> (Vec<Transformation>, String) {
|
|
let (a, b) = input.split_once("\n\n").unwrap();
|
|
let t = a
|
|
.lines()
|
|
.map(|line| {
|
|
let (lhs, rhs) = line.split_once(" => ").unwrap();
|
|
(lhs.to_string(), rhs.to_string())
|
|
})
|
|
.collect();
|
|
(t, b.to_string())
|
|
}
|
|
|
|
#[aoc(day19, part1)]
|
|
fn part1((transformations, start): &(Vec<Transformation>, String)) -> usize {
|
|
let all = possibilities(transformations, start);
|
|
all.len()
|
|
}
|
|
|
|
fn possibilities(transformations: &[Transformation], start: &str) -> HashSet<String> {
|
|
let all: HashSet<String> = transformations
|
|
.iter()
|
|
.flat_map(|(from, to)| {
|
|
let start_indexes = starts(start, from);
|
|
start_indexes.into_iter().map(|si| {
|
|
let mut s = start.to_string();
|
|
s.replace_range(si..si + from.len(), to);
|
|
s
|
|
})
|
|
})
|
|
.collect();
|
|
all
|
|
}
|
|
|
|
fn starts(a: &str, pattern: &str) -> Vec<usize> {
|
|
let mut start = 0;
|
|
let mut result = vec![];
|
|
while let Some(index) = a[start..].find(pattern) {
|
|
result.push(start + index);
|
|
start += index + 1;
|
|
}
|
|
result
|
|
}
|
|
|
|
#[aoc(day19, part2)]
|
|
fn part2((transformations, target): &(Vec<Transformation>, String)) -> usize {
|
|
search("e", transformations, target)
|
|
}
|
|
|
|
fn search(base: &str, transformations: &[Transformation], target: &str) -> usize {
|
|
let mut seen = HashSet::new();
|
|
seen.insert(base.to_string());
|
|
let mut queue = VecDeque::new();
|
|
queue.push_back((base.to_string(), 0));
|
|
while let Some((from, depth)) = queue.pop_front() {
|
|
for possibility in possibilities(transformations, &from) {
|
|
if possibility == target {
|
|
return depth + 1;
|
|
}
|
|
if seen.insert(possibility.clone()) {
|
|
queue.push_back((possibility, depth + 1));
|
|
}
|
|
}
|
|
}
|
|
unreachable!("No solution was found");
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
#[test]
|
|
fn part1() {
|
|
let input = "H => HO
|
|
H => OH
|
|
O => HH
|
|
|
|
HOH";
|
|
assert_eq!(super::part1(&super::parse(input)), 4);
|
|
}
|
|
|
|
#[test]
|
|
fn part2a() {
|
|
let input = "e => H
|
|
e => O
|
|
H => HO
|
|
H => OH
|
|
O => HH
|
|
|
|
HOH";
|
|
assert_eq!(super::part2(&super::parse(input)), 3);
|
|
}
|
|
|
|
#[test]
|
|
fn part2b() {
|
|
let input = "e => H
|
|
e => O
|
|
H => HO
|
|
H => OH
|
|
O => HH
|
|
|
|
HOHOHO";
|
|
assert_eq!(super::part2(&super::parse(input)), 6);
|
|
}
|
|
}
|