Skip to content

Commit

Permalink
Complete day 21
Browse files Browse the repository at this point in the history
  • Loading branch information
Riari committed Dec 23, 2023
1 parent ae3d0f4 commit b1df903
Showing 1 changed file with 76 additions and 62 deletions.
138 changes: 76 additions & 62 deletions src/bin/21.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::collections::HashMap;
use num::Integer;
use std::collections::{HashSet, VecDeque};

advent_of_code::solution!(21);

Expand All @@ -17,15 +16,16 @@ impl Map {
Map { value, size }
}

fn get(&self, mut x: isize, mut y: isize) -> char {
if x < 0 {
x = self.size - x;
}
if y < 0 {
y = self.size - y;
fn contains(&self, x: isize, y: isize) -> bool {
x >= 0 && x < self.size && y >= 0 && y < self.size
}

fn at(&self, x: isize, y: isize) -> Option<char> {
if !self.contains(x, y) {
return None;
}

self.value[(y % self.size) as usize][(x % self.size) as usize]
Some(self.value[y as usize][x as usize])
}
}

Expand All @@ -34,72 +34,86 @@ const S: Position = (0, 1);
const E: Position = (1, 0);
const W: Position = (-1, 0);

fn get_plots_adjacent_to(x: isize, y: isize, map: &Map) -> Vec<Position> {
let mut plots = vec![];
for (dx, dy) in &[N, S, E, W] {
let (nx, ny) = (x + dx, y + dy);
if map.get(nx, ny) == '.' {
plots.push((nx, ny));
}
}
plots
}

fn solve(input: &str, is_p2: bool) -> Option<u32> {
let map: Map = Map::new(input);
let mut visited: HashMap<Position, u32> = HashMap::new();
// HyperNeutrino to the rescue again for part 2: https://www.youtube.com/watch?v=9UOMZSL0JTg&t=619s

// Input is assumed to be square with starting position in the centre
visited.insert((map.size / 2, map.size / 2), 0);
fn walk(map: &Map, start_x: isize, start_y: isize, distance: usize) -> u64 {
let mut accessible_plots: HashSet<Position> = HashSet::new();
let mut seen: HashSet<Position> = HashSet::from([(start_x, start_y)]);
let mut to_visit: VecDeque<(isize, isize, usize)> = VecDeque::from([(start_x, start_y, distance)]);

let mut to_visit: Vec<Position> = vec![];
for plot in get_plots_adjacent_to(map.size / 2, map.size / 2, &map) {
to_visit.push(plot);
}
while let Some((x, y, steps)) = to_visit.pop_front() {
if steps % 2 == 0 {
accessible_plots.insert((x, y));
}

for steps in 1..65 {
let mut next: Vec<Position> = vec![];
while let Some(plot) = to_visit.pop() {
let (x, y) = plot;
visited.insert((x, y), steps);
if steps == 0 {
continue;
}

let neighbours = get_plots_adjacent_to(x, y, &map);
for neighbour in neighbours {
if !visited.contains_key(&neighbour) && !next.contains(&neighbour) {
next.push(neighbour);
for (dx, dy) in &[N, S, E, W] {
let (nx, ny) = (x + dx, y + dy);
if let Some(c) = map.at(nx, ny) {
if c != '#' && !seen.contains(&(nx, ny)) {
seen.insert((nx, ny));
to_visit.push_back((nx, ny, steps - 1));
}
}
}
to_visit = next;
}

// for y in 0..map_size {
// for x in 0..map_size {
// if visited.contains_key(&(x, y)) && visited[&(x, y)].is_even() {
// print!("O");
// } else {
// print!("{}", map[y as usize][x as usize]);
// }
// }
// println!();
// }

let mut reachable = 0;
for (_, steps) in visited {
if steps.is_even() {
reachable += 1;
}
}

Some(reachable as u32)
accessible_plots.len() as u64
}

pub fn part_one(input: &str) -> Option<u32> {
solve(input, false)
pub fn part_one(input: &str) -> Option<u64> {
let map = Map::new(input);
Some(walk(&map, map.size / 2, map.size / 2, 64))
}

pub fn part_two(input: &str) -> Option<u32> {
solve(input, true)
pub fn part_two(input: &str) -> Option<u64> {
let map = Map::new(input);

let start = map.size / 2;

let steps = 26501365 as u64;
let map_width = steps / map.size as u64 - 1;

let odd_grids = (map_width / 2 * 2 + 1).pow(2) as u64;
let even_grids = ((map_width + 1) / 2 * 2).pow(2) as u64;

let odd = walk(&map, start, start, (map.size * 2 + 1) as usize);
let even = walk(&map, start, start, (map.size * 2) as usize);

let corner_distance = map.size as usize - 1;
let corners = Vec::from([
walk(&map, start, map.size - 1, corner_distance),
walk(&map, 0, start, corner_distance),
walk(&map, start, 0, corner_distance),
walk(&map, map.size - 1, start, corner_distance),
]);

let small_distance = map.size as usize / 2 - 1;
let small = Vec::from([
walk(&map, 0, map.size - 1, small_distance),
walk(&map, map.size - 1, map.size - 1, small_distance),
walk(&map, 0, 0, small_distance),
walk(&map, map.size - 1, 0, small_distance),
]);

let large_distance = map.size as usize * 3 / 2 - 1;
let large = Vec::from([
walk(&map, 0, map.size - 1, large_distance),
walk(&map, map.size - 1, map.size - 1, large_distance),
walk(&map, 0, 0, large_distance),
walk(&map, map.size - 1, 0, large_distance),
]);

Some(
odd_grids * odd +
even_grids * even +
corners.iter().sum::<u64>() +
(map_width + 1) * small.iter().sum::<u64>() +
map_width * large.iter().sum::<u64>()
)
}

#[cfg(test)]
Expand Down

0 comments on commit b1df903

Please sign in to comment.