advent-of-code-2024/day20/part1.rs

127 lines
4.2 KiB
Rust

#![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}");
}