Skip to content

Commit

Permalink
Day 25: Snowverload
Browse files Browse the repository at this point in the history
  • Loading branch information
ephemient committed Dec 25, 2023
1 parent 0de05f2 commit e3b945e
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 3 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ Development occurs in language-specific directories:
|[Day22.hs](hs/src/Day22.hs)|[Day22.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day22.kt)|[day22.py](py/aoc2023/day22.py)|[day22.rs](rs/src/day22.rs)|
||[Day23.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day23.kt)||[day23.rs](rs/src/day23.rs)|
|[Day24.hs](hs/src/Day24.hs) ½|[Day24.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day24.kt) ½|[day24.py](py/aoc2023/day24.py) ½|[day24.rs](rs/src/day24.rs) ½|
|[Day25.hs](hs/src/Day25.hs)||||
|[Day25.hs](hs/src/Day25.hs)|||[day25.rs](rs/src/day25.rs)|
7 changes: 6 additions & 1 deletion rs/benches/criterion.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use aoc2023::{
day1, day10, day11, day12, day13, day14, day15, day16, day17, day18, day19, day2, day20, day21,
day22, day23, day24, day3, day4, day5, day6, day7, day8, day9,
day22, day23, day24, day25, day3, day4, day5, day6, day7, day8, day9,
};
use criterion::{black_box, Criterion};
use std::env;
Expand Down Expand Up @@ -160,6 +160,11 @@ fn aoc2023_bench(c: &mut Criterion) -> io::Result<()> {
g.bench_function("part 1", |b| b.iter(|| day24::part1(black_box(&data))));
g.finish();

let data = get_day_input(25)?;
let mut g = c.benchmark_group("day 25");
g.bench_function("part 1", |b| b.iter(|| day25::part1(black_box(&data))));
g.finish();

Ok(())
}

Expand Down
112 changes: 112 additions & 0 deletions rs/src/day25.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use std::collections::{HashMap, HashSet, VecDeque};
use std::fmt::Debug;
use std::hash::Hash;

fn cut<T: Copy + Debug + Eq + Hash + Ord>(gr: &HashMap<T, HashSet<T>>, n: usize) -> Option<usize> {
if n == 0 {
let mut components = vec![];
let mut keys = gr.keys().copied().collect::<HashSet<_>>();
while let Some(start) = keys.iter().next().copied() {
keys.remove(&start);
let mut stack = vec![start];
let mut n = 0;
while let Some(node) = stack.pop() {
n += 1;
let Some(next) = gr.get(&node) else {
continue;
};
stack.extend(next.iter().copied().filter(|node| keys.remove(node)));
}
components.push(n);
}
#[cfg(debug_assertions)]
eprintln!("{:?}", &components);
return if components.len() > 1 {
Some(components.into_iter().product())
} else {
None
};
}

let mut weights = HashMap::<_, usize>::new();
for start in gr.keys().copied() {
let mut queue = VecDeque::from([(start, Vec::new())]);
let mut visited = HashSet::from([start]);
while let Some((node, mut path)) = queue.pop_front() {
path.iter().rev().fold(node, |node1, node2| {
*weights
.entry((node1.min(*node2), node1.max(*node2)))
.or_default() += 1;
*node2
});
path.push(node);
let Some(next) = gr.get(&node) else {
continue;
};
queue.extend(
next.iter()
.copied()
.filter(|node| visited.insert(*node))
.map(|node| (node, path.clone())),
);
}
}
let mut weights = weights.into_iter().collect::<Vec<_>>();
weights.sort_by_key(|(_, n)| *n);

for ((a, b), _) in weights.into_iter().rev() {
#[cfg(debug_assertions)]
eprintln!("({:?}, {:?})", &a, &b);
let mut gr = gr.clone();
let r1 = gr.get_mut(&a).is_some_and(|next| next.remove(&b));
let r2 = gr.get_mut(&b).is_some_and(|next| next.remove(&a));
debug_assert!(r1 & r2);
if let Some(r) = cut(&gr, n - 1) {
return Some(r);
}
}

None
}

pub fn part1(data: &str) -> Option<usize> {
let mut gr = HashMap::new();
for line in data.lines() {
let Some((src, rhs)) = line.split_once(':') else {
continue;
};
for dst in rhs.split_whitespace() {
gr.entry(src).or_insert_with(HashSet::new).insert(dst);
gr.entry(dst).or_insert_with(HashSet::new).insert(src);
}
}
cut(&gr, 3)
}

#[cfg(test)]
mod tests {
use super::*;
use indoc::indoc;
use pretty_assertions::assert_eq;

static EXAMPLE: &str = indoc! {"
jqt: rhn xhk nvd
rsh: frs pzl lsr
xhk: hfx
cmg: qnr nvd lhk bvb
rhn: xhk bvb hfx
bvb: xhk hfx
pzl: lsr hfx nvd
qnr: nvd
ntq: jqt hfx bvb xhk
nvd: lhk
lsr: lhk
rzs: qnr cmg lsr rsh
frs: qnr lhk lsr
"};

#[test]
fn part1_examples() {
assert_eq!(Some(54), part1(EXAMPLE));
}
}
1 change: 1 addition & 0 deletions rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub mod day21;
pub mod day22;
pub mod day23;
pub mod day24;
pub mod day25;
pub mod day3;
pub mod day4;
pub mod day5;
Expand Down
9 changes: 8 additions & 1 deletion rs/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use aoc2023::{
day1, day10, day11, day12, day13, day14, day15, day16, day17, day18, day19, day2, day20, day21,
day22, day23, day24, day3, day4, day5, day6, day7, day8, day9,
day22, day23, day24, day25, day3, day4, day5, day6, day7, day8, day9,
};
use std::collections::HashSet;
use std::env;
Expand Down Expand Up @@ -210,5 +210,12 @@ fn main() -> io::Result<()> {
println!();
}

if args.is_empty() || args.contains("25") {
println!("Day 25");
let data = get_day_input(25)?;
println!("{:?}", day25::part1(&data).expect("error"));
println!();
}

Ok(())
}

0 comments on commit e3b945e

Please sign in to comment.