1
0
Fork 0
advent-of-code/2023/src/bin/21.rs

126 lines
3.4 KiB
Rust

advent_of_code::solution!(21);
use std::collections::{HashMap, VecDeque};
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
struct Point(usize, usize);
type Map = [[char; 131]; 131];
fn parse(input: &str) -> (Point, Map) {
let mut m = [['#'; 131]; 131];
let mut start: Point = Point(0, 0);
for (x, line) in input.lines().enumerate() {
if line.is_empty() {
continue;
}
for (y, t) in line.chars().enumerate() {
match t {
'.' => m[x][y] = '.',
'#' => {}
'S' => {
start.0 = x;
start.1 = y;
m[x][y] = '.';
}
n @ _ => panic!("bad map char {}", n),
}
}
}
(start, m)
}
fn infinite_step(start: &Point, map: &Map) -> HashMap<Point, usize> {
let mut visited: HashMap<Point, usize> = HashMap::new();
let mut frontier: VecDeque<(Point, usize)> = VecDeque::new();
frontier.push_back((*start, 0));
while let Some((cur, dist)) = frontier.pop_front() {
if visited.contains_key(&cur) {
continue;
}
visited.insert(cur, dist);
let x = cur.0;
let y = cur.1;
if x > 0 && map[x - 1][y] == '.' {
let p = Point(x - 1, y);
if !visited.contains_key(&p) {
frontier.push_back((p, dist + 1));
}
}
if x < map.len() - 1 && map[x + 1][y as usize] == '.' {
let p = Point(x + 1, y);
if !visited.contains_key(&p) {
frontier.push_back((p, dist + 1));
}
}
if y > 0 && map[x][y - 1] == '.' {
let p = Point(x, y - 1);
if !visited.contains_key(&p) {
frontier.push_back((p, dist + 1));
}
}
if y < map[x].len() - 1 && map[x][y + 1] == '.' {
let p = Point(x, y + 1);
if !visited.contains_key(&p) {
frontier.push_back((p, dist + 1));
}
}
}
visited
}
pub fn part_one(input_str: &str) -> Option<usize> {
let input = parse(input_str);
let visited = infinite_step(&input.0, &input.1);
let r = visited
.values()
.filter(|t| **t <= 64 && **t % 2 == 0)
.count();
Some(r)
}
pub fn part_two(input_str: &str) -> Option<usize> {
let input = parse(input_str);
let visited = infinite_step(&input.0, &input.1);
let even_corners = visited
.values()
.filter(|v| **v % 2 == 0 && **v > 65)
.count();
let odd_corners = visited
.values()
.filter(|v| **v % 2 == 1 && **v > 65)
.count();
let even_full = visited.values().filter(|v| **v % 2 == 0).count();
let odd_full = visited.values().filter(|v| **v % 2 == 1).count();
let n = ((26501365 - (input.1.len() / 2)) / input.1.len()) as usize;
assert_eq!(n, 202300);
let r = ((n + 1) * (n + 1)) * odd_full + (n * n) * even_full - (n + 1) * odd_corners
+ n * even_corners;
Some(r)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_part_one() {
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
assert_eq!(result, Some(42));
}
#[test]
fn test_part_two() {
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
assert_eq!(result, Some(3314964269439));
}
}