day18: optimize
* Implement generic path tracing, so part 1 doesn't store and return the full path, just the count. * Improve part2 impl to only run path search if the block fell on our last path. * Start part2 path search from the best path position immediately before the placed block.
This commit is contained in:
		
							
								
								
									
										92
									
								
								src/day18.rs
									
									
									
									
									
								
							
							
						
						
									
										92
									
								
								src/day18.rs
									
									
									
									
									
								
							@@ -1,7 +1,7 @@
 | 
				
			|||||||
use std::{cmp::Reverse, collections::BinaryHeap};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use aoc_runner_derive::aoc;
 | 
					use aoc_runner_derive::aoc;
 | 
				
			||||||
use grid::Grid;
 | 
					use grid::Grid;
 | 
				
			||||||
 | 
					use itertools::Itertools;
 | 
				
			||||||
 | 
					use std::{cmp::Reverse, collections::BinaryHeap};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone)]
 | 
					#[derive(Clone)]
 | 
				
			||||||
struct MemoryMap {
 | 
					struct MemoryMap {
 | 
				
			||||||
@@ -9,6 +9,34 @@ struct MemoryMap {
 | 
				
			|||||||
    byte_stream: Vec<(i64, i64)>,
 | 
					    byte_stream: Vec<(i64, i64)>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					trait PathTrack {
 | 
				
			||||||
 | 
					    fn new() -> Self;
 | 
				
			||||||
 | 
					    fn push(&mut self, pos: (i64, i64));
 | 
				
			||||||
 | 
					    fn finalize(&mut self) {}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct LengthPath(usize);
 | 
				
			||||||
 | 
					impl PathTrack for LengthPath {
 | 
				
			||||||
 | 
					    fn new() -> Self {
 | 
				
			||||||
 | 
					        LengthPath(0)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn push(&mut self, _: (i64, i64)) {
 | 
				
			||||||
 | 
					        self.0 += 1
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl PathTrack for Vec<(i64, i64)> {
 | 
				
			||||||
 | 
					    fn new() -> Self {
 | 
				
			||||||
 | 
					        Vec::new()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn push(&mut self, pos: (i64, i64)) {
 | 
				
			||||||
 | 
					        self.push(pos);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn finalize(&mut self) {
 | 
				
			||||||
 | 
					        self.reverse();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl MemoryMap {
 | 
					impl MemoryMap {
 | 
				
			||||||
    fn from_str(input: &str, width: usize, height: usize) -> Self {
 | 
					    fn from_str(input: &str, width: usize, height: usize) -> Self {
 | 
				
			||||||
        let map = Grid::with_shape(width, height, true);
 | 
					        let map = Grid::with_shape(width, height, true);
 | 
				
			||||||
@@ -36,45 +64,46 @@ impl MemoryMap {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn valid_moves(&self, pos: &(i64, i64)) -> Vec<(i64, i64)> {
 | 
					    fn valid_moves<'a>(&'a self, pos: &'a (i64, i64)) -> impl Iterator<Item = (i64, i64)> + 'a {
 | 
				
			||||||
        [(0, 1), (1, 0), (0, -1), (-1, 0)]
 | 
					        ([(0, 1), (1, 0), (0, -1), (-1, 0)])
 | 
				
			||||||
            .iter()
 | 
					            .iter()
 | 
				
			||||||
            .filter(|ofs| self.map.get(&(pos.0 + ofs.0, pos.1 + ofs.1)).is_some_and(|v| *v))
 | 
					            .filter(|ofs| self.map.get(&(pos.0 + ofs.0, pos.1 + ofs.1)).is_some_and(|v| *v))
 | 
				
			||||||
            .map(|ofs| (pos.0 + ofs.0, pos.1 + ofs.1))
 | 
					            .map(|ofs| (pos.0 + ofs.0, pos.1 + ofs.1))
 | 
				
			||||||
            .collect()
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn dijkstra(&self) -> Option<Vec<(i64, i64)>> {
 | 
					    fn dijkstra<T: PathTrack>(&self, start: (i64, i64)) -> Option<T> {
 | 
				
			||||||
        let start = (0i64, 0i64);
 | 
					 | 
				
			||||||
        let goal = (self.map.width() as i64 - 1, self.map.height() as i64 - 1);
 | 
					        let goal = (self.map.width() as i64 - 1, self.map.height() as i64 - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut distances = self.map.same_shape(i64::MAX);
 | 
					        let mut costs = self.map.same_shape(i64::MAX);
 | 
				
			||||||
        let mut prev = self.map.same_shape((i64::MAX, i64::MAX));
 | 
					        let mut prev = self.map.same_shape((i64::MAX, i64::MAX));
 | 
				
			||||||
        let mut queue = BinaryHeap::new();
 | 
					        let mut queue = BinaryHeap::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        distances.set(&start, 0);
 | 
					        costs.set(&start, 0);
 | 
				
			||||||
        queue.push((Reverse(0), start));
 | 
					        queue.push((Reverse(0), start));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        while let Some((cost, pos)) = queue.pop() {
 | 
					        while let Some((cost, pos)) = queue.pop() {
 | 
				
			||||||
            if pos == goal {
 | 
					            if pos == goal {
 | 
				
			||||||
                let mut visited = Vec::new();
 | 
					 | 
				
			||||||
                let mut visited_pos = goal;
 | 
					                let mut visited_pos = goal;
 | 
				
			||||||
 | 
					                let mut path = T::new();
 | 
				
			||||||
                visited.push(pos);
 | 
					                path.push(pos);
 | 
				
			||||||
                while let Some(next) = prev.get(&visited_pos) {
 | 
					                while let Some(next) = prev.get(&visited_pos) {
 | 
				
			||||||
                    visited_pos = *next;
 | 
					                    visited_pos = *next;
 | 
				
			||||||
                    visited.push(*next);
 | 
					                    path.push(*next);
 | 
				
			||||||
 | 
					                    if *next == start {
 | 
				
			||||||
 | 
					                        path.finalize();
 | 
				
			||||||
 | 
					                        return Some(path);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                return Some(visited);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if distances.get(&pos).is_some_and(|v| cost.0 > *v) {
 | 
					            if costs.get(&pos).is_some_and(|v| cost.0 > *v) {
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for new_pos in self.valid_moves(&pos) {
 | 
					            let moves = self.valid_moves(&pos);
 | 
				
			||||||
                if distances.get(&new_pos).is_none_or(|best_cost| cost.0 + 1 < *best_cost) {
 | 
					            for new_pos in moves {
 | 
				
			||||||
                    distances.set(&new_pos, cost.0 + 1);
 | 
					                if costs.get(&new_pos).is_none_or(|best_cost| cost.0 + 1 < *best_cost) {
 | 
				
			||||||
 | 
					                    costs.set(&new_pos, cost.0 + 1);
 | 
				
			||||||
                    prev.set(&new_pos, pos);
 | 
					                    prev.set(&new_pos, pos);
 | 
				
			||||||
                    queue.push((Reverse(cost.0 + 1), new_pos));
 | 
					                    queue.push((Reverse(cost.0 + 1), new_pos));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -87,34 +116,27 @@ impl MemoryMap {
 | 
				
			|||||||
pub fn part1_impl(input: &str, width: usize, height: usize, n: usize) -> usize {
 | 
					pub fn part1_impl(input: &str, width: usize, height: usize, n: usize) -> usize {
 | 
				
			||||||
    let mut map = MemoryMap::from_str(input, width, height);
 | 
					    let mut map = MemoryMap::from_str(input, width, height);
 | 
				
			||||||
    map.place_bytes(n);
 | 
					    map.place_bytes(n);
 | 
				
			||||||
    let path = map.dijkstra().expect("no path found");
 | 
					    let path = map.dijkstra::<LengthPath>((0, 0)).expect("no path found");
 | 
				
			||||||
    let mut sol_map = map.map.same_shape(b'.');
 | 
					 | 
				
			||||||
    sol_map.data = map
 | 
					 | 
				
			||||||
        .map
 | 
					 | 
				
			||||||
        .data
 | 
					 | 
				
			||||||
        .iter()
 | 
					 | 
				
			||||||
        .map(|clear| if *clear { b'.' } else { b'#' })
 | 
					 | 
				
			||||||
        .collect();
 | 
					 | 
				
			||||||
    for visited in &path {
 | 
					 | 
				
			||||||
        sol_map.set(visited, b'O');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    path.len() - 2 // count vertexes, not visited nodes
 | 
					    path.0 - 1 // count edges, not visited nodes (start doesn't count)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn part2_impl(input: &str, width: usize, height: usize, n: usize) -> (i64, i64) {
 | 
					pub fn part2_impl(input: &str, width: usize, height: usize, n: usize) -> (i64, i64) {
 | 
				
			||||||
    let mut input_map = MemoryMap::from_str(input, width, height);
 | 
					    let mut input_map = MemoryMap::from_str(input, width, height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    input_map.place_bytes(n);
 | 
					    input_map.place_bytes(n);
 | 
				
			||||||
    let mut path = input_map.dijkstra().expect("no path found");
 | 
					    let mut path = input_map.dijkstra::<Vec<(i64, i64)>>((0, 0)).expect("no path found");
 | 
				
			||||||
 | 
					    println!("{:?}", path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for byte in n..input_map.byte_stream.len() {
 | 
					    for byte in n..input_map.byte_stream.len() {
 | 
				
			||||||
        input_map.place_byte(byte);
 | 
					        input_map.place_byte(byte);
 | 
				
			||||||
        if path.contains(&input_map.byte_stream[byte]) {
 | 
					
 | 
				
			||||||
            if let Some(new_path) = input_map.dijkstra() {
 | 
					        if let Some((obs_at, _)) = path.iter().find_position(|v| *v == &input_map.byte_stream[byte]) {
 | 
				
			||||||
                path = new_path;
 | 
					            let (before, _) = path.split_at(obs_at);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if let Some(new_path) = input_map.dijkstra::<Vec<(i64, i64)>>(path[obs_at - 1]) {
 | 
				
			||||||
 | 
					                path = [before, &new_path].concat();
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                println!("obstruction found on trial {}", byte);
 | 
					 | 
				
			||||||
                return input_map.byte_stream[byte];
 | 
					                return input_map.byte_stream[byte];
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user