Day 11
This commit is contained in:
parent
2b436350f6
commit
e0df7d08ef
1 changed files with 194 additions and 0 deletions
194
src/bin/day11.rs
Normal file
194
src/bin/day11.rs
Normal file
|
|
@ -0,0 +1,194 @@
|
||||||
|
use std::{collections::HashMap, time::Instant};
|
||||||
|
|
||||||
|
use aoc_2025::{load, print_res};
|
||||||
|
use bstr::{BString, ByteSlice};
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
|
||||||
|
pub struct Label([u8; 3]);
|
||||||
|
|
||||||
|
impl std::fmt::Debug for Label {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_tuple("Label").field(&self.0.as_bstr()).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Label {
|
||||||
|
pub fn new(b: &[u8]) -> color_eyre::Result<Self> {
|
||||||
|
color_eyre::eyre::ensure!(b.len() == 3);
|
||||||
|
Ok(Self([b[0], b[1], b[2]]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Parsed = HashMap<Label, Vec<Label>>;
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
pub fn parsing(input: &BString) -> color_eyre::Result<Parsed> {
|
||||||
|
input
|
||||||
|
.lines()
|
||||||
|
.map(|l| {
|
||||||
|
let Some((label, downstream)) = l.split_once_str(b": ") else {
|
||||||
|
color_eyre::eyre::bail!("Malformed line: {}", l.as_bstr());
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
Label::new(label)?,
|
||||||
|
downstream
|
||||||
|
.split_str(" ")
|
||||||
|
.map(Label::new)
|
||||||
|
.collect::<Result<_, _>>()?,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path_count(at: Label, graph: &Parsed, cache: &mut HashMap<Label, usize>) -> usize {
|
||||||
|
if at == Label::new(b"out").unwrap() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
match cache.get(&at) {
|
||||||
|
Some(&v) => v,
|
||||||
|
None => {
|
||||||
|
let count = graph
|
||||||
|
.get(&at)
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|&v| path_count(v, graph, cache))
|
||||||
|
.sum();
|
||||||
|
cache.insert(at, count);
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
pub fn part1(input: Parsed) {
|
||||||
|
let mut cache = HashMap::new();
|
||||||
|
let paths = path_count(Label::new(b"you").unwrap(), &input, &mut cache);
|
||||||
|
print_res!("Path from you -> out: {paths}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
fn paths_by(
|
||||||
|
current: Vec<Label>,
|
||||||
|
mut saw: u8,
|
||||||
|
graph: &Parsed,
|
||||||
|
cache: &mut HashMap<(Label, u8), Vec<Vec<Label>>>,
|
||||||
|
) -> Option<Vec<Vec<Label>>> {
|
||||||
|
let &at = current.last().unwrap();
|
||||||
|
|
||||||
|
if at == Label::new(b"out").unwrap() {
|
||||||
|
if saw == 0b11 {
|
||||||
|
return Some(vec![current]);
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if at == Label::new(b"dac").unwrap() {
|
||||||
|
saw |= 0b01;
|
||||||
|
}
|
||||||
|
|
||||||
|
if at == Label::new(b"fft").unwrap() {
|
||||||
|
saw |= 0b10;
|
||||||
|
}
|
||||||
|
|
||||||
|
match cache.get(&(at, saw)) {
|
||||||
|
Some(v) => Some(v.clone()),
|
||||||
|
None => {
|
||||||
|
let paths: Vec<_> = graph
|
||||||
|
.get(&at)
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.filter_map(|&v| {
|
||||||
|
let mut new = current.clone();
|
||||||
|
new.push(v);
|
||||||
|
paths_by(new, saw, graph, cache)
|
||||||
|
})
|
||||||
|
.fold(Vec::new(), |mut acc, mut v| {
|
||||||
|
acc.append(&mut v);
|
||||||
|
acc
|
||||||
|
});
|
||||||
|
cache.insert((at, saw), paths.clone());
|
||||||
|
Some(paths)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paths_by_count(
|
||||||
|
at: Label,
|
||||||
|
mut saw: u8,
|
||||||
|
graph: &Parsed,
|
||||||
|
cache: &mut HashMap<(Label, u8), usize>,
|
||||||
|
) -> usize {
|
||||||
|
if at == Label::new(b"out").unwrap() {
|
||||||
|
if saw == 0b11 {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if at == Label::new(b"dac").unwrap() {
|
||||||
|
saw |= 0b01;
|
||||||
|
}
|
||||||
|
|
||||||
|
if at == Label::new(b"fft").unwrap() {
|
||||||
|
saw |= 0b10;
|
||||||
|
}
|
||||||
|
|
||||||
|
match cache.get(&(at, saw)) {
|
||||||
|
Some(&v) => v,
|
||||||
|
None => {
|
||||||
|
let count = graph
|
||||||
|
.get(&at)
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|&v| paths_by_count(v, saw, graph, cache))
|
||||||
|
.sum();
|
||||||
|
cache.insert((at, saw), count);
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
pub fn part2(input: Parsed) {
|
||||||
|
let mut cache = HashMap::new();
|
||||||
|
|
||||||
|
// let paths = paths_by(vec![Label::new(b"svr").unwrap()], 0, &input, &mut cache).unwrap();
|
||||||
|
//
|
||||||
|
// for path in paths {
|
||||||
|
// print!("{}", path[0].0.as_bstr());
|
||||||
|
// for p in &path[1..] {
|
||||||
|
// print!(",{}", p.0.as_bstr());
|
||||||
|
// }
|
||||||
|
// println!()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// todo!()
|
||||||
|
|
||||||
|
let paths = paths_by_count(Label::new(b"svr").unwrap(), 0, &input, &mut cache);
|
||||||
|
print_res!("Path from svr -> {{dac,fft}} -> out: {paths}");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() -> color_eyre::Result<()> {
|
||||||
|
let context = load()?;
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
let parsed = parsing(&context.input)?;
|
||||||
|
let elapsed = humantime::format_duration(start.elapsed());
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
if context.part == 1 {
|
||||||
|
part1(parsed);
|
||||||
|
} else {
|
||||||
|
part2(parsed);
|
||||||
|
}
|
||||||
|
let elapsed_part = humantime::format_duration(start.elapsed());
|
||||||
|
|
||||||
|
println!(" Parsing: {elapsed}");
|
||||||
|
println!(" Solving: {elapsed_part}");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue