From c4163824ccbd96bc97965ed256b77e8b3c3b6281 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Tue, 19 Feb 2019 15:28:32 +0100 Subject: [PATCH] Implement 2016 day 22 part 2. Only 2 years and 2 months later. --- 2016/src/day22.rs | 135 ++++++++++++++++++++++++++++++++++++++-- 2016/src/samples/22.txt | 10 +++ 2 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 2016/src/samples/22.txt diff --git a/2016/src/day22.rs b/2016/src/day22.rs index 02308fe..763b87d 100644 --- a/2016/src/day22.rs +++ b/2016/src/day22.rs @@ -1,13 +1,18 @@ +use std::cmp::Reverse; +use std::collections::BinaryHeap; +use std::collections::HashSet; +use std::io::BufRead; +use std::io::BufReader; use std::io::Read; -use common::Solution; -use std::io::BufReader; +use itertools::Itertools; use regex::Regex; -use std::io::BufRead; + +use common::Solution; type Coordinate = (usize, usize); -#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)] +#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Default)] struct Node { pos: Coordinate, capacity: usize, @@ -20,6 +25,30 @@ impl Node { } } +fn get_grid(list: &[T], size: usize, (x, y): Coordinate) -> &T { + &list[x + size * y] +} + +fn get_mut_grid(list: &mut [T], size: usize, (x, y): Coordinate) -> &mut T { + &mut list[x + size * y] +} + +#[derive(Ord, PartialOrd, Eq, PartialEq, Clone)] +struct State { + pos: Coordinate, + empty_pos: Coordinate, + grid: Vec, +} + +impl State { + fn estimate(&self) -> usize { + self.pos.0 + self.pos.1 + + self.pos.0.max(self.empty_pos.0) + self.pos.1.max(self.empty_pos.1) + - self.pos.0.min(self.empty_pos.0) - self.pos.1.min(self.empty_pos.1) + - 1 + } +} + #[derive(Default)] pub struct Day22 { nodes: Vec, @@ -49,8 +78,25 @@ impl Day22 { self.nodes.push(node) } + self.nodes.sort_unstable(); + assert_ne!(0, self.nodes.len()) } + + fn build_grid(&self) -> (usize, Vec, Vec) { + let size = self.nodes.iter().map(|node| node.pos.0).max().unwrap_or(0) + 1; + + let mut used = vec![0usize; self.nodes.len()]; + let mut capacity = used.clone(); + + for node in &self.nodes { + let pos = node.pos; + *get_mut_grid(&mut used, size, pos) = node.used; + *get_mut_grid(&mut capacity, size, pos) = node.capacity; + } + + (size, used, capacity) + } } impl Solution for Day22 { @@ -66,4 +112,83 @@ impl Solution for Day22 { fitting.to_string() } -} \ No newline at end of file + + fn part2(&mut self, input: &mut Read) -> String { + self.read_input(input); + + let (size, used, capacity) = self.build_grid(); + + let (empty_pos, _) = used.iter() + .find_position(|&&used| used == 0) + .unwrap(); + + let height = used.len() / size; + + let state = State { + pos: (size - 1, 0), + empty_pos: (empty_pos % size, empty_pos / size), + grid: used, + }; + + + let mut visited = HashSet::new(); + visited.insert((state.pos, state.empty_pos)); + + let mut todo = BinaryHeap::new(); + todo.push(Reverse((state.estimate(), 0usize, state))); + + while let Some(Reverse((_, dist, state))) = todo.pop() { + if state.pos == (0, 0) { + return dist.to_string(); + } + let (xe, ye) = state.empty_pos; + let empty_capacity = *get_grid(&capacity, size, state.empty_pos); + + let valid_x = [if xe > 0 { Some(xe - 1) } else { None }, Some(xe), Some(xe), if xe < size - 1 { Some(xe + 1) } else { None }]; + let valid_y = [Some(ye), if ye > 0 { Some(ye - 1) } else { None }, if ye < height - 1 { Some(ye + 1) } else { None }, Some(ye)]; + for (x, y) in valid_x.iter().zip(valid_y.iter()) { + if x.is_none() || y.is_none() { + continue + } + let switch = (x.unwrap(), y.unwrap()); + let contents = *get_grid(&state.grid, size, switch); + + if contents > empty_capacity { + // Not enough capacity + continue; + } + + let mut new_state = State { + grid: state.grid.clone(), + empty_pos: switch, + pos: if switch == state.pos { state.empty_pos } else { state.pos }, + }; + + if !visited.contains(&(new_state.pos, new_state.empty_pos)) { + visited.insert((new_state.pos, new_state.empty_pos)); + *get_mut_grid(&mut new_state.grid, size, switch) = 0; + *get_mut_grid(&mut new_state.grid, size, state.empty_pos) = contents; + + todo.push(Reverse((dist + 1 + new_state.estimate(), dist + 1, new_state))); + } + } + } + + unreachable!("Did not arrive at an end state") + } +} + +#[cfg(test)] +mod tests { + use common::Solution; + use day22::Day22; + + const SAMPLE_INPUT: &[u8] = include_bytes!("samples/22.txt"); + + #[test] + fn sample_part2() { + let mut instance = Day22::new(); + let result = instance.part2(&mut SAMPLE_INPUT); + assert_eq!("7", result); + } +} diff --git a/2016/src/samples/22.txt b/2016/src/samples/22.txt new file mode 100644 index 0000000..5cdf80f --- /dev/null +++ b/2016/src/samples/22.txt @@ -0,0 +1,10 @@ +Filesystem Size Used Avail Use% +/dev/grid/node-x0-y0 10T 8T 2T 80% +/dev/grid/node-x0-y1 11T 6T 5T 54% +/dev/grid/node-x0-y2 32T 28T 4T 87% +/dev/grid/node-x1-y0 9T 7T 2T 77% +/dev/grid/node-x1-y1 8T 0T 8T 0% +/dev/grid/node-x1-y2 11T 7T 4T 63% +/dev/grid/node-x2-y0 10T 6T 4T 60% +/dev/grid/node-x2-y1 9T 8T 1T 88% +/dev/grid/node-x2-y2 9T 6T 3T 66%