From 62c866abf953bb7524429e431d3a71dbcd451589 Mon Sep 17 00:00:00 2001 From: jazzpi Date: Thu, 22 Dec 2022 17:21:06 +0100 Subject: [PATCH] Day 22, part 2 --- src/bin/d22p1.rs | 2 +- src/bin/d22p2.rs | 40 +++++ src/day22.rs | 397 +++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 425 insertions(+), 14 deletions(-) create mode 100644 src/bin/d22p2.rs diff --git a/src/bin/d22p1.rs b/src/bin/d22p1.rs index b3a16af..7150c8c 100644 --- a/src/bin/d22p1.rs +++ b/src/bin/d22p1.rs @@ -9,7 +9,7 @@ pub fn main() { // dbg!(&grid); // dbg!(&instructions); - let mut pose = day22::Pose::new(&grid); + let mut pose = grid.initial_pose(); // dbg!(&pose); for inst in &instructions { pose = grid.exec_instruction(&pose, inst).0; diff --git a/src/bin/d22p2.rs b/src/bin/d22p2.rs new file mode 100644 index 0000000..a4ca300 --- /dev/null +++ b/src/bin/d22p2.rs @@ -0,0 +1,40 @@ +use std::collections::HashMap; + +use aoc22::{ + day22::{self, CubeSide, Navigable}, + util, +}; + +pub fn main() { + let (grid, instructions) = day22::parse_map_and_path(&util::parse_input()); + + let mut pattern = HashMap::new(); + pattern.insert(CubeSide::Top, (0, 1)); + pattern.insert(CubeSide::Fore, (1, 1)); + pattern.insert(CubeSide::Left, (2, 0)); + pattern.insert(CubeSide::Back, (3, 0)); + pattern.insert(CubeSide::Bottom, (2, 1)); + pattern.insert(CubeSide::Right, (0, 2)); + + // Pattern for example input + // WARNING: CubeGrid::wrap_around is hardcoded for the pattern above + // pattern.insert(CubeSide::Top, (0, 2)); + // pattern.insert(CubeSide::Fore, (1, 2)); + // pattern.insert(CubeSide::Left, (1, 1)); + // pattern.insert(CubeSide::Back, (1, 0)); + // pattern.insert(CubeSide::Bottom, (2, 2)); + // pattern.insert(CubeSide::Right, (2, 3)); + + let grid = day22::CubeGrid::from(&grid, &pattern); + let mut pose = grid.initial_pose(); + for inst in &instructions { + pose = grid.exec_instruction(&pose, inst).0; + } + let pos = pose.1.pos; + let row_add = pattern[&pose.0].0 * grid.side_height; + let col_add = pattern[&pose.0].1 * grid.side_width; + + let pass = + 1000 * (row_add + pos.0 + 1) + 4 * (col_add + pos.1 + 1) + (pose.1.orientation as usize); + println!("Passowrd: {}", pass); +} diff --git a/src/day22.rs b/src/day22.rs index e51f701..7514f15 100644 --- a/src/day22.rs +++ b/src/day22.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use itertools::Itertools; use crate::util::Dir; @@ -20,19 +22,7 @@ pub struct Pose { pub orientation: Dir, } -impl Pose { - pub fn new(grid: &SparseGrid) -> Pose { - let col = grid.grid[0] - .iter() - .position(|t| if let Some(occ) = t { !occ } else { false }) - .unwrap(); - Pose { - pos: (0, col), - orientation: Dir::Right, - } - } -} - +#[derive(Debug)] pub enum MoveResult { Blocked, BlockedByWrapping, @@ -63,6 +53,7 @@ pub trait Navigable { from: &Self::Pose, inst: &Self::NavigationInstruction, ) -> (Self::Pose, MoveResult); + fn initial_pose(&self) -> Self::Pose; } #[derive(Debug)] @@ -74,6 +65,40 @@ pub struct SparseGrid { pub col_bounds: Vec<(usize, usize)>, } +impl SparseGrid { + fn print(&self, pose: Option<&Pose>) { + let check_pos = |y, x| { + if let Some(pose) = pose { + pose.pos.0 == y && pose.pos.1 == x + } else { + false + } + }; + + for (y, row) in self.grid.iter().enumerate() { + for (x, col) in row.iter().enumerate() { + let c = if check_pos(y, x) { + assert!(*col == Some(false)); + match pose.unwrap().orientation { + Dir::Right => '>', + Dir::Down => 'v', + Dir::Left => '<', + Dir::Up => '^', + } + } else if col.is_none() { + ' ' + } else if *col == Some(true) { + '#' + } else { + '.' + }; + print!("{}", c); + } + print!("\n"); + } + } +} + impl Navigable for SparseGrid { type Pose = Pose; type NavigationInstruction = PassInstr; @@ -159,6 +184,352 @@ impl Navigable for SparseGrid { ), } } + + fn initial_pose(&self) -> Self::Pose { + let col = self.grid[0] + .iter() + .position(|t| if let Some(occ) = t { !occ } else { false }) + .unwrap(); + Pose { + pos: (0, col), + orientation: Dir::Right, + } + } +} + +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +pub enum CubeSide { + Top, + Bottom, + Left, + Right, + Fore, + Back, +} + +#[derive(Debug)] +pub struct CubeGrid { + // The side grids don't have to be sparse, but we don't have a dense + // implementation... + pub sides: HashMap, + + pub side_height: usize, + pub side_width: usize, +} + +impl CubeGrid { + pub fn from(sparse: &SparseGrid, pattern: &HashMap) -> CubeGrid { + let fold_width = pattern.values().map(|(_, col)| col).max().unwrap() + 1; + let fold_height = pattern.values().map(|(row, _)| row).max().unwrap() + 1; + let side_width = sparse.width / fold_width; + assert!(sparse.width % fold_width == 0); + let side_height = sparse.height / fold_height; + assert!(sparse.height % fold_height == 0); + + let mut sides = HashMap::new(); + for (side, (fold_row, fold_col)) in pattern { + let mut grid = Vec::new(); + for r in 0..side_height { + let mut row = Vec::new(); + for c in 0..side_width { + let tile = sparse.grid[fold_row * side_height + r][fold_col * side_width + c]; + assert!(tile.is_some()); + row.push(tile); + } + grid.push(row); + } + let grid = SparseGrid { + grid, + height: side_height, + width: side_width, + row_bounds: vec![(0, side_width - 1); side_height], + col_bounds: vec![(0, side_height - 1); side_width], + }; + assert!(sides.insert(*side, grid).is_none()); + } + + CubeGrid { + sides, + side_height, + side_width, + } + } + + pub fn print(&self, pose: &(CubeSide, Pose)) { + for (side, grid) in &self.sides { + if pose.0 != *side { + continue; + } + println!("{:?}:", side); + grid.print(Some(&pose.1)); + println!(""); + } + } + + fn wrap_around(&self, side: CubeSide, pose: &Pose) -> (CubeSide, Pose) { + // FIXME: This is hardcoded for the folding pattern of the actual input. + // It won't work for the example input. + assert_eq!(self.side_height, self.side_width); + let dir = pose.orientation; + let pos = pose.pos; + let row = pos.0; + let last = self.side_height - 1; + let row_inv = last - row; + let col = pos.1; + match side { + CubeSide::Top => match dir { + Dir::Right => ( + CubeSide::Right, + Pose { + pos: (row, 0), + orientation: Dir::Right, + }, + ), + Dir::Down => ( + CubeSide::Fore, + Pose { + pos: (0, col), + orientation: Dir::Down, + }, + ), + Dir::Left => ( + CubeSide::Left, + Pose { + pos: (row_inv, 0), + orientation: Dir::Right, + }, + ), + Dir::Up => ( + CubeSide::Back, + Pose { + pos: (col, 0), + orientation: Dir::Right, + }, + ), + }, + CubeSide::Bottom => match dir { + Dir::Right => ( + CubeSide::Right, + Pose { + pos: (row_inv, last), + orientation: Dir::Left, + }, + ), + Dir::Down => ( + CubeSide::Back, + Pose { + pos: (col, last), + orientation: Dir::Left, + }, + ), + Dir::Left => ( + CubeSide::Left, + Pose { + pos: (row, last), + orientation: Dir::Left, + }, + ), + Dir::Up => ( + CubeSide::Fore, + Pose { + pos: (last, col), + orientation: Dir::Up, + }, + ), + }, + CubeSide::Left => match dir { + Dir::Right => ( + CubeSide::Bottom, + Pose { + pos: (row, 0), + orientation: Dir::Right, + }, + ), + Dir::Down => ( + CubeSide::Back, + Pose { + pos: (0, col), + orientation: Dir::Down, + }, + ), + Dir::Left => ( + CubeSide::Top, + Pose { + pos: (row_inv, 0), + orientation: Dir::Right, + }, + ), + Dir::Up => ( + CubeSide::Fore, + Pose { + pos: (col, 0), + orientation: Dir::Right, + }, + ), + }, + CubeSide::Right => match dir { + Dir::Right => ( + CubeSide::Bottom, + Pose { + pos: (row_inv, last), + orientation: Dir::Left, + }, + ), + Dir::Down => ( + CubeSide::Fore, + Pose { + pos: (col, last), + orientation: Dir::Left, + }, + ), + Dir::Left => ( + CubeSide::Top, + Pose { + pos: (row, last), + orientation: Dir::Left, + }, + ), + Dir::Up => ( + CubeSide::Back, + Pose { + pos: (last, col), + orientation: Dir::Up, + }, + ), + }, + CubeSide::Fore => match dir { + Dir::Right => ( + CubeSide::Right, + Pose { + pos: (last, row), + orientation: Dir::Up, + }, + ), + Dir::Down => ( + CubeSide::Bottom, + Pose { + pos: (0, col), + orientation: Dir::Down, + }, + ), + Dir::Left => ( + CubeSide::Left, + Pose { + pos: (0, row), + orientation: Dir::Down, + }, + ), + Dir::Up => ( + CubeSide::Top, + Pose { + pos: (last, col), + orientation: Dir::Up, + }, + ), + }, + CubeSide::Back => match dir { + Dir::Right => ( + CubeSide::Bottom, + Pose { + pos: (last, row), + orientation: Dir::Up, + }, + ), + Dir::Down => ( + CubeSide::Right, + Pose { + pos: (0, col), + orientation: Dir::Down, + }, + ), + Dir::Left => ( + CubeSide::Top, + Pose { + pos: (0, row), + orientation: Dir::Down, + }, + ), + Dir::Up => ( + CubeSide::Left, + Pose { + pos: (last, col), + orientation: Dir::Up, + }, + ), + }, + } + } +} + +impl Navigable for CubeGrid { + type Pose = (CubeSide, Pose); + type NavigationInstruction = PassInstr; + + fn forward( + &self, + (from_side, from_pose): &Self::Pose, + steps: usize, + ) -> (Self::Pose, MoveResult) { + let mut side = from_side.clone(); + let mut pose = from_pose.clone(); + let mut status = MoveResult::Success; + for _ in 0..steps { + let (next_pose, res) = self.sides[&side].forward(&pose, 1); + if res.wrapped_around() { + let (next_side, next_pose) = self.wrap_around(side, &pose); + if self.sides[&next_side].grid[next_pose.pos.0][next_pose.pos.1] == Some(true) { + // Can't wrap around, we're blocked + return ((side, pose), MoveResult::BlockedByWrapping); + } + side = next_side; + pose = next_pose; + status = MoveResult::WrappedAround; + } else if res.was_blocked() { + return ((side, pose), MoveResult::Blocked); + } else { + pose = next_pose; + } + } + + ((side, pose), status) + } + + fn exec_instruction( + &self, + from: &Self::Pose, + inst: &Self::NavigationInstruction, + ) -> (Self::Pose, MoveResult) { + match inst { + PassInstr::Move(n) => self.forward(from, *n), + PassInstr::Turn(dir) => ( + ( + from.0, + Pose { + pos: from.1.pos, + orientation: match dir { + TurnDir::CW => from.1.orientation.cw(), + TurnDir::CCW => from.1.orientation.ccw(), + }, + }, + ), + MoveResult::Success, + ), + } + } + + fn initial_pose(&self) -> Self::Pose { + let col = self.sides[&CubeSide::Top].grid[0] + .iter() + .position(|t| *t == Some(false)) + .unwrap(); + ( + CubeSide::Top, + Pose { + pos: (0, col), + orientation: Dir::Right, + }, + ) + } } pub fn parse_map_and_path(input: &str) -> (SparseGrid, Vec) {