use std::{ error::Error, fmt::{self, Formatter, Display}, io::{self, Read}, mem, str::FromStr, }; type Result = std::result::Result>; #[derive(Debug, Clone)] struct ErrorMessage(String); impl From for ErrorMessage { fn from(other: String) -> Self { ErrorMessage(other) } } impl ErrorMessage { fn message(&self) -> &String { &self.0 } } impl Display for ErrorMessage { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { write!(fmt, "{}", self.message()) } } impl Error for ErrorMessage { fn description(&self) -> &str { self.message() } } #[derive(Debug, Clone, Copy)] enum Wire { Up(usize), Down(usize), Left(usize), Right(usize), } impl Wire { fn magnitude(&self) -> usize { match self { Wire::Up(m) | Wire::Down(m) | Wire::Left(m) | Wire::Right(m) => *m } } fn add_pos(&self, (x, y): (isize, isize)) -> (isize, isize) { let (x0, y0) = match self { Wire::Up(y0) => (0, -(*y0 as isize)), Wire::Down(y0) => (0, *y0 as isize), Wire::Left(x0) => (-(*x0 as isize), 0), Wire::Right(x0) => (*x0 as isize, 0), }; (x + x0, y + y0) } } impl FromStr for Wire { type Err = ErrorMessage; fn from_str(s: &str) -> std::result::Result { let num = s[1..].parse::() .map_err(|e| ErrorMessage(e.description().to_string()))?; let c = s.chars().nth(0) .ok_or_else(|| ErrorMessage("expected U, D, L, or R followed by a number".to_string()))?; match c { 'U' => Ok(Wire::Up(num)), 'D' => Ok(Wire::Down(num)), 'L' => Ok(Wire::Left(num)), 'R' => Ok(Wire::Right(num)), _ => Err(ErrorMessage("expected U, D, L, or R followed by a number".to_string())), } } } #[derive(Debug, Clone, Copy, Default)] struct Cell { pass: usize, steps: [usize; 2], } const FIRST_PASS: usize = 1; const SECOND_PASS: usize = 2; const BOTH_PASS: usize = FIRST_PASS | SECOND_PASS; #[derive(Debug)] struct Board { board: Vec>, pos: (isize, isize), origin: (isize, isize), steps: usize, } impl Default for Board { fn default() -> Self { Board { board: vec![vec![Default::default()]], pos: Default::default(), origin: Default::default(), steps: Default::default(), } } } impl Display for Board { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { for (y, row) in self.board.iter().enumerate() { let y = y as isize; for (x, col) in row.iter().enumerate() { let x = x as isize; let c = if (x, y) == self.origin { 'o' } else if (x, y) == self.pos { '*' } else if col.pass > 0 { '+' } else { '.' }; write!(fmt, "{}", c)?; } writeln!(fmt)?; } Ok(()) } } impl Board { fn x(&self) -> isize { self.pos.0 } fn y(&self) -> isize { self.pos.1 } fn origin_x(&self) -> isize { self.origin.0 } fn origin_y(&self) -> isize { self.origin.1 } fn min_x(&self) -> isize { -self.origin_x() } fn min_y(&self) -> isize { -self.origin_y() } fn max_x(&self) -> isize { self.board_w() - self.origin_x() } fn max_y(&self) -> isize { self.board_h() - self.origin_y() } fn board_w(&self) -> isize { if self.board.is_empty() { 0 } else { self.board[0].len() as isize } } fn board_h(&self) -> isize { self.board.len() as isize } fn ensure_pos(&mut self, (pos_x, pos_y): (isize, isize)) { self.ensure_y_pos(pos_y); self.ensure_x_pos(pos_x); } fn ensure_x_pos(&mut self, pos_x: isize) { if pos_x >= self.max_x() { let amount = pos_x - self.max_x() + 1; self.grow_right(amount as usize); } else if pos_x <= self.min_x() { let amount = (self.min_x() - pos_x).abs() + 1; self.grow_left(amount as usize); } } fn ensure_y_pos(&mut self, pos_y: isize) { if pos_y >= self.max_y() { let amount = pos_y - self.max_y() + 1; self.grow_down(amount as usize); } else if pos_y <= self.min_y() { let amount = (self.min_y() - pos_y).abs() + 1; self.grow_up(amount as usize); } } fn grow_down(&mut self, amount: usize) { let rows = vec!(vec!(Default::default(); self.board_w() as usize); amount); self.board.extend(rows); } fn grow_up(&mut self, amount: usize) { let w = self.board_w(); let rows = mem::replace(&mut self.board, vec!(vec!(Default::default(); w as usize); amount)); self.board.extend(rows); self.origin.1 += amount as isize; } fn grow_left(&mut self, amount: usize) { for row in self.board.iter_mut() { let row_remainder = mem::replace(row, vec!(Default::default(); amount)); row.extend(row_remainder); } self.origin.0 += amount as isize; } fn grow_right(&mut self, amount: usize) { for row in self.board.iter_mut() { row.extend(vec!(Cell::default(); amount)); } } fn apply_wire(&mut self, wire: Wire, which_pass: usize) -> Vec<(Cell, isize, isize)> { let end_pos = wire.add_pos(self.pos); self.ensure_pos(end_pos); let x = (self.x() + self.origin_x()) as usize; let y = (self.y() + self.origin_y()) as usize; /* println!("applying wire {:?}", wire); println!("pos: {:?}", self.pos); println!("end pos: {:?}", end_pos); println!("origin: {:?}", self.origin); println!("start pos: {:?}", (x, y)); println!("size: {:?}", self.board_size()); */ let mut crossings = Vec::new(); let step_index = which_pass - 1; match wire { Wire::Up(amount) => for offset in 0..amount { let y0 = y - offset; self.board[y0][x].pass |= which_pass; if self.board[y0][x].steps[step_index] == 0 { self.board[y0][x].steps[step_index] = self.steps + offset; } if self.board[y0][x].pass == BOTH_PASS { let cell = self.board[y0][x]; crossings.push((cell, self.x(), self.y() - (offset as isize))); } } Wire::Down(amount) => for offset in 0..amount { let y0 = y + offset; self.board[y0][x].pass |= which_pass; if self.board[y0][x].steps[step_index] == 0 { self.board[y0][x].steps[step_index] = self.steps + offset; } if self.board[y0][x].pass == BOTH_PASS { let cell = self.board[y0][x]; crossings.push((cell, self.x(), self.y() + (offset as isize))); } } Wire::Left(amount) => for offset in 0..amount { let x0 = x - offset; self.board[y][x0].pass |= which_pass; if self.board[y][x0].steps[step_index] == 0 { self.board[y][x0].steps[step_index] = self.steps + offset; } if self.board[y][x0].pass == BOTH_PASS { let cell = self.board[y][x0]; crossings.push((cell, self.x() - (offset as isize), self.y())); } } Wire::Right(amount) => for offset in 0..amount { let x0 = x + offset; self.board[y][x0].pass |= which_pass; if self.board[y][x0].steps[step_index] == 0 { self.board[y][x0].steps[step_index] = self.steps + offset; } if self.board[y][x0].pass == BOTH_PASS { let cell = self.board[y][x0]; crossings.push((cell, self.x() + (offset as isize), self.y())); } } } self.steps += wire.magnitude(); self.pos = end_pos; crossings.retain(|(_, x, y)| (*x, *y) != (0, 0)); crossings } fn reset_pos(&mut self) { self.steps = 0; self.pos = (0, 0); } } fn get_input_string() -> io::Result { let mut buffer = String::new(); io::stdin().read_to_string(&mut buffer)?; Ok(buffer) } fn main() -> Result<()> { let input = get_input_string()?; let mut lines = input .trim() .lines() .map(|line| line.trim() .split(",") .map(|wire| wire.parse::().map_err(From::from)) .collect::>>()); let wires1 = lines.next() .expect("two lines of input")?; let wires2 = lines.next() .expect("two lines of input")?; day3(&wires1, &wires2); Ok(()) } fn day3(wires1: &Vec, wires2: &Vec) { let mut board = Board::default(); for wire in wires1.iter() { board.apply_wire(*wire, FIRST_PASS); } board.reset_pos(); let mut crossings = Vec::new(); for wire in wires2.iter() { crossings.extend(board.apply_wire(*wire, SECOND_PASS)); } //println!("{}", board); //println!("{:#?}", crossings); let shortest: isize = crossings.iter() .map(|(_, x, y)| x.abs() + y.abs()) .min() .unwrap(); println!("Part 1: {}", shortest); let fewest: usize = crossings.iter() .map(|(cell, _, _)| cell.steps[0] + cell.steps[1]) .min() .unwrap(); println!("Part 2: {}", fewest); }