This commit is contained in:
Quentin Boyer 2025-01-04 20:07:25 +01:00
parent ca60c2b164
commit cd4268a9ce

211
src/bin/day18.rs Normal file
View file

@ -0,0 +1,211 @@
use std::{
collections::{BinaryHeap, HashMap, HashSet},
time::Instant,
};
use aoc_2024::{load, print_res};
use bstr::BString;
use color_eyre::eyre::OptionExt;
type Parsed = Vec<(usize, usize)>;
#[inline(never)]
pub fn parsing(input: &BString) -> color_eyre::Result<Parsed> {
std::str::from_utf8(input)?
.lines()
.map(|l| {
let (x, y) = l.split_once(',').ok_or_eyre("Missing ,")?;
Ok((x.parse()?, y.parse()?))
})
.collect()
}
fn a_star(grid: &[[bool; GRID_SIZE]; GRID_SIZE]) -> Option<usize> {
#[derive(Clone, PartialEq, Eq)]
struct Path {
x: usize,
y: usize,
score: usize,
}
impl Path {
fn heuristic(&self) -> usize {
self.score + (GRID_SIZE - 1 - self.x) + (GRID_SIZE - 1 - self.y)
}
}
impl Ord for Path {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
other.heuristic().cmp(&self.heuristic())
}
}
impl PartialOrd for Path {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
let mut prev = HashMap::new();
let mut visited = HashMap::new();
visited.insert((0, 0), 0);
let mut current = BinaryHeap::new();
current.push(Path {
x: 0,
y: 0,
score: 0,
});
while let Some(p) = current.pop() {
if p.x == GRID_SIZE - 1 && p.y == GRID_SIZE - 1 {
return Some(p.score);
}
let mut try_insert = |n: Path| {
if !grid[n.x][n.y] {
match visited.get(&(n.x, n.y)) {
Some(&s) if s >= n.score => (),
_ => {
visited.insert((n.x, n.y), n.score);
prev.insert((n.x, n.y), (p.x, p.y));
current.push(n);
}
}
}
};
let x = p.x;
let y = p.y;
let score = p.score + 1;
if x > 0 {
let new_path = Path { x: x - 1, y, score };
try_insert(new_path);
}
if x < GRID_SIZE - 1 {
let new_path = Path { x: x + 1, y, score };
try_insert(new_path);
}
if y > 0 {
let new_path = Path { x, y: y - 1, score };
try_insert(new_path);
}
if y < GRID_SIZE - 1 {
let new_path = Path { x, y: y + 1, score };
try_insert(new_path);
}
}
None
}
const GRID_SIZE: usize = 70 + 1;
const CUTTOF: usize = 1024;
#[inline(never)]
pub fn part1(input: Parsed) {
let mut grid = [[false; GRID_SIZE]; GRID_SIZE];
for &(x, y) in &input[0..CUTTOF] {
grid[x][y] = true;
}
let result = a_star(&grid).unwrap();
print_res!("Steps to take: {result}");
}
fn bfs(grid: &[[bool; GRID_SIZE]; GRID_SIZE]) -> Option<HashSet<(usize, usize)>> {
let mut seen = HashMap::new();
seen.insert((0, 0), (0, 0));
let mut paths = Vec::new();
paths.push((0, 0));
while let Some((x, y)) = paths.pop() {
if x == GRID_SIZE - 1 && y == GRID_SIZE - 1 {
let mut paths = HashSet::new();
let mut current = (x, y);
while current != (0, 0) {
paths.insert(current);
current = seen[&current];
}
return Some(paths);
}
let mut try_insert = |nx: usize, ny: usize| {
if !grid[nx][ny] && !seen.contains_key(&(nx, ny)) {
seen.insert((nx, ny), (x, y));
paths.push((nx, ny));
}
};
if x > 0 {
try_insert(x - 1, y);
}
if x < GRID_SIZE - 1 {
try_insert(x + 1, y);
}
if y > 0 {
try_insert(x, y - 1);
}
if y < GRID_SIZE - 1 {
try_insert(x, y + 1);
}
}
None
}
#[inline(never)]
pub fn part2(input: Parsed) {
let mut grid = [[false; GRID_SIZE]; GRID_SIZE];
let mut seen = bfs(&grid).unwrap();
for (x, y) in input {
grid[x][y] = true;
if seen.contains(&(x, y)) {
match bfs(&grid) {
Some(p) => seen = p,
None => {
print_res!("Breaking block: {x},{y}");
return;
}
}
}
}
panic!("Did not find a breaking block");
}
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(())
}