mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-25 21:00:31 +01:00
Implement 2016 day 22 part 2.
Only 2 years and 2 months later.
This commit is contained in:
@@ -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<T>(list: &[T], size: usize, (x, y): Coordinate) -> &T {
|
||||
&list[x + size * y]
|
||||
}
|
||||
|
||||
fn get_mut_grid<T>(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<usize>,
|
||||
}
|
||||
|
||||
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<Node>,
|
||||
@@ -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<usize>, Vec<usize>) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user