diff --git a/2018/src/day22.rs b/2018/src/day22.rs index f2fa66d..9aae16e 100644 --- a/2018/src/day22.rs +++ b/2018/src/day22.rs @@ -1,7 +1,34 @@ +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; +type Coordinate = (usize, usize); + +#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Copy, Clone, Debug)] +struct State { + pos: Coordinate, + climbing: bool, + torch: bool, +} + +impl State { + pub fn is_valid(&self, terrain: usize) -> bool { + match terrain { + 0 => self.torch || self.climbing, + 1 => !self.torch, + 2 => !self.climbing, + _ => panic!(), + } + } +} + +const MOD_BASE: usize = 20183; + #[derive(Default)] pub struct Day22 {} @@ -11,15 +38,142 @@ impl Day22 { } } -impl Solution for Day22 { - fn part1(&mut self, _input: &mut Read) -> String { - unimplemented!() +fn compute_table((x, y): Coordinate, depth: usize) -> Vec> { + let mut table = vec![vec![0usize; x + 1]; y + 1]; + table[0][0] = 0; + for x in 1..=x { + table[0][x] = (16807 * x + depth) % MOD_BASE; } - fn part2(&mut self, _input: &mut Read) -> String { - unimplemented!() + for y in 1..=y { + table[y][0] = (48271 * y + depth) % MOD_BASE; + } + + for y in 1..=y { + for x in 1..=x { + table[y][x] = (table[y - 1][x] * table[y][x - 1] + depth) % MOD_BASE; + } + } + + for c in table.iter_mut().flat_map(|x| x.iter_mut()) { + *c %= 3; + } + + table +} + +fn read_input(input: &mut Read) -> (usize, Coordinate) { + let mut buf = String::new(); + let mut reader = BufReader::new(input); + reader.read_line(&mut buf).unwrap(); + + let depth: usize; + { + let mut parts = buf.trim().split(' '); + depth = parts.nth(1).unwrap().parse().unwrap(); + } + buf.clear(); + reader.read_line(&mut buf).unwrap(); + + let target: Coordinate; + { + let mut parts = buf.trim().split(|c| c == ',' || c == ' '); + let x: usize = parts.nth(1).unwrap().parse().unwrap(); + let y: usize = parts.next().unwrap().parse().unwrap(); + target = (x, y); + } + + (depth, target) +} + +impl Solution for Day22 { + fn part1(&mut self, input: &mut Read) -> String { + let (depth, target) = read_input(input); + let mut table = compute_table(target, depth); + table[target.1][target.0] = 0; + + let result: usize = table.iter().flat_map(|x| x.iter()) + .sum(); + format!("{}", result) + } + + fn part2(&mut self, input: &mut Read) -> String { + let (depth, target) = read_input(input); + let mut table = compute_table((1000, 1000), depth); + table[target.1][target.0] = 0; + + let mut todo = BinaryHeap::new(); + let mut visited: HashSet = HashSet::new(); + todo.push((Reverse(0), State { pos: (0, 0), climbing: false, torch: true })); + + let target_state = State { pos: target, climbing: false, torch: true }; + + while let Some((Reverse(dist), state)) = todo.pop() { + if visited.contains(&state) { + continue; + } + visited.insert(state); + if state == target_state { + return format!("{}", dist); + } + + let (x, y) = state.pos; + + // Handle equipment changes + let changes = [ + State { pos: state.pos, climbing: state.climbing, torch: !state.torch }, + State { pos: state.pos, climbing: !state.climbing, torch: state.torch }, + State { pos: state.pos, climbing: !state.climbing, torch: !state.torch }, + ]; + + for state in changes.iter().cloned() { + if visited.contains(&state) || !state.is_valid(table[y][x]) { + continue; + } + + todo.push((Reverse(dist + 7), state)); + } + + let xmin = if x == 0 { 0 } else { x - 1 }; + let ymin = if y == 0 { 0 } else { y - 1 }; + + for xn in xmin..=(x + 1) { + for yn in ymin..=(y + 1) { + let new_state = State { + pos: (xn, yn), + torch: state.torch, + climbing: state.climbing, + }; + + if !visited.contains(&new_state) && new_state.is_valid(table[yn][xn]) && (x == xn || y == yn) { + todo.push((Reverse(dist + 1), new_state)); + } + } + } + //println!("{} {:?} {:?}", dist, &state, &todo); + } + unreachable!(); } } #[cfg(test)] -mod tests {} +mod tests { + use common::Solution; + + use super::*; + + const SAMPLE_INPUT: &[u8] = include_bytes!("samples/22.txt"); + + + #[test] + fn sample_part1() { + let mut instance = Day22::new(); + assert_eq!("114", instance.part1(&mut SAMPLE_INPUT)); + } + + #[test] + fn sample_part2() { + let mut instance = Day22::new(); + assert_eq!("45", instance.part2(&mut SAMPLE_INPUT)); + } +} diff --git a/2018/src/samples/22.txt b/2018/src/samples/22.txt new file mode 100644 index 0000000..eea8c66 --- /dev/null +++ b/2018/src/samples/22.txt @@ -0,0 +1,2 @@ +depth: 510 +target: 10,10