From 02fe9aea971053b525b45a53c6cef16450b48df1 Mon Sep 17 00:00:00 2001 From: Jan-Bulthuis Date: Fri, 6 Dec 2024 17:54:34 +0100 Subject: [PATCH] day6 fast --- Cargo.lock | 52 +++++++++++ Cargo.toml | 1 + src/day6.rs | 242 +++++++++++++++++++++++++++------------------------- 3 files changed, 180 insertions(+), 115 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 65e034a..4a89a64 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -47,10 +47,42 @@ dependencies = [ "aoc-runner", "aoc-runner-derive", "nom", + "rayon", "regex", "topological-sort", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "itoa" version = "1.0.14" @@ -97,6 +129,26 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "regex" version = "1.11.1" diff --git a/Cargo.toml b/Cargo.toml index 70f6aa1..69cce51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,5 +7,6 @@ edition = "2021" aoc-runner = "0.3.0" aoc-runner-derive = "0.3.0" nom = "7.1.3" +rayon = "1.10.0" regex = "1.11.1" topological-sort = "0.2.2" diff --git a/src/day6.rs b/src/day6.rs index 5567ad7..5c0c80c 100644 --- a/src/day6.rs +++ b/src/day6.rs @@ -1,29 +1,72 @@ -type Input = ((usize, usize), Vec>); - -use std::collections::HashSet; +use std::collections::VecDeque; use aoc_runner_derive::{aoc, aoc_generator}; + +type Input = ((usize, usize), Vec>); +// type Input = ((usize, usize), Vec>, Vec>); + +const UP: u8 = 0; +const DOWN: u8 = 1; +const LEFT: u8 = 2; +const RIGHT: u8 = 4; +const EMPTY: u8 = 8; +const WALL: u8 = 16; +const PASSED: u8 = 32; + +const DIR: [(isize, isize); 5] = [(0, -1), (0, 1), (-1, 0), (0, 0), (1, 0)]; +const NEXT_DIR: [usize; 5] = [RIGHT as usize, LEFT as usize, UP as usize, 0, DOWN as usize]; + #[aoc_generator(day6)] fn parse(input: &str) -> Input { let lines = input.split("\n"); let mut pos = (0, 0); - let map = lines + let mut stones = VecDeque::new(); + + let map: Vec> = lines .enumerate() .map(|(y, line)| { line.chars() .enumerate() .map(|(x, c)| match c { - '.' => 0, - '#' => 1, + '.' => EMPTY, + '#' => { + stones.push_back((x, y)); + WALL + } '^' => { pos = (x, y); - 0 + EMPTY } _ => todo!(), }) .collect() }) .collect(); + + // let mut jumps = vec![vec![[u8::MAX; 5]; map[0].len()]; map.len()]; + + // while let Some((x, y)) = stones.pop_front() { + // for dir in NEXT_DIR { + // let mut d = 1; + // loop { + // let pos = (x as isize - d * DIR[dir].0, y as isize - d * DIR[dir].1); + // if pos.0 < 0 + // || pos.0 >= map[0].len() as isize + // || pos.1 < 0 + // || pos.1 >= map.len() as isize + // || map[pos.1 as usize][pos.0 as usize] == WALL + // { + // break; + // } + + // jumps[pos.1 as usize][pos.0 as usize][dir] = (d - 1) as u8; + + // d += 1; + // } + // } + // } + + // (pos, map, jumps) (pos, map) } @@ -31,161 +74,128 @@ fn parse(input: &str) -> Input { 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 jumps = input.2.clone(); + + let mut pos = ((input.0).0 as isize, (input.0).1 as isize); + let mut dir = UP as usize; let mut count = 0; + loop { - if map[pos.1][pos.0] == 0 { - map[pos.1][pos.0] = 2; + if map[pos.1 as usize][pos.0 as usize] == EMPTY { 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 + map[pos.1 as usize][pos.0 as usize] = PASSED; + } + + if pos.0 + DIR[dir].0 < 0 + || pos.0 + DIR[dir].0 >= w as isize + || pos.1 + DIR[dir].1 < 0 + || pos.1 + DIR[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); - } + + while map[(pos.1 + DIR[dir].1) as usize][(pos.0 + DIR[dir].0) as usize] == WALL { + dir = NEXT_DIR[dir]; } - pos = ( - (pos.0 as isize + dir.0) as usize, - (pos.1 as isize + dir.1) as usize, - ) + + let next_pos = (pos.0 + DIR[dir].0, pos.1 + DIR[dir].1); + pos = next_pos; } + 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 initial = ((input.0).0 as isize, (input.0).1 as isize); + let mut map = input.1.clone(); - let mut pos = input.0; - let mut dir = (0, -1); - let mut loops = vec![]; + + let mut pos = initial; + let mut dir = UP as usize; + let mut count = 0; + 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 + if pos.0 + DIR[dir].0 < 0 + || pos.0 + DIR[dir].0 >= w as isize + || pos.1 + DIR[dir].1 < 0 + || pos.1 + DIR[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); - } + + while map[(pos.1 + DIR[dir].1) as usize][(pos.0 + DIR[dir].0) as usize] == WALL { + dir = NEXT_DIR[dir]; } - 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); + + let next_pos = (pos.0 + DIR[dir].0, pos.1 + DIR[dir].1); + + if next_pos != initial && map[next_pos.1 as usize][next_pos.0 as usize] == EMPTY { + map[next_pos.1 as usize][next_pos.0 as usize] = WALL; + if check_loop(pos, dir, &mut map) { + count += 1; } - map[next_pos.1][next_pos.0] = 0; + map[next_pos.1 as usize][next_pos.0 as usize] = PASSED; } + pos = next_pos; } - loops.len() as u64 + + count } -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 { +fn check_loop(mut pos: (isize, isize), mut dir: usize, map: &mut [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; + let mut changed = Vec::with_capacity(w * h * 4); 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 + if pos.0 + DIR[dir].0 < 0 + || pos.0 + DIR[dir].0 >= w as isize + || pos.1 + DIR[dir].1 < 0 + || pos.1 + DIR[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; + let mut turn = false; + while map[(pos.1 + DIR[dir].1) as usize][(pos.0 + DIR[dir].0) as usize] == WALL { + dir = NEXT_DIR[dir]; + turn = true; } - 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(); + + let next_pos = (pos.0 + DIR[dir].0, pos.1 + DIR[dir].1); + + if turn { + if map[next_pos.1 as usize][next_pos.0 as usize] & dir as u8 != 0 { + changed.into_iter().for_each(|pos: (isize, isize)| { + map[pos.1 as usize][pos.0 as usize] &= PASSED | WALL | EMPTY; + }); + return true; - } else if turns > 0 { - VISITED[pos.1][pos.0][dirnum] = true; - VISITED_VEC.push((pos.0, pos.1, dirnum)); + } else { + map[next_pos.1 as usize][next_pos.0 as usize] |= dir as u8; + changed.push(next_pos); } } + + pos = next_pos; } - unsafe { - clear_visited(); - } + changed.into_iter().for_each(|pos: (isize, isize)| { + map[pos.1 as usize][pos.0 as usize] &= PASSED | WALL | EMPTY; + }); false } -unsafe fn clear_visited() { - VISITED_VEC - .iter() - .for_each(|(a, b, c)| VISITED[*b][*a][*c] = false); -} #[cfg(test)] mod tests { use super::*; @@ -193,10 +203,12 @@ mod tests { #[test] fn part1_example() { assert_eq!(part1(&parse("....#.....\n.........#\n..........\n..#.......\n.......#..\n..........\n.#..^.....\n........#.\n#.........\n......#...")), 41); + assert_eq!(part1(&parse(include_str!("../input/2024/day6.txt"))), 4977); } #[test] fn part2_example() { assert_eq!(part2(&parse("....#.....\n.........#\n..........\n..#.......\n.......#..\n..........\n.#..^.....\n........#.\n#.........\n......#...")), 6); + assert_eq!(part2(&parse(include_str!("../input/2024/day6.txt"))), 1729); } }