diff --git a/day21/Cargo.lock b/day21/Cargo.lock new file mode 100644 index 0000000..c06e5b8 --- /dev/null +++ b/day21/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "day21" +version = "0.1.0" diff --git a/day21/Cargo.toml b/day21/Cargo.toml new file mode 100644 index 0000000..ea8752e --- /dev/null +++ b/day21/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "day21" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/day21/input.txt b/day21/input.txt new file mode 100644 index 0000000..bb592bd --- /dev/null +++ b/day21/input.txt @@ -0,0 +1,131 @@ +................................................................................................................................... +.#........#.#.........#................##.#.............#....................#....#....#.#...#.......#..........#........#......... +....#......#.....................##...#.#....#.#..#...#.......................#.....#..........##....#..#........#.##..#..#...#.... +.#.....#.............#.#...............#...........#.#.#................#...........#.#.....#.......##......#........#.#........... +.....#...........#.......##.#.#..........#....#....#..#.#..................................#....##...#.#.....#..#.......#...#...#.. +...#....##............#...#..#.....#....##....#....##...........#...............#..##..#..............#..#......#.....#............ +..##....#.........#.......#..#...#...........................................#.....#.#........#...#.......................#.....#.. +..##.#........#..........#......#...#.....#..#.......#........................#..........##......#...#.#..............#......#..... +........#....##.................................#.##.........#.......#........#...#..#.....................#.#.##....#.........#... +.#...............#....#..........#....#.#...........................................................#.#.....#...#...#.........#.... +..#........#.##..............#...##.....#..#...#....................................#..##......#..#.......#........................ +...........................................#.................#.#..................#..#..#..#....#..#................#..........#... +.....#...#...#.#......#.....#.#.....#.#..#........................#..#.##...................#.................................#.#.. +......#...........#.........##....#.#.#....#..............#........#....................#.............#..##...#.............#...... +..#...........##....#......#..#........................#.#........#..#...#...........#............................#.#............#. +.#....#..#......#..........................#.......................#....#...........#......#.......#............................... +....#.#...##....#.........#....#...#.#..#............................#.......#...............................................#..#.. +.......#..........##......#..#...#.....#............#...#...................#...............#..#..................#.....#..#...#... +..................#.........#....#.#....#..#....................#.................................................................. +.#.##.................#....#..#.......##.#.................#.#............#...#..........#.#...#.........##.#...............#...... +..#........#.....#.#.........#.#.....#..#........#...#.....#....#....#.....................##....#...............................#. +..#...#....#.#.............#..#..#...............##.......##.........#.....................................#....###.....##....#.... +....#..####.....#....................#................#.......#.............#..#..#..........##......##.......#...........#........ +..........##..#........#.......#..............#.#......#..#....#.............#...#................#......##...#..#......#.......... +......#....#...#........#.............................##..#.....#..............#.#......................................##.#...#... +.#..........................................#.......#...#.##..#............#.......................#.#...##........................ +............#.#...........#.....#....................##...#...#....#.................#.#.........#......#........#......#...#...... +.#.....##.#..#..........#....##............#...........#.#.....#.......#............#.#............#............................... +.........#........#...#.###.....#.........#.#...##.##....##..........#................................#..........#...##...#........ +...........#.........##...#.#..................................................#.........#..................#.........#.......#.... +....##...#..#.#..#.......................#....#......#............#....#.........###......#..............#.......#...#..........#.. +...........#.......#......#...............................#.....#.......#..#...............#.........##........#.#.........#....... +......#....#.................#..............#..#..........#...#........................##..............#.........................#. +.###.##...#.##........#........................#.#.###................#...#.#....#...........#...............##.........#.......... +...........................................#.........#..##.................#...#.#.#..##..#............#.#.#.....##.....#.......... +.#............................................#..#............#....#...#...#......#.#..........................#....#.#..........#. +...........#...........#................#........#..........#........#...........#..............#.........#....##....#....#...##.#. +................#............................#...#......#.....................#..............#.........................##..#...#... +...#....#.............................................#..................#.....#.#..#.....#................#.......#....#.......... +.#....##..#.#.......#.............#..............#...#..#....................#..#........#.#....................................... +.#...#.......................................................#..........#.........#.......##...#.#.#............###............#.#. +.....#.....#......#..............#.#...............#....#.....................#......#....#.#........#.........#....#.........#.... +............#.#.###.......................................................#...................#....#...#.......##............#..... +.#......#..............................#..........#....#....#.#.#........#............#.....#........................#......#...... +................#......................##.......#.......#..#..............##...................#...#...#........................... +.#...#..................#.......................#...#...#..........#.......#.......#...................##...................#...... +........#...............#...#....................#..#.......................#.............#.#.#..........#..........###........#.#. +.#..##...#..#............#.....#.....................#......#..............#......#..#..............#.....................#........ +....#...###.....................###..........#....#....#..............#................#.#.......#.#....##............#......#.#... +......#.....................#.......#.#.#...#....#...#.#.....#..#........##.#..#..#......##........................................ +.......#.#............#...........#........#..............#....##..##..................##..........#...........#.......#..#..#...#. +..#..#......................#..................................#....#......#...#............##......#......................##...#.. +.#...............................#.#.........#.....................###.#..................##.#...#.......#....................#..#. +......##................#.........#......#................#........#.....#....#.#.##..#..#...##...#.#...........#................#. +..#..#.........#...............##..................#........................................#........#......##..................... +...................#....#......#...................#.#.....#..............#....##................#......#...#......#.........#..... +....#...........#.......#......#..........#.....................#..#.........##.....##...........###......##....................... +.................#..#....#..##.........................................#......#..........#............#..#....#.................#.. +.............#..#........#..#..#.#.......#..........#...........#..#.........#.#...................###.....#....................... +..#............#..#..#..#..................#.##.#...#.#...#............###......#........................#.........#..#.........#.. +...........#..##.#..#.............#...#.............#.........#.....#.......#..................#...##..##......#...#..#............ +..............#...#...#.....##....#.#........#................#.....#.....#......#.......##....#.....#......#..#....#.............. +.............#....#..##..#.................#....#.#......#.#......#...##.....#....###.......#.......#.............................. +............................#..........#.......#................#....#.........#.......#.#..................#...................... +..........#...#.......#......................................##..........#..#......#............#..............#.....#..#.......... +.................................................................S................................................................. +................#.........#.....#........#......................#....#.#.............#....#...#.............#...........#.......... +.........................................#......#............#...........#.......#......#..##.........##.............#............. +........#................#.......#..........#.....#..........#........#.................##......#.............#.....#.....#........ +........#........#......#.......#....#...##.....................#.........#.....#.........#.................#..#................... +.............#............................#.......#......#...##.........##...#......#.....#.#...#....................#............. +..................................................#......#.......................#..##..#...##........#.#........#................. +.#...........#..#.......#.....#...............#...#.....#..........#...#.#.#.......#.#..........................#.#................ +.####...............#...........#......#..#...................#....#....#...........#...#...................#.................#.#.. +...............#......#.##...............##..#..#........#..........#.#.............##......#............#...#.....#............#.. +.......................#.....###..#....#..#........................................#.#.............#...#........#.................. +.....................##....#...............#.........#....#.####...#......#.................#.#...........#......##..........#.#... +..................#.................###...#............................#.#...##..#.#.#..#...#.................................#..#. +....................#..........#..............#............................#.......#.........................#................#.... +.#..#...............#.........#..#..........#...#..........##.......##.##.....#....#...#.#......#...##.#........................... +....#...............#....#.............................#.....#.........#..............#......#..............#............#.......#. +........................#...........#..........#...#............#...#.#....#.............##..............##.#...................#.. +...#..................#.#...#...#.........#...........#.....#...#.##........#.#....#..........#.......##........................... +.........#.#..........##.#....................#....#................#..#.#.....#...........#.........####.............#.......##... +.#......#...................#........#.......#........#...#..#..........#..##..#..........#........................................ +.....#....#..#................##.....#......#.........................#.###...................#..#................#..#......#...... +..............#..........................................#.........#.....##.###............#...##.....................##........... +..#......#..#.#.#.........#...#....................#.........#.........#......#...#.....................................#.......... +.......#...#.................#..........................#...##..#...#.#.........##............#..................#...............#. +....#.........#.............#.......#.#..........#................#..##.#.#.#...................#.....#............#.....##....#... +.###...............................#.........#..............#.#......#......#.....##.#.#.......#................#.#..............#. +...#........#......#........................#.......#.##...#.........#........#...#.......#..##....#...........#....#..##.......... +......#.......#........#.......#..#...#....#.......#....#.#..........##........#..#...............#.......................##..#.... +...................#....#..........#........#.............#...#...##...#..........#.......#...#............#..........##........... +.#........##......#.#................#.....#..........#....###................#.....#.......###...............#...#..........#..#.. +...#.........#...#....#............#..................##....#......#.....#.......#..........#...#...........#.....#..#.......#..... +........#.................#........#.........#...............#...........#.##...##.......#..................#.......#....##........ +.###......#.....#......#.....................##..#...#........#........#....#................................#....#.....#.#........ +...........##..............................#..#...........#....#...#......#.#..#......#...............#............................ +.....#.#..#.........#............................#.....#.#.##......#.#.........#.....#.#...........................#...##......#.#. +..............#.#.#.....#.#.............#..#.#.....#....#..#.#....#.................#....................#..........#........#..... +..........#.....##.......#................#....................................#..#......#.........#..............#...#......##.... +.##.#......###..............#.#...............###......#.................#.......#......#.............................#....#.#..... +..#..#...#............#....##.............#........................#.#..................................#......................#... +......#.....#.............#...#............#....#.......#.....#.................................#..##...#.#..............#......... +...........#..........#..#.#..................##...........#.......#........#....#.............#....................#.....#........ +....#...........#........##........................................#.#...............#.............#.#...##...#........#........... +...#..#..............................................................#...#.#...#..#...........#..#..#....#..#..#.......#..#........ +...................#.#.....##...........................#................#.#...#...........#.............................#......... +.....###.........#.....#...#...##...#..#........###.....#..#..............#.....#...................#...#...........#...#.......... +..............#......................................#..#....#.........##....##..#.......#.......................#.....##.......... +..#...................#...........#................................#.....#.#.#..........................#....#......#......#....... +..#......#....#..##..#....#............#...........###........................#.........#........#........#............#.........#. +...................#.##...........#....#..##............#...................#.........#........##........#...........#.#........... +...#.#......#....#........#.......#....................................#.##..#.........#....#.#.....#.........................#..#. +....#.#...#...........................................#...#..#....#....#..............#.........................#.....#......#..#.. +......#..................#..........#..#....#.......................#...............#...........#..............#....#.#............ +.#..............#........##......#...#....#...#.#................................................#.....##....#...##...#.#........#. +.........#.............#..#..#...........................#.............#...........................#....##............#............ +..........#...#.....#..#...#...........#.#.......#........#.....................#.....#.....#...#.#..#......#....#....#...#..#..... +.#........#............##.................##...............#.....................#.##.................#........#......#............ +....#........##....#..#.#.........#................................#................#.#.....#.............#..#....##.#....##....... +........#.#.....#.......#.#..##..............##...................#...................#..#....................#..#.......#...#.##.. +.....#.#.........#.#....###..#..#.......#.....#................#....#........#.....................#.###.....#...........#......#.. +......#...#................#..#...##............#......#...................#............................#.........#....##...##..... +..........#...##.....#....#...........#.....#......#..#.......................##...#.##.....#...#..##...#...#..#..#..........#...#. +......#............#...........#.#..#...............#.............................#..#.......#...#.................#.......#....#.. +..............#........#............#....#.....##...#.###...................#............#..#.#..................#.............#... +.............#....#.......#..#......#....#...#..#.......##.............#.##...#.........#..........................#.#...........#. +.#..............#...#......#.......#..#................#.#.............#....#...............#.......#.............................. +................................................................................................................................... diff --git a/day21/src/main.rs b/day21/src/main.rs new file mode 100644 index 0000000..17b1681 --- /dev/null +++ b/day21/src/main.rs @@ -0,0 +1,267 @@ +use std::mem; +use std::fs::read_to_string; +use std::time::Instant; + +// Solution to part 1 and helper for part 2 +fn count_options_basic(layout: &Vec<&[u8]>, start_pos: (i32, i32), steps: i32) -> u64 { + count_options_basic_cont(layout, start_pos, steps, false) +} + +// Like count_options_basic but can begin as if we're already on an odd number +fn count_options_basic_cont(layout: &Vec<&[u8]>, start_pos: (i32, i32), steps: i32, count_odd: bool) -> u64 { + if steps < 0 { + return 0; + } + let modulo_equal = (steps + if count_odd { 1 } else { 0 }) % 2; + let width = layout[0].len(); + let height = layout.len(); + let mut visited: Vec> = vec![vec![false; width]; height]; + let mut tile_queue: Vec<(i32, i32)> = vec![start_pos]; + visited[start_pos.1 as usize][start_pos.0 as usize] = true; + let mut sum = (1 - modulo_equal) as u64; + let mut next_tile_queue: Vec<(i32, i32)> = vec![]; + for step in 1..=steps { + let mut try_tile = |x: i32, y: i32| { + if layout[y as usize][x as usize] != b'#' { + if !visited[y as usize][x as usize] { + visited[y as usize][x as usize] = true; + next_tile_queue.push((x, y)); + if step % 2 == modulo_equal { + sum += 1; + } + } + } + }; + for &(x, y) in &tile_queue { + if x > 0 { + try_tile(x - 1, y); + } + if x < width as i32 - 1 { + try_tile(x + 1, y); + } + if y > 0 { + try_tile(x, y - 1); + } + if y < height as i32 - 1 { + try_tile(x, y + 1); + } + } + if next_tile_queue.is_empty() { + break; + } + tile_queue.clear(); + mem::swap(&mut tile_queue, &mut next_tile_queue); + } + sum +} + +// Not used in the end but helped figure out the better solution, also has commented visualisation +fn count_options_infinite_naive(layout: &Vec<&[u8]>, start_pos: (i32, i32), steps: i32) -> u64 { + if steps < 0 { + return 0; + } + let modulo_equal = steps % 2; + // Assuming square + let chunk_width = layout.len() as i32; + let chunk_reach = (steps + chunk_width / 2) / chunk_width + 1; + #[derive(Clone)] + struct Chunk { + visited: Vec>, + solution: i32, + } + impl Chunk { + fn new(width: usize, height: usize) -> Chunk { + Chunk { + visited: vec![vec![false; width]; height], + solution: 0, + } + } + } + let chunk_offset = chunk_reach - 1; + let chunks_size = chunk_reach * 2 - 1; + let mut chunks: Vec> = vec![vec![Chunk::new(chunk_width as usize, chunk_width as usize); chunks_size as usize]; chunks_size as usize]; + let mut tile_queue: Vec<(i32, i32)> = vec![start_pos]; + let mut sum = 0; + let mut next_tile_queue: Vec<(i32, i32)> = vec![]; + for step in 1..=steps { + let mut try_tile = |x: i32, y: i32| { + let mut chunk_x = if x < 0 { (x - chunk_width + 1) / chunk_width } else { x / chunk_width }; + let mut chunk_y = if y < 0 { (y - chunk_width + 1) / chunk_width } else { y / chunk_width }; + let inside_x = (x - chunk_x * chunk_width) as usize; + let inside_y = (y - chunk_y * chunk_width) as usize; + chunk_x += chunk_offset; + chunk_y += chunk_offset; + if chunk_x < 0 || chunk_y < 0 || chunk_x >= chunks_size || chunk_y >= chunks_size { + // println!("Out of bounds"); + return; + } + let chunk = &mut chunks[chunk_y as usize][chunk_x as usize]; + if layout[inside_y][inside_x] != b'#' { + if !chunk.visited[inside_y][inside_x] { + chunk.visited[inside_y][inside_x] = true; + next_tile_queue.push((x, y)); + if step % 2 == modulo_equal { + sum += 1; + chunk.solution += 1; + } + } + } + }; + for &(x, y) in &tile_queue { + try_tile(x - 1, y); + try_tile(x + 1, y); + try_tile(x, y - 1); + try_tile(x, y + 1); + } + tile_queue.clear(); + mem::swap(&mut tile_queue, &mut next_tile_queue); + } + // for row in &chunks { + // println!("{:?}", row.iter().map(|chunk| chunk.solution).collect::>()); + // } + sum +} + +// Solution to part 2, makes an assumption that the starting row and column are empty +fn count_options_infinite(layout: &Vec<&[u8]>, steps: i32) -> u64 { + // Assuming square + let chunk_width = layout.len() as i32; + // Assuming the start is in the middle of chunk (it is so in the input) + let start_pos = chunk_width / 2; + // How many chunks in line from the start would the search reach + let chunk_reach = (steps + chunk_width / 2) / chunk_width + 1; + let chunk_reach_diag = (steps - 1) / chunk_width + 1; + // Used to get solutions for various kinds of edge garden repetitions of the reachable region + let count_options_adjusted = |start_pos: (i32, i32), steps_to_edge: i32| { + let remaining_steps = steps - steps_to_edge; + count_options_basic(layout, start_pos, remaining_steps) + }; + // Order: N, W, S, E + let edges_straight_closer = [ + count_options_adjusted( + (start_pos, chunk_width - 1), + start_pos + 1 + (chunk_reach - 3) * chunk_width), + count_options_adjusted( + (0, start_pos), + start_pos + 1 + (chunk_reach - 3) * chunk_width), + count_options_adjusted( + (start_pos, 0), + start_pos + 1 + (chunk_reach - 3) * chunk_width), + count_options_adjusted( + (chunk_width - 1, start_pos), + start_pos + 1 + (chunk_reach - 3) * chunk_width) + ]; + let edges_straight_further = [ + count_options_adjusted( + (start_pos, chunk_width - 1), + start_pos + 1 + (chunk_reach - 2) * chunk_width), + count_options_adjusted( + (0, start_pos), + start_pos + 1 + (chunk_reach - 2) * chunk_width), + count_options_adjusted( + (start_pos, 0), + start_pos + 1 + (chunk_reach - 2) * chunk_width), + count_options_adjusted( + (chunk_width - 1, start_pos), + start_pos + 1 + (chunk_reach - 2) * chunk_width) + ]; + // Order: NE, NW, SW, SE + let edges_diagonal_closer = [ + count_options_adjusted( + (chunk_width - 1, chunk_width - 1), + 2 * (start_pos + 1) + (chunk_reach_diag - 3) * chunk_width), + count_options_adjusted( + (0, chunk_width - 1), + 2 * (start_pos + 1) + (chunk_reach_diag - 3) * chunk_width), + count_options_adjusted( + (0, 0), + 2 * (start_pos + 1) + (chunk_reach_diag - 3) * chunk_width), + count_options_adjusted( + (chunk_width - 1, 0), + 2 * (start_pos + 1) + (chunk_reach_diag - 3) * chunk_width) + ]; + let edges_diagonal_further = [ + count_options_adjusted( + (chunk_width - 1, chunk_width - 1), + 2 * (start_pos + 1) + (chunk_reach_diag - 2) * chunk_width), + count_options_adjusted( + (0, chunk_width - 1), + 2 * (start_pos + 1) + (chunk_reach_diag - 2) * chunk_width), + count_options_adjusted( + (0, 0), + 2 * (start_pos + 1) + (chunk_reach_diag - 2) * chunk_width), + count_options_adjusted( + (chunk_width - 1, 0), + 2 * (start_pos + 1) + (chunk_reach_diag - 2) * chunk_width) + ]; + let mut sum: u64 = 0; + if chunk_reach_diag < 3 { + // If the reach is small, compute the central chunk + sum += count_options_basic(layout, (start_pos, start_pos), steps); + } + if chunk_reach >= 2 { + // Fill the corners (of the rotated square) + sum += edges_straight_further.iter().sum::(); + } + if chunk_reach_diag >= 2 { + // Fill the outer edges + sum += edges_diagonal_further.iter().sum::() * (chunk_reach_diag - 1) as u64; + } + if chunk_reach >= 3 { + // Fill the inner corners (of the rotated square) + sum += edges_straight_closer.iter().sum::(); + } + if chunk_reach_diag >= 3 { + // Fill the space where we know chunks will be fully filled + let full_even = count_options_basic_cont( + layout, (start_pos, start_pos), 2 * chunk_width, false); + let full_odd = count_options_basic_cont( + layout, (start_pos, start_pos), 2 * chunk_width, true); + let r = chunk_reach_diag as u64; + let quadratic1 = r * r + 4 - 4 * r; + let quadratic2 = r * r - 2 * r - 3; + if (chunk_reach_diag + steps) % 2 == 0 { + sum += full_even * quadratic2; + sum += full_odd * quadratic1; + } else { + sum += full_even * quadratic1; + sum += full_odd * quadratic2; + } + // Fill the inner edges + sum += edges_diagonal_closer.iter().sum::() * (chunk_reach_diag - 2) as u64; + // Fill the extension of central shape if the corners stick out more + if chunk_reach > chunk_reach_diag { + sum += 4 * if chunk_reach_diag % 2 == 0 { full_even } else {full_odd}; + } + } + sum +} + +fn main() { + let time_start = Instant::now(); + let input_str = read_to_string("input.txt").unwrap(); + let time_start_no_io = Instant::now(); + let mut start_pos: (i32, i32) = (0, 0); + let mut layout: Vec<&[u8]> = vec![]; + for line in input_str.lines() { + let bytes = line.as_bytes(); + match bytes.iter().position(|&c| c == b'S') { + Some(pos) => { + start_pos = (pos as i32, layout.len() as i32); + } + None => {} + } + layout.push(bytes); + } + // Part 1 + let sum1 = count_options_basic(&layout, start_pos, 64); + // part 2 + // let sum2 = count_options_infinite_naive(&layout, start_pos, 500); + let sum2 = count_options_infinite(&layout, 26501365); + let elapsed = time_start.elapsed().as_micros(); + let elapsed_no_io = time_start_no_io.elapsed().as_micros(); + println!("Time: {}us", elapsed); + println!("Time without file i/o: {}us", elapsed_no_io); + println!("Sum1: {}", sum1); + println!("Sum2: {}", sum2); +}