-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
260 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,128 @@ | ||
use crate::input::Input; | ||
use crate::{ | ||
common::array_deque::ArrayDeque, | ||
input::{on_error, Input}, | ||
}; | ||
|
||
pub const fn solve(_input: &Input) -> Result<u32, String> { | ||
Ok(0) | ||
const MAX_GRID_SIZE: usize = 150; | ||
const WORK_QUEUE_MAX_SIZE: usize = 1024; | ||
|
||
pub fn solve(input: &Input) -> Result<u32, String> { | ||
let width = input | ||
.text | ||
.lines() | ||
.next() | ||
.map(|line| line.len()) | ||
.ok_or_else(on_error)? as i16; | ||
let grid = Grid { | ||
s: input.text.as_bytes(), | ||
width, | ||
}; | ||
if grid.s.len() != ((grid.width + 1) * grid.width - 1) as usize { | ||
return Err("Invalid input - not a rectangle".to_string()); | ||
} else if grid.width >= MAX_GRID_SIZE as i16 { | ||
return Err("Invalid input - too big rectangle".to_string()); | ||
} | ||
|
||
let mut start_location = (0, 0); | ||
let mut end_location = (0, 0); | ||
for y in 0..grid.width { | ||
for x in 0..grid.width { | ||
match grid.at((x, y)) { | ||
b'S' => start_location = (x, y), | ||
b'E' => end_location = (x, y), | ||
_ => {} | ||
} | ||
} | ||
} | ||
if start_location == (0, 0) { | ||
return Err("No start location".to_string()); | ||
} else if end_location == (0, 0) { | ||
return Err("No end location".to_string()); | ||
} | ||
|
||
let mut to_visit = ArrayDeque::<{ WORK_QUEUE_MAX_SIZE }, (u16, (i16, i16))>::new(); | ||
let mut costs = [[u16::MAX; MAX_GRID_SIZE]; MAX_GRID_SIZE]; | ||
|
||
to_visit.push_back((0, start_location))?; | ||
|
||
while let Some((cost, position)) = to_visit.pop_front() { | ||
if costs[position.1 as usize][position.0 as usize] <= cost { | ||
continue; | ||
} | ||
costs[position.1 as usize][position.0 as usize] = cost; | ||
for (dx, dy) in [(0, -1), (1, 0), (0, 1), (-1, 0)] { | ||
let next = (position.0 + dx, position.1 + dy); | ||
if grid.at(next) != b'#' { | ||
to_visit.push_back((cost + 1, next))?; | ||
} | ||
} | ||
} | ||
|
||
let diamond_size: i16 = input.part_values(2, 20); | ||
|
||
// Second pass to find cheats | ||
let mut visited = [[false; MAX_GRID_SIZE]; MAX_GRID_SIZE]; | ||
let mut to_visit = ArrayDeque::<{ WORK_QUEUE_MAX_SIZE }, (i16, i16)>::new(); | ||
to_visit.push_back(start_location)?; | ||
let mut num_great_cheats = 0; | ||
|
||
while let Some(position) = to_visit.pop_front() { | ||
if visited[position.1 as usize][position.0 as usize] { | ||
continue; | ||
} | ||
let cost = costs[position.1 as usize][position.0 as usize]; | ||
visited[position.1 as usize][position.0 as usize] = true; | ||
for (dx, dy) in [(0, -1), (1, 0), (0, 1), (-1, 0)] { | ||
let next = (position.0 + dx, position.1 + dy); | ||
if grid.at(next) != b'#' { | ||
to_visit.push_back(next)?; | ||
} | ||
} | ||
|
||
// Cheat: | ||
for dx in -diamond_size..=diamond_size { | ||
for dy in -diamond_size..=diamond_size { | ||
let manhattan_distance = dx.abs() + dy.abs(); | ||
if (dx, dy) == (0, 0) || (manhattan_distance > diamond_size) { | ||
continue; | ||
} | ||
let cheat = (position.0 + dx, position.1 + dy); | ||
if grid.at(cheat) != b'#' && costs[cheat.1 as usize][cheat.0 as usize] != u16::MAX { | ||
if let Some(gain) = costs[cheat.1 as usize][cheat.0 as usize] | ||
.checked_sub(cost + manhattan_distance as u16) | ||
{ | ||
if gain >= 100 { | ||
num_great_cheats += 1; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
Ok(num_great_cheats) | ||
} | ||
|
||
struct Grid<'a> { | ||
s: &'a [u8], | ||
width: i16, | ||
} | ||
|
||
impl Grid<'_> { | ||
const fn at(&self, position: (i16, i16)) -> u8 { | ||
if position.0 < 0 || position.0 >= self.width || position.1 < 0 || position.1 >= self.width | ||
{ | ||
return b'#'; | ||
} | ||
self.s[(position.0 + (self.width + 1) * position.1) as usize] | ||
} | ||
} | ||
|
||
#[test] | ||
pub fn tests() { | ||
use crate::input::{test_part_one_no_allocations, test_part_two_no_allocations}; | ||
|
||
let test_input = ""; | ||
test_part_one_no_allocations!(test_input => 0); | ||
test_part_two_no_allocations!(test_input => 0); | ||
|
||
let real_input = include_str!("day20_input.txt"); | ||
test_part_one_no_allocations!(real_input => 0); | ||
test_part_two_no_allocations!(real_input => 0); | ||
test_part_one_no_allocations!(real_input => 1338); | ||
test_part_two_no_allocations!(real_input => 975_376); | ||
} |
Oops, something went wrong.