use std::collections::HashSet; use crate::util::Dir; pub type Motion = (Dir, usize); pub fn parse_motions(input: &String) -> Vec { let mut result = Vec::new(); for line in input.lines() { let (d, n) = line.split_once(' ').unwrap(); let dir = match d { "L" => Dir::Left, "R" => Dir::Right, "U" => Dir::Up, "D" => Dir::Down, _ => panic!("Unknown direction {}", d), }; let n = n.parse().unwrap(); result.push((dir, n)); } result } pub type Coord = (i64, i64); pub struct State { pub head: Coord, pub knots: Vec, pub visited: HashSet, } impl State { pub fn new(n_knots: usize) -> State { let mut s = State { head: (0, 0), knots: vec![(0, 0); n_knots], visited: HashSet::new(), }; s.visited.insert((0, 0)); s } pub fn print(&self) { let knots_x: Vec = self.knots.iter().map(|v| v.0).collect(); let visited_x: Vec = self.visited.iter().map(|v| v.0).collect(); let knots_y: Vec = self.knots.iter().map(|v| v.1).collect(); let visited_y: Vec = self.visited.iter().map(|v| v.1).collect(); let min_x = 0 .min(self.head.0) .min(*knots_x.iter().min().unwrap()) .min(*visited_x.iter().min().unwrap()); let max_x = 0 .max(self.head.0) .max(*knots_x.iter().max().unwrap()) .max(*visited_x.iter().max().unwrap()); let min_y = 0 .min(self.head.1) .min(*knots_y.iter().min().unwrap()) .min(*visited_y.iter().min().unwrap()); let max_y = 0 .max(self.head.1) .max(*knots_y.iter().max().unwrap()) .max(*visited_y.iter().max().unwrap()); for y in min_y..max_y + 1 { 'x: for x in min_x..max_x + 1 { let c = (x, y); if self.head == c { print!("H"); } else { for (i, knot) in self.knots.iter().enumerate() { if c == *knot { print!("{}", i + 1); continue 'x; } } if self.visited.contains(&c) { print!("#"); } else if c == (0, 0) { print!("s"); } else { print!("."); } } } print!("\n"); } } } pub fn execute_motion(state: &mut State, motion: &Motion) { for _ in 0..motion.1 { let head = &mut state.head; *head = match motion.0 { Dir::Left => (head.0 - 1, head.1), Dir::Right => (head.0 + 1, head.1), Dir::Up => (head.0, head.1 - 1), Dir::Down => (head.0, head.1 + 1), }; tail_catchup(state); state.visited.insert(*state.knots.last().unwrap()); } } pub fn tail_catchup(state: &mut State) { let mut prev = state.head.clone(); let knots = &mut state.knots; for i in 0..knots.len() { let knot = knots.get_mut(i).unwrap(); let dx = prev.0 - knot.0; let dy = prev.1 - knot.1; if dx > 1 { knot.0 += 1; knot.1 += dy / dy.abs().max(1); } else if dx < -1 { knot.0 -= 1; knot.1 += dy / dy.abs().max(1); } else if dy > 1 { knot.1 += 1; knot.0 += dx / dx.abs().max(1); } else if dy < -1 { knot.1 -= 1; knot.0 += dx / dx.abs().max(1); } prev = knot.clone(); } }