type Input = ((usize, usize), Vec>); use std::collections::HashSet; use aoc_runner_derive::{aoc, aoc_generator}; #[aoc_generator(day6)] fn parse(input: &str) -> Input { let lines = input.split("\n"); let mut pos = (0, 0); let map = lines .enumerate() .map(|(y, line)| { line.chars() .enumerate() .map(|(x, c)| match c { '.' => 0, '#' => 1, '^' => { pos = (x, y); 0 } _ => todo!(), }) .collect() }) .collect(); (pos, map) } #[aoc(day6, part1)] fn part1(input: &Input) -> u64 { let h = input.1.len(); let w = input.1[0].len(); let mut map = input.1.clone(); let mut pos = input.0; let mut dir = (0, -1); let mut count = 0; loop { if map[pos.1][pos.0] == 0 { map[pos.1][pos.0] = 2; count += 1; }; if pos.0 as isize + dir.0 < 0 || pos.0 as isize + dir.0 >= w as isize || pos.1 as isize + dir.1 < 0 || pos.1 as isize + dir.1 >= h as isize { break; } while map[(pos.1 as isize + dir.1) as usize][(pos.0 as isize + dir.0) as usize] == 1 { if dir.0 == 1 { dir = (0, 1); } else if dir.0 == -1 { dir = (0, -1); } else if dir.1 == 1 { dir = (-1, 0); } else if dir.1 == -1 { dir = (1, 0); } } pos = ( (pos.0 as isize + dir.0) as usize, (pos.1 as isize + dir.1) as usize, ) } count } #[aoc(day6, part2)] fn part2(input: &Input) -> u64 { let initial = input.0; let h = input.1.len(); let w = input.1[0].len(); let mut map = input.1.clone(); let mut pos = input.0; let mut dir = (0, -1); let mut loops = vec![]; loop { if pos.0 as isize + dir.0 < 0 || pos.0 as isize + dir.0 >= w as isize || pos.1 as isize + dir.1 < 0 || pos.1 as isize + dir.1 >= h as isize { break; } while map[(pos.1 as isize + dir.1) as usize][(pos.0 as isize + dir.0) as usize] == 1 { if dir.0 == 1 { dir = (0, 1); } else if dir.0 == -1 { dir = (0, -1); } else if dir.1 == 1 { dir = (-1, 0); } else if dir.1 == -1 { dir = (1, 0); } } let next_pos = ( (pos.0 as isize + dir.0) as usize, (pos.1 as isize + dir.1) as usize, ); if map[next_pos.1][next_pos.0] != 1 { map[next_pos.1][next_pos.0] = 1; if check_loop(pos, dir, &map) { // count += 1; if !(loops.contains(&next_pos)) && next_pos != initial { loops.push(next_pos); } // println!("{} {}", nexts_pos.0 + 1, next_pos.1 + 1); } map[next_pos.1][next_pos.0] = 0; } pos = next_pos; } loops.len() as u64 } static mut VISITED: [[[bool; 4]; 130]; 130] = [[[false; 4]; 130]; 130]; static mut VISITED_VEC: Vec<(usize, usize, usize)> = vec![]; fn check_loop(pos: (usize, usize), dir: (isize, isize), map: &[Vec]) -> bool { let h = map.len(); let w = map[0].len(); unsafe { VISITED_VEC.clear(); VISITED_VEC.reserve(w * h * 4); } // let mut visited = [[[false; 4]; 130]; 130]; // let mut visited = HashSet::with_capacity(h * w * 4); // let mut visited = 0; let mut dir = dir; let mut pos = pos; loop { if pos.0 as isize + dir.0 < 0 || pos.0 as isize + dir.0 >= w as isize || pos.1 as isize + dir.1 < 0 || pos.1 as isize + dir.1 >= h as isize { break; } let mut turns = 0; while map[(pos.1 as isize + dir.1) as usize][(pos.0 as isize + dir.0) as usize] == 1 { if dir.0 == 1 { dir = (0, 1); } else if dir.0 == -1 { dir = (0, -1); } else if dir.1 == 1 { dir = (-1, 0); } else if dir.1 == -1 { dir = (1, 0); } turns += 1; } if turns == 2 { break; } pos = ( (pos.0 as isize + dir.0) as usize, (pos.1 as isize + dir.1) as usize, ); let dirnum = ((dir.1 + 1) >> 2) as usize + (dir.0 + 1) as usize; unsafe { if VISITED[pos.1][pos.0][dirnum] { clear_visited(); return true; } else if turns > 0 { VISITED[pos.1][pos.0][dirnum] = true; VISITED_VEC.push((pos.0, pos.1, dirnum)); } } } unsafe { clear_visited(); } false } unsafe fn clear_visited() { VISITED_VEC .iter() .for_each(|(a, b, c)| VISITED[*b][*a][*c] = false); } #[cfg(test)] mod tests { use super::*; #[test] fn part1_example() { assert_eq!(part1(&parse("....#.....\n.........#\n..........\n..#.......\n.......#..\n..........\n.#..^.....\n........#.\n#.........\n......#...")), 41); } #[test] fn part2_example() { assert_eq!(part2(&parse("....#.....\n.........#\n..........\n..#.......\n.......#..\n..........\n.#..^.....\n........#.\n#.........\n......#...")), 6); } }