Skip to content

Commit

Permalink
Day 23: A Long Walk (part 2)
Browse files Browse the repository at this point in the history
  • Loading branch information
ephemient committed Dec 25, 2023
1 parent 8d2b1e1 commit 97852c4
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 23 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ Development occurs in language-specific directories:
|[Day20.hs](hs/src/Day20.hs)|[Day20.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day20.kt)|[day20.py](py/aoc2023/day20.py)|[day20.rs](rs/src/day20.rs)|
|[Day21.hs](hs/src/Day21.hs)|[Day21.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day21.kt)|[day21.py](py/aoc2023/day21.py)|[day21.rs](rs/src/day21.rs)|
|[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) ½|
||[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) ½|
1 change: 1 addition & 0 deletions rs/benches/criterion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ fn aoc2023_bench(c: &mut Criterion) -> io::Result<()> {
let data = get_day_input(23)?;
let mut g = c.benchmark_group("day 23");
g.bench_function("part 1", |b| b.iter(|| day23::part1(black_box(&data))));
g.bench_function("part 2", |b| b.iter(|| day23::part2(black_box(&data))));
g.finish();

let data = get_day_input(24)?;
Expand Down
119 changes: 97 additions & 22 deletions rs/src/day23.rs
Original file line number Diff line number Diff line change
@@ -1,48 +1,123 @@
use std::collections::btree_map::Entry;
use std::collections::{BTreeMap, BTreeSet};

pub fn part1(data: &str) -> Option<usize> {
let grid = data.lines().map(|line| line.as_bytes()).collect::<Vec<_>>();
let graph = grid
let mut graph = grid
.iter()
.enumerate()
.flat_map(|(y, row)| {
let grid = &grid;
row.iter().enumerate().filter_map(move |(x, c)| {
if [b'.', b'<', b'>', b'^', b'v'].contains(c) {
let mut edges = BTreeMap::new();
if b".<".contains(c) && x > 0 && b".<".contains(&row[x - 1]) {
edges.insert((y, x - 1), 1);
let edges = [
(b".<", b".>", Some(y), x.checked_sub(1)),
(b".>", b".<", Some(y), x.checked_add(1)),
(b".^", b".v", y.checked_sub(1), Some(x)),
(b".v", b".^", y.checked_add(1), Some(x)),
]
.into_iter()
.filter_map(|(f, b, y2, x2)| {
let (y2, x2) = (y2?, x2?);
let d = grid.get(y2)?.get(x2)?;
let f = f.contains(c) && f.contains(d);
let b = b.contains(c) && b.contains(d);
if f || b {
Some(((y2, x2), (f, b, 1)))
} else {
None
}
if b".>".contains(c) && x + 1 < row.len() && b".>".contains(&row[x + 1]) {
edges.insert((y, x + 1), 1);
}
if b".^".contains(c) && y > 0 {
let row = grid[y - 1];
if x < row.len() && b".^".contains(&row[x]) {
edges.insert((y - 1, x), 1);
}
}
if b".v".contains(c) && y + 1 < grid.len() {
let row = grid[y + 1];
if x < row.len() && b".v".contains(&row[x]) {
edges.insert((y + 1, x), 1);
}
}
Some(((y, x), edges))
} else {
})
.collect::<BTreeMap<_, _>>();
if edges.is_empty() {
None
} else {
Some(((y, x), edges))
}
})
})
.collect::<BTreeMap<_, _>>();
let start = *graph.keys().next()?;
let end = *graph.keys().next_back()?;
loop {
let mut any_removed = false;
for key in graph
.keys()
.copied()
.filter(|key| key != &start && key != &end && grid[key.0][key.1] == b'.')
.collect::<Vec<_>>()
{
let Entry::Occupied(mut entry) = graph.entry(key) else {
panic!("expected Entry::Occupied");
};
let edges = entry.get_mut();
if edges.len() == 1 {
let &key1 = edges.keys().next().unwrap();
entry.remove();
graph.get_mut(&key1).unwrap().remove(&key);
any_removed = true;
} else if edges.len() == 2 {
let mut iter = edges.iter();
let (&key1, &(f1, b1, w1)) = iter.next().unwrap();
let (&key2, &(f2, b2, w2)) = iter.next().unwrap();
entry.remove();
let edges = graph.get_mut(&key1).unwrap();
if let Some((f, b, w)) = edges.remove(&key) {
edges.insert(key2, (f && f2, b && b2, w + w2));
}
let edges = graph.get_mut(&key2).unwrap();
if let Some((f, b, w)) = edges.remove(&key) {
edges.insert(key1, (f && f1, b && b1, w + w1));
}
any_removed = true;
}
}
if !any_removed {
break;
}
}
let graph = graph
.into_iter()
.filter_map(|(key, edges)| {
let edges = edges
.into_iter()
.filter_map(|(key, (f, _, w))| if f { Some((key, w)) } else { None })
.collect::<BTreeMap<_, _>>();
if edges.is_empty() {
None
} else {
Some((key, edges))
}
})
.collect::<BTreeMap<_, _>>();

let mut stack = vec![(start, BTreeSet::from([start]), 0)];
let mut best = None::<usize>;
while let Some((node, used, distance)) = stack.pop() {
if node == end {
best = Some(best.map_or(distance, |best| best.max(distance)));
continue;
}
let mut potential = distance;
let mut used2 = used.clone();
let mut stack2 = vec![node];
while let Some(next) = stack2.pop() {
let Some(edges) = graph.get(&next) else {
continue;
};
let mut best_weight = 0;
for (next, weight) in edges {
if used.contains(next) {
continue;
}
best_weight = best_weight.max(*weight);
if used2.insert(*next) {
stack2.push(*next);
}
}
potential += best_weight;
}
if best.is_some_and(|best| best >= potential) || !used2.contains(&end) {
continue;
}
let Some(edges) = graph.get(&node) else {
continue;
Expand Down
1 change: 1 addition & 0 deletions rs/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ fn main() -> io::Result<()> {
println!("Day 23");
let data = get_day_input(23)?;
println!("{:?}", day23::part1(&data).expect("error"));
println!("{:?}", day23::part2(&data).expect("error"));
println!();
}

Expand Down

0 comments on commit 97852c4

Please sign in to comment.