AdventOfCode/y2024/src/days/d10.rs
2024-12-10 10:32:05 +01:00

333 lines
8.8 KiB
Rust

use itertools::Itertools;
#[derive(Debug, Clone, Copy, Default, Hash, PartialEq, Eq)]
struct TrailTile {
coords: (i32, i32),
height: u8,
up: Option<bool>,
down: Option<bool>,
left: Option<bool>,
right: Option<bool>,
}
impl TrailTile {
fn get_neighbors(&self, map: &[Vec<TrailTile>]) -> Vec<TrailTile> {
let mut neighbors = Vec::new();
if let Some(up) = self.up {
if up {
let neighbor = map[self.coords.1 as usize - 1][self.coords.0 as usize];
if neighbor.height == self.height + 1 {
neighbors.push(neighbor);
}
}
}
if let Some(down) = self.down {
if down {
let neighbor = map[self.coords.1 as usize + 1][self.coords.0 as usize];
if neighbor.height == self.height + 1 {
neighbors.push(neighbor);
}
}
}
if let Some(left) = self.left {
if left {
let neighbor = map[self.coords.1 as usize][self.coords.0 as usize - 1];
if neighbor.height == self.height + 1 {
neighbors.push(neighbor);
}
}
}
if let Some(right) = self.right {
if right {
let neighbor = map[self.coords.1 as usize][self.coords.0 as usize + 1];
if neighbor.height == self.height + 1 {
neighbors.push(neighbor);
}
}
}
neighbors
}
}
pub fn process_part1(input: &str) -> i32 {
let map = input
.lines()
.enumerate()
.map(|(yidx, line)| {
line.as_bytes()
.iter()
.enumerate()
.map(|(xidx, &height)| TrailTile {
coords: (xidx as i32, yidx as i32),
height,
..Default::default()
})
.collect_vec()
})
.collect_vec();
let mut updated_map = Vec::new();
let mut trailheads = Vec::new();
for row in map.iter() {
let mut new_row = Vec::new();
for tile in row {
let up = match map.get((tile.coords.1 - 1) as usize) {
Some(row) => {
let up = row[tile.coords.0 as usize];
Some(up.height == tile.height + 1)
}
None => None,
};
let down = match map.get((tile.coords.1 + 1) as usize) {
Some(row) => {
let down = row[tile.coords.0 as usize];
Some(down.height == tile.height + 1)
}
None => None,
};
let left = map[tile.coords.1 as usize]
.get((tile.coords.0 - 1) as usize)
.map(|left| left.height == tile.height + 1);
let right = map[tile.coords.1 as usize]
.get((tile.coords.0 + 1) as usize)
.map(|right| right.height == tile.height + 1);
let tile = TrailTile {
up,
down,
left,
right,
..*tile
};
if tile.height == b'0' {
trailheads.push(tile);
}
//print!(
// "{}: {}, {} ",
// tile.height as char, tile.coords.0, tile.coords.1
//);
new_row.push(tile);
}
//println!();
updated_map.push(new_row);
}
let mut res = 0;
//println!("0 {trailheads:?}");
for trailhead in trailheads {
let mut neighbors = trailhead.get_neighbors(&updated_map);
//println!("1 {neighbors:?}");
for _idx in 2..=9 {
//println!("{idx}");
if neighbors.is_empty() {
//println!("empty");
break;
}
let inner_neighbors = neighbors.clone();
neighbors.clear();
for neighbor in inner_neighbors {
neighbors.extend_from_slice(&neighbor.get_neighbors(&updated_map));
}
//println!("{idx} {neighbors:?}");
}
res += neighbors.iter().unique().count();
}
res as i32
}
pub fn process_part2(input: &str) -> i32 {
let map = input
.lines()
.enumerate()
.map(|(yidx, line)| {
line.as_bytes()
.iter()
.enumerate()
.map(|(xidx, &height)| TrailTile {
coords: (xidx as i32, yidx as i32),
height,
..Default::default()
})
.collect_vec()
})
.collect_vec();
let mut updated_map = Vec::new();
let mut trailheads = Vec::new();
for row in map.iter() {
let mut new_row = Vec::new();
for tile in row {
let up = match map.get((tile.coords.1 - 1) as usize) {
Some(row) => {
let up = row[tile.coords.0 as usize];
Some(up.height == tile.height + 1)
}
None => None,
};
let down = match map.get((tile.coords.1 + 1) as usize) {
Some(row) => {
let down = row[tile.coords.0 as usize];
Some(down.height == tile.height + 1)
}
None => None,
};
let left = map[tile.coords.1 as usize]
.get((tile.coords.0 - 1) as usize)
.map(|left| left.height == tile.height + 1);
let right = map[tile.coords.1 as usize]
.get((tile.coords.0 + 1) as usize)
.map(|right| right.height == tile.height + 1);
let tile = TrailTile {
up,
down,
left,
right,
..*tile
};
if tile.height == b'0' {
trailheads.push(tile);
}
//print!(
// "{}: {}, {} ",
// tile.height as char, tile.coords.0, tile.coords.1
//);
new_row.push(tile);
}
//println!();
updated_map.push(new_row);
}
let mut res = 0;
//println!("0 {trailheads:?}");
for trailhead in trailheads {
let mut neighbors = trailhead.get_neighbors(&updated_map);
//println!("1 {neighbors:?}");
for _idx in 2..=9 {
//println!("{idx}");
if neighbors.is_empty() {
//println!("empty");
break;
}
let inner_neighbors = neighbors.clone();
neighbors.clear();
for neighbor in inner_neighbors {
neighbors.extend_from_slice(&neighbor.get_neighbors(&updated_map));
}
//println!("{idx} {neighbors:?}");
}
res += neighbors.len();
}
res as i32
}
#[cfg(test)]
mod tests {
use super::*;
const INPUT_SMALL: &str = "0123
1234
8765
9876";
const INPUT_LARGE: &str = "89010123
78121874
87430965
96549874
45678903
32019012
01329801
10456732";
const INPUT_SIMPLIFIED_1: &str = "...0...
...1...
...2...
6543456
7.....7
8.....8
9.....9";
const INPUT_SIMPLIFIED_2: &str = "..90..9
...1.98
...2..7
6543456
765.987
876....
987....";
const INPUT_SIMPLIFIED_3: &str = "10..9..
2...8..
3...7..
4567654
...8..3
...9..2
.....01";
const INPUT_SIMPLIFIED_4: &str = ".....0.
..4321.
..5..2.
..6543.
..7..4.
..8765.
..9....";
const INPUT_SIMPLIFIED_5: &str = "012345
123456
234567
345678
4.6789
56789.";
#[test]
fn part1_small() {
let result = process_part1(INPUT_SMALL);
assert_eq!(result, 1);
}
#[test]
fn part1_large() {
let result = process_part1(INPUT_LARGE);
assert_eq!(result, 36);
}
#[test]
fn part1_simplified_1() {
let result = process_part1(INPUT_SIMPLIFIED_1);
assert_eq!(result, 2);
}
#[test]
fn part1_simplified_2() {
let result = process_part1(INPUT_SIMPLIFIED_2);
assert_eq!(result, 4);
}
#[test]
fn part1_simplified_3() {
let result = process_part1(INPUT_SIMPLIFIED_3);
assert_eq!(result, 3);
}
#[test]
fn part2_small() {
let result = process_part2(INPUT_SMALL);
assert_eq!(result, 16);
}
#[test]
fn part2_large() {
let result = process_part2(INPUT_LARGE);
assert_eq!(result, 81);
}
#[test]
fn part2_simplified_2() {
let result = process_part2(INPUT_SIMPLIFIED_2);
assert_eq!(result, 13);
}
#[test]
fn part2_simplified_4() {
let result = process_part2(INPUT_SIMPLIFIED_4);
assert_eq!(result, 3);
}
#[test]
fn part2_simplified_5() {
let result = process_part2(INPUT_SIMPLIFIED_5);
assert_eq!(result, 227);
}
}