diff --git a/day15/src/main.rs b/day15/src/main.rs index e5d635f..f788002 100644 --- a/day15/src/main.rs +++ b/day15/src/main.rs @@ -2,11 +2,13 @@ use std::{collections::VecDeque, fmt::Display}; use glam::IVec2; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Copy)] enum Tile { Wall, Robot, Box, + BoxL, + BoxR, Empty, } use Tile::*; @@ -17,6 +19,8 @@ impl Display for Tile { Wall => write!(f, "#"), Robot => write!(f, "@"), Box => write!(f, "O"), + BoxL => write!(f, "["), + BoxR => write!(f, "]"), Empty => write!(f, "."), } } @@ -62,6 +66,27 @@ impl Display for State { } } +impl State { + fn widen(&mut self) { + self.map = self + .map + .iter() + .map(|l| { + l.iter() + .flat_map(|t| match t { + Empty => [Empty, Empty], + Wall => [Wall, Wall], + Box => [BoxL, BoxR], + Robot => [Robot, Empty], + e => [*e, *e], + }) + .collect() + }) + .collect(); + self.robot_pos *= IVec2::new(1, 2); + } +} + fn parse(input: &str) -> State { let (map, directions) = input.split_once("\n\n").unwrap(); let mut robot_pos = IVec2::new(0, 0); @@ -105,6 +130,31 @@ fn get_tile<'a>(map: &'a mut [Vec], idx: &IVec2) -> &'a mut Tile { &mut map[idx.x as usize][idx.y as usize] } +fn can_be_moved(state: &State, pos: &IVec2, direction: &IVec2, to_move: &mut Vec) -> bool { + let tile_in_front = state.map[(pos + direction).x as usize][(pos + direction).y as usize]; + if tile_in_front == Wall { + return false; + } + if tile_in_front == Empty { + return true; + } + + let to_check_dir = match tile_in_front { + BoxL => Right.as_vec(), + BoxR => Left.as_vec(), + _ => IVec2::new(0, 0), + }; + + if direction == &Up.as_vec() || direction == &Down.as_vec() { + to_move.push(pos + direction); + to_move.push(pos + direction + to_check_dir); + return can_be_moved(state, &(pos + direction), direction, to_move) + && can_be_moved(state, &(pos + direction + to_check_dir), direction, to_move); + } + to_move.push(pos + direction); + can_be_moved(state, &(pos + direction), direction, to_move) +} + fn do_move(state: &mut State) { let dir = state.directions.pop_front().unwrap().as_vec(); let pos_in_front = state.robot_pos + dir; @@ -120,42 +170,50 @@ fn do_move(state: &mut State) { return; } - let mut curr = pos_in_front; - while get_tile(&mut state.map, &curr) != &Wall && get_tile(&mut state.map, &curr) != &Empty { - curr += dir; + let mut to_move = Vec::new(); + if can_be_moved(state, &state.robot_pos, &dir, &mut to_move) { + let mut new_map = state.map.clone(); + for pos in &to_move { + *get_tile(&mut new_map, pos) = Empty; + } + + for pos in &to_move { + *get_tile(&mut new_map, &(pos + dir)) = *get_tile(&mut state.map, pos); + } + state.map = new_map; + *get_tile(&mut state.map, &pos_in_front) = Robot; + *get_tile(&mut state.map, &state.robot_pos) = Empty; + state.robot_pos = pos_in_front; } - if get_tile(&mut state.map, &curr) == &Wall { - return; - } - *get_tile(&mut state.map, &curr) = Box; - *get_tile(&mut state.map, &pos_in_front) = Robot; - *get_tile(&mut state.map, &state.robot_pos) = Empty; - state.robot_pos = pos_in_front; } -fn part1(state: &mut State) -> usize { - while !state.directions.is_empty() { - do_move(state); - } +fn get_gps_sum(state: &State) -> usize { state .map .iter() .enumerate() .flat_map(|(l_idx, l)| { - l.iter().enumerate().map( - move |(t_idx, t)| { - if t == &Box { - 100 * l_idx + t_idx - } else { - 0 - } - }, - ) + l.iter().enumerate().map(move |(t_idx, t)| { + if t == &Box || t == &BoxL { + 100 * l_idx + t_idx + } else { + 0 + } + }) }) .sum() } +fn part12(state: &mut State) -> usize { + while !state.directions.is_empty() { + do_move(state); + } + get_gps_sum(state) +} + fn main() { let mut input = parse(include_str!("../input.txt")); - println!("{}", part1(&mut input)); + println!("{}", part12(&mut input.clone())); + input.widen(); + println!("{}", part12(&mut input)); }