From ec7a8d539982494f7fc225f26c6dc8029ea07f33 Mon Sep 17 00:00:00 2001 From: Tyler Beckman Date: Thu, 19 Dec 2024 23:50:42 -0700 Subject: [PATCH] Day 20, that was fun --- Cargo.toml | 8 ++++ day20/part1.rs | 127 +++++++++++++++++++++++++++++++++++++++++++++++++ day20/part2.rs | 125 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 260 insertions(+) create mode 100644 day20/part1.rs create mode 100644 day20/part2.rs diff --git a/Cargo.toml b/Cargo.toml index 12004eb..e6bcd08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,3 +43,11 @@ path = "day17/part1.rs" [[bin]] name = "d17p2" path = "day17/part2.rs" + +[[bin]] +name = "d20p1" +path = "day20/part1.rs" + +[[bin]] +name = "d20p2" +path = "day20/part2.rs" diff --git a/day20/part1.rs b/day20/part1.rs new file mode 100644 index 0000000..0002a87 --- /dev/null +++ b/day20/part1.rs @@ -0,0 +1,127 @@ +#![feature(let_chains)] +use itertools::Itertools; + +#[derive(Debug, PartialEq)] +enum Block { + Start { position: u64 }, + End { position: u64 }, + Track { position: u64 }, + Wall, +} + +const GRID_SIZE: usize = 141; +// const GRID_SIZE: usize = 15; +const INPUT: &str = include_str!("input.txt"); + +fn main() { + let (mut start, mut end) = ((0_usize, 0_usize), (0_usize, 0_usize)); + let mut grid: [[Block; GRID_SIZE]; GRID_SIZE] = INPUT + .lines() + .enumerate() + .map(|(row, l)| { + l.chars() + .enumerate() + .map(|(col, c)| match c { + 'S' => { + start = (row, col); + Block::Start { position: 0 } + } + 'E' => { + end = (row, col); + Block::End { position: u64::MAX } + } + '.' => Block::Track { position: u64::MAX }, + '#' => Block::Wall, + _ => unreachable!(), + }) + .collect_vec() + .try_into() + .unwrap() + }) + .collect_vec() + .try_into() + .unwrap(); + + // Run the track, setting the index of the specific block each time + let mut cur = start; + let mut i = 0_u64; + while grid[cur.0][cur.1] != (Block::End { position: u64::MAX }) { + // Set current index + if let Block::Track { ref mut position } = grid[cur.0][cur.1] { + *position = i; + } + + // Find the next one and move to it + if let Block::Track { position } | Block::End { position } = grid[cur.0 + 1][cur.1] + && position == u64::MAX + { + cur = (cur.0 + 1, cur.1); + } else if let Block::Track { position } | Block::End { position } = grid[cur.0 - 1][cur.1] + && position == u64::MAX + { + cur = (cur.0 - 1, cur.1); + } else if let Block::Track { position } | Block::End { position } = grid[cur.0][cur.1 - 1] + && position == u64::MAX + { + cur = (cur.0, cur.1 - 1); + } else if let Block::Track { position } | Block::End { position } = grid[cur.0][cur.1 + 1] + && position == u64::MAX + { + cur = (cur.0, cur.1 + 1); + } else { + unreachable!(); + } + + // Increment and continue + i += 1; + } + // Set end position too + if let Block::End { ref mut position } = grid[cur.0][cur.1] { + *position = i; + } else { + unreachable!(); + } + + // Iterate over it again, this time checking if a cheat is skipping more than 100 + // Run the track, setting the index of the specific block each time + let mut sum = 0; + for row in 0..GRID_SIZE { + for col in 0..GRID_SIZE { + let (Block::Start { + position: cur_position, + } + | Block::End { + position: cur_position, + } + | Block::Track { + position: cur_position, + }) = grid[row][col] + else { + continue; + }; + + // Check all directions for a skip + let to_check = [ + ((row.checked_add(1), Some(col)), (row.checked_add(2), Some(col))), // Down + ((row.checked_sub(1), Some(col)), (row.checked_sub(2), Some(col))), // Up + ((Some(row), col.checked_add(1)), (Some(row), col.checked_add(2))), // Right + ((Some(row), col.checked_sub(1)), (Some(row), col.checked_sub(2))), // Left + ]; + + for (single, double) in to_check { + if let (Some(single_row), Some(single_col)) = single + && let (Some(double_row), Some(double_col)) = double + && itertools::max([single_row, single_col, double_row, double_col]).unwrap() < GRID_SIZE + && let Block::Wall = grid[single_row][single_col] + && let Block::Track { position } | Block::End { position } = grid[double_row][double_col] + && position > cur_position + && (position - cur_position - 2) >= 100 + { + sum += 1; + } + } + } + } + + println!("Result: {sum}"); +} diff --git a/day20/part2.rs b/day20/part2.rs new file mode 100644 index 0000000..c0dd8e4 --- /dev/null +++ b/day20/part2.rs @@ -0,0 +1,125 @@ +#![feature(let_chains)] +use itertools::Itertools; + +#[derive(Debug, PartialEq)] +enum Block { + Start { position: u64 }, + End { position: u64 }, + Track { position: u64 }, + Wall, +} + +const GRID_SIZE: usize = 141; +// const GRID_SIZE: usize = 15; +const INPUT: &str = include_str!("input.txt"); + +fn main() { + let (mut start, mut end) = ((0_usize, 0_usize), (0_usize, 0_usize)); + let mut grid: [[Block; GRID_SIZE]; GRID_SIZE] = INPUT + .lines() + .enumerate() + .map(|(row, l)| { + l.chars() + .enumerate() + .map(|(col, c)| match c { + 'S' => { + start = (row, col); + Block::Start { position: 0 } + } + 'E' => { + end = (row, col); + Block::End { position: u64::MAX } + } + '.' => Block::Track { position: u64::MAX }, + '#' => Block::Wall, + _ => unreachable!(), + }) + .collect_vec() + .try_into() + .unwrap() + }) + .collect_vec() + .try_into() + .unwrap(); + + // Run the track, setting the index of the specific block each time + let mut cur = start; + let mut i = 0_u64; + while grid[cur.0][cur.1] != (Block::End { position: u64::MAX }) { + // Set current index + if let Block::Track { ref mut position } = grid[cur.0][cur.1] { + *position = i; + } + + // Find the next one and move to it + if let Block::Track { position } | Block::End { position } = grid[cur.0 + 1][cur.1] + && position == u64::MAX + { + cur = (cur.0 + 1, cur.1); + } else if let Block::Track { position } | Block::End { position } = grid[cur.0 - 1][cur.1] + && position == u64::MAX + { + cur = (cur.0 - 1, cur.1); + } else if let Block::Track { position } | Block::End { position } = grid[cur.0][cur.1 - 1] + && position == u64::MAX + { + cur = (cur.0, cur.1 - 1); + } else if let Block::Track { position } | Block::End { position } = grid[cur.0][cur.1 + 1] + && position == u64::MAX + { + cur = (cur.0, cur.1 + 1); + } else { + unreachable!(); + } + + // Increment and continue + i += 1; + } + // Set end position too + if let Block::End { ref mut position } = grid[cur.0][cur.1] { + *position = i; + } else { + unreachable!(); + } + + // Iterate over it again, this time checking if a cheat is skipping more than 100 + // Run the track, setting the index of the specific block each time + let mut sum = 0; + for row in 0..GRID_SIZE { + for col in 0..GRID_SIZE { + let (Block::Start { + position: cur_position, + } + | Block::End { + position: cur_position, + } + | Block::Track { + position: cur_position, + }) = grid[row][col] + else { + continue; + }; + + // Check all points that + for end_row in 0..GRID_SIZE { + for end_col in 0..GRID_SIZE { + // 1. Are within 20 manhattan distance + let manhattan: u64 = + (if end_row > row { end_row - row } else { row - end_row } + + if end_col > col { end_col - col } else { col - end_col }).try_into().unwrap(); + if manhattan > 20 { continue; } + + // 2. Would save time by ignoring walls (end - start - manhattan-distance) + if let Block::Track { position } | Block::End { position } = grid[end_row][end_col] + && position > cur_position + && (position - cur_position - manhattan) >= 100 + { + sum += 1; + } + } + } + } + } + + println!("Result: {sum}"); +}