Geen ongedefinieerd gedrag, enkel vergeten dat een plant onderdeel kan zijn van verschillende eigen hekkies

This commit is contained in:
2024-12-17 11:06:07 +01:00
parent 4e3e081db4
commit c972d803a4
4 changed files with 349 additions and 0 deletions

View File

@@ -0,0 +1,193 @@
use std::collections::{HashMap, HashSet};
fn parse(input: &str) -> HashMap<(i64, i64), char> {
let mut result = HashMap::new();
for (j, l) in input.lines().enumerate() {
for (i, c) in l.chars().enumerate() {
result.insert((i as i64, j as i64), c);
}
}
result
}
fn solve_1(input: &str) -> i64 {
let original_map = parse(input);
let mut visited: HashSet<(i64, i64)> = HashSet::new();
let mut result = 0;
for (&(x, y), plant) in original_map.iter() {
if !visited.contains(&(x, y)) {
let mut stack = vec![(x, y)];
let mut area = 0;
let mut perimeter = 0;
visited.insert((x, y));
while !stack.is_empty() {
let cur = stack.pop().unwrap_or_default();
area += 1;
let up = (cur.0, cur.1 - 1);
let right = (cur.0 + 1, cur.1);
let down = (cur.0, cur.1 + 1);
let left = (cur.0 - 1, cur.1);
let candidates = vec![up, right, down, left];
for candidate in candidates {
if let Some(p) = original_map.get(&candidate) {
if p == plant {
if !visited.contains(&candidate) {
stack.push(candidate);
visited.insert(candidate);
}
} else {
perimeter += 1;
}
} else {
perimeter += 1;
}
}
}
result += area * perimeter;
}
}
result
}
fn turn(dir: (i64, i64)) -> (i64, i64) {
match dir {
(0, -1) => (1, 0),
(1, 0) => (0, 1),
(0, 1) => (-1, 0),
(-1, 0) => (0, -1),
_ => unreachable!(),
}
}
fn rev_turn(dir: (i64, i64)) -> (i64, i64) {
turn(turn(turn(dir)))
}
fn solve_2(input: &str) -> u64 {
let original_map = parse(input);
let mut visited: HashSet<(i64, i64)> = HashSet::new();
let mut result = 0;
for (&(x, y), plant) in original_map.iter() {
if !visited.contains(&(x, y)) {
let mut stack = vec![(x, y)];
let mut area = 0;
visited.insert((x, y));
let mut edges = HashSet::new();
while !stack.is_empty() {
let cur = stack.pop().unwrap_or_default();
area += 1;
let up = (0, -1);
let right = (1, 0);
let down = (0, 1);
let left = (-1, 0);
let candidates = vec![up, right, down, left];
for candidate in candidates {
let cand_coord = (cur.0 + candidate.0, cur.1 + candidate.1);
if let Some(p) = original_map.get(&cand_coord) {
if p == plant {
if !visited.contains(&cand_coord) {
stack.push(cand_coord);
visited.insert(cand_coord);
}
} else {
edges.insert((cur, candidate));
}
} else {
edges.insert((cur, candidate));
}
}
}
let mut sides = 0;
while edges.len() > 0 {
let start_edge = edges.iter().next().unwrap().to_owned();
let edge_side = start_edge.1;
let start_dir = turn(edge_side);
let mut dir = start_dir.clone();
let mut cur_edge = start_edge.clone().0;
let mut visited_edges_dir: HashSet<((i64, i64), (i64, i64))> = HashSet::new();
while !visited_edges_dir.contains(&(start_edge.0, start_dir)) {
let next = (cur_edge.0 + dir.0, cur_edge.1 + dir.1);
if let Some(p) = original_map.get(&next) {
if p == plant {
cur_edge = next;
let left = rev_turn(dir);
let left_edge = (cur_edge.0 + left.0, cur_edge.1 + left.1);
if let Some(p2) = original_map.get(&left_edge) {
if p2 == plant {
dir = left;
cur_edge = left_edge;
sides += 1;
}
}
} else {
dir = turn(dir);
sides += 1;
}
} else {
dir = turn(dir);
sides += 1;
}
edges.remove(&(cur_edge, rev_turn(dir)));
visited_edges_dir.insert((cur_edge, dir));
}
}
result += area * sides;
}
}
result
}
fn main() {
println!("Hello, this is Patrick!");
let input = include_str!("../input.txt");
let result_1 = solve_1(input);
println!("The total price of all fencing is {}", result_1);
let result_2 = solve_2(input);
println!("The new total price of all fencing is {}", result_2);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_1() {
let test_input = include_str!("../test.txt");
assert_eq!(solve_1(test_input), 1930);
}
#[test]
fn test_2() {
let test_input = include_str!("../test.txt");
assert_eq!(solve_2(test_input), 1206);
}
}