use aoc_runner_derive::{aoc, aoc_generator}; use Instruction::*; use itertools::Itertools; #[derive(Debug)] enum Instruction { MoveNorth(i32), MoveSouth(i32), MoveEast(i32), MoveWest(i32), TurnLeft(i32), TurnRight(i32), MoveForward(i32) } impl From<(char, i32)> for Instruction { fn from(value: (char, i32)) -> Self { match value { (i, v) if i == 'N' => MoveNorth(v), (i, v) if i == 'S' => MoveSouth(v), (i, v) if i == 'E' => MoveEast(v), (i, v) if i == 'W' => MoveWest(v), (i, v) if i == 'L' => TurnLeft(v), (i, v) if i == 'R' => TurnRight(v), (i, v) if i == 'F' => MoveForward(v), _ => unreachable!() } } } #[aoc_generator(day12)] fn parse(input: &str) -> Vec { input .lines() .map(|line| { (line.chars().next().unwrap(), line[1..].parse::().unwrap()).into() }) .collect_vec() } const DIR_POS_MODIFIER: [(i32, i32); 4] = [(1, 0), (0, -1), (-1, 0), (0, 1)]; #[aoc(day12, part1)] fn part1(input: &Vec) -> i32 { let mut x = 0; let mut y = 0; let mut direction = 0; // 0 is east, thus 90 is south, 180 is west, and 270 is north for instr in input { match instr { MoveNorth(val) => y += val, MoveSouth(val) => y -= val, MoveEast(val) => x += val, MoveWest(val) => x -= val, TurnLeft(val) => { direction -= val; if direction < 0 { direction += 360; } }, TurnRight(val) => { direction += val; if direction >= 360 { direction -= 360 } }, MoveForward(val) => { let (xmod, ymod) = DIR_POS_MODIFIER[(direction / 90) as usize]; x += xmod * val; y += ymod * val; }, } } x.abs() + y.abs() } #[aoc(day12, part2)] fn part2(input: &Vec) -> i32 { let mut way_x = 10; let mut way_y = 1; let mut ship_x: i32 = 0; let mut ship_y: i32 = 0; for instr in input { match instr { MoveNorth(val) => way_y += val, MoveSouth(val) => way_y -= val, MoveEast(val) => way_x += val, MoveWest(val) => way_x -= val, TurnLeft(val) => { for _ in 0..(val / 90) { let tmp = way_y; way_y = way_x; way_x = -tmp; } }, TurnRight(val) => { for _ in 0..(val / 90) { let tmp = way_y; way_y = -way_x; way_x = tmp; } }, MoveForward(val) => { ship_x += way_x * val; ship_y += way_y * val; }, } } ship_x.abs() + ship_y.abs() } #[cfg(test)] mod tests { use super::*; const EX: &str = r"F10 N3 F7 R90 F11"; #[test] fn part1_example() { assert_eq!(part1(&parse(EX)), 25); } #[test] fn part2_example() { assert_eq!(part2(&parse(EX)), 286); } }