Optimzed the solution for day 21 and actually calculate the answer for part 2 in code

This commit is contained in:
Dreaded_X 2023-12-22 01:59:35 +01:00
parent 6aaca41194
commit 295bfed6b4
Signed by: Dreaded_X
GPG Key ID: FA5F485356B0D2D4

View File

@ -15,8 +15,8 @@ mod tests {
#[test] #[test]
fn part1_test1() -> Result<()> { fn part1_test1() -> Result<()> {
// The example provided uses 6 steps instead of 64, however this should be the correct // The example only provides an answer for 6 steps. (16)
// answer for 64 steps with the example // This should be the correct answer for running the example with 64 steps
Day::test(Day::part1, "test-1", 42) Day::test(Day::part1, "test-1", 42)
} }
@ -25,9 +25,11 @@ mod tests {
Day::test(Day::part1, "input", 3642) Day::test(Day::part1, "input", 3642)
} }
// There is no test case for part 2
#[test] #[test]
fn part2_test1() -> Result<()> { fn part2_solution() -> Result<()> {
Day::test(Day::part2, "test-1", 154) Day::test(Day::part2, "input", 608603023105276)
} }
// Benchmarks // Benchmarks
@ -48,7 +50,7 @@ mod tests {
pub struct Day; pub struct Day;
impl aoc::Solver for Day { impl aoc::Solver for Day {
type Output1 = usize; type Output1 = usize;
type Output2 = usize; type Output2 = isize;
fn day() -> u8 { fn day() -> u8 {
21 21
@ -64,7 +66,7 @@ impl aoc::Solver for Day {
.enumerate() .enumerate()
.map(|(x, mut c)| { .map(|(x, mut c)| {
if c == 'S' { if c == 'S' {
queue.push(((x as isize, y as isize), 64)); queue.push((x as isize, y as isize));
c = '.'; c = '.';
} }
@ -74,49 +76,35 @@ impl aoc::Solver for Day {
}) })
.collect(); .collect();
let mut visited = HashSet::new(); let mut set = HashSet::new();
let directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]; let directions = [(-1, 0), (1, 0), (0, -1), (0, 1)];
// Depth first for _ in 0..64 {
while let Some(step) = queue.pop() { while let Some(step) = queue.pop() {
// If we have already evaluated this state, do not add it to the queue for direction in directions {
if visited.contains(&step) { let next = (step.0 + direction.0, step.1 + direction.1);
continue;
}
// Mark this node as visited // If the tile is free add it to the queue
visited.insert(step); if let Some(&tile) = map.get(&next) {
if tile == '.' {
// Check if we have ran out of steps set.insert(next);
if step.1 == 0 { }
continue;
}
// Try moving in all directions
for direction in directions {
let next = (
(step.0 .0 + direction.0, step.0 .1 + direction.1),
step.1 - 1,
);
// If the tile is free add it to the queue
if let Some(&tile) = map.get(&next.0) {
if tile == '.' {
queue.push(next);
} }
} }
} }
queue = set.into_iter().collect();
set = HashSet::new();
} }
visited.iter().filter(|step| step.1 == 0).count() queue.len()
} }
fn part2(input: &str) -> Self::Output2 { fn part2(input: &str) -> Self::Output2 {
let height = input.lines().count(); // All maps are square
let width = input.lines().next().unwrap().chars().count(); let size = input.lines().count();
println!("{width}, {height}"); // Map is square: 131 x 131 => size = 131
// Map is square: 131 x 131 // !!! Others have observed 26501365 = 202300 * size + size/2 !!!
// !!! Others have observed 26501365 = 202300 * 131 + 65 !!! // Is there a pattern for i * size + size/2?
// Is there a pattern for i * 131 + 65?
// i = 0 => 3776 // i = 0 => 3776
// i = 1 => 33652 // i = 1 => 33652
// i = 2 => 93270 // i = 2 => 93270
@ -125,9 +113,9 @@ impl aoc::Solver for Day {
// 3642 - 14737 x + 14871 x^2 // 3642 - 14737 x + 14871 x^2
// i = 202300 => 608603023105276 // i = 202300 => 608603023105276
// Could not have done this without a hint from Reddit... // Could not have done this without a hint from Reddit...
const N: usize = 2 * 131 + 65; let i: isize = 202300;
let mut queue = VecDeque::new(); let mut queue = Vec::new();
let map: HashMap<(isize, isize), char> = input let map: HashMap<(isize, isize), char> = input
.lines() .lines()
.enumerate() .enumerate()
@ -136,7 +124,7 @@ impl aoc::Solver for Day {
.enumerate() .enumerate()
.map(|(x, mut c)| { .map(|(x, mut c)| {
if c == 'S' { if c == 'S' {
queue.push_back(((x as isize, y as isize), 0)); queue.push((x as isize, y as isize));
c = '.'; c = '.';
} }
@ -146,44 +134,41 @@ impl aoc::Solver for Day {
}) })
.collect(); .collect();
let mut visited = HashSet::new(); let mut nums = Vec::new();
let mut set = HashSet::new();
let directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]; let directions = [(-1, 0), (1, 0), (0, -1), (0, 1)];
// Depth first for n in 0..(2 * size + size / 2) {
while let Some(step) = queue.pop_front() { while let Some(step) = queue.pop() {
// If we have already evaluated this state, do not add it to the queue for direction in directions {
if visited.contains(&step) { let next = (step.0 + direction.0, step.1 + direction.1);
continue;
}
// Mark this node as visited let next_wrapped = (
visited.insert(step); next.0.rem_euclid(size as isize),
next.1.rem_euclid(size as isize),
);
// Check if we have ran out of steps // If the tile is free add it to the queue
if step.1 == N { if let Some(&tile) = map.get(&next_wrapped) {
continue; if tile == '.' {
} set.insert(next);
}
// Try moving in all directions
for direction in directions {
let next = (
(step.0 .0 + direction.0, step.0 .1 + direction.1),
step.1 + 1,
);
let next_wrapped = (
next.0 .0.rem_euclid(width as isize),
next.0 .1.rem_euclid(height as isize),
);
// If the tile is free add it to the queue
if let Some(&tile) = map.get(&next_wrapped) {
if tile == '.' {
queue.push_back(next);
} }
} }
} }
queue = set.into_iter().collect();
set = HashSet::new();
if n + 1 == nums.len() * size + size / 2 {
nums.push(queue.len() as isize);
}
} }
visited.iter().filter(|step| step.1 == N).count() // Using linear algebra these solutions can be found
let a = (nums[0] - 2 * nums[1] + nums[2]) / 2;
let b = (4 * nums[1] - 3 * nums[0] - nums[2]) / 2;
let c = nums[0];
a * i.pow(2) + b * i + c
} }
} }