diff --git a/2023/src/bin/22.rs b/2023/src/bin/22.rs index ebaa693..6fc936e 100644 --- a/2023/src/bin/22.rs +++ b/2023/src/bin/22.rs @@ -1,10 +1,116 @@ advent_of_code::solution!(22); -pub fn part_one(input: &str) -> Option { - None +use aoc_parse::{parser, prelude::*}; +use std::collections::{HashMap, HashSet}; + +#[derive(Clone, Copy, Debug, PartialOrd, Ord, Eq, PartialEq)] +struct Coord { + z: usize, + x: usize, + y: usize, } -pub fn part_two(input: &str) -> Option { +#[derive(Debug, Clone, PartialOrd, Ord, Eq, PartialEq)] +struct Block { + id: usize, + one: Coord, + two: Coord, +} + +impl Block { + fn intersect_xy(&self, other: &Self) -> bool { + self.one.x <= other.two.x + && other.one.x <= self.two.x + && self.one.y <= other.two.y + && other.one.y <= self.two.y + } + + fn below(&self, other: &Self) -> bool { + // self.id != other.id && self.intersect_xy(other) && self.two.z == other.one.z - 1 + self.intersect_xy(other) && self.two.z == other.one.z - 1 + } + + fn above(&self, other: &Self) -> bool { + // self.id != other.id && self.intersect_xy(other) && self.one.z == other.two.z + 1 + self.intersect_xy(other) && self.one.z == other.two.z + 1 + } +} + +fn parse(input: &str) -> Vec { + let p = parser!(lines(usize "," usize "," usize "~" usize "," usize "," usize)); + let mut blocks = p + .parse(input) + .unwrap() + .iter() + .enumerate() + .map(|(index, r)| Block { + id: index + 1, + one: Coord { + z: r.2, + x: r.0, + y: r.1, + }, + two: Coord { + z: r.5, + x: r.3, + y: r.4, + }, + }) + .collect::>(); + blocks.sort_by_key(|b| b.one); + blocks +} + +fn drop_blocks(blocks: &[Block]) -> Vec { + let mut btm: Vec = Vec::with_capacity(blocks.len()); + for b in blocks.iter() { + let mut block = b.clone(); + for _i in 1..b.one.z { + if let Some(_below) = btm.iter().find(|d| d.below(&block)) { + break; + } + block.one.z -= 1; + block.two.z -= 1; + } + btm.push(block); + } + btm +} + +fn pyprint(b: &Block) { + println!( + "(({}, {}, {}), ({}, {}, {}))", + b.one.x, b.one.y, b.one.z, b.two.x, b.two.y, b.two.z + ); +} + +pub fn part_one(input_str: &str) -> Option { + let input = parse(input_str); + let btm = drop_blocks(&input); + let mut b: HashMap> = HashMap::new(); + for outside in btm.iter() { + let mut below: Vec = vec![]; + for inside in btm.iter() { + if inside.below(&outside) { + below.push(inside.id); + } + } + b.insert(outside.id, below); + } + let mut s: HashSet = HashSet::new(); + for id in 1..=btm.len() { + if let Some(below) = b.get(&id) { + if below.len() == 1 { + for &x in below { + s.insert(x); + } + } + } + } + Some(btm.len() - s.len()) +} + +pub fn part_two(input_str: &str) -> Option { None } @@ -15,7 +121,7 @@ mod tests { #[test] fn test_part_one() { let result = part_one(&advent_of_code::template::read_file("examples", DAY)); - assert_eq!(result, None); + assert_eq!(result, Some(5)); } #[test] @@ -23,4 +129,62 @@ mod tests { let result = part_two(&advent_of_code::template::read_file("examples", DAY)); assert_eq!(result, None); } + + fn perform_test_intersect( + one: &(usize, usize, usize, usize, usize, usize), + two: &(usize, usize, usize, usize, usize, usize), + res: bool, + ) { + let b1 = Block { + id: 1, + one: Coord { + x: one.0, + y: one.1, + z: one.2, + }, + two: Coord { + x: one.3, + y: one.4, + z: one.5, + }, + }; + let b2 = Block { + id: 2, + one: Coord { + x: two.0, + y: two.1, + z: two.2, + }, + two: Coord { + x: two.3, + y: two.4, + z: two.5, + }, + }; + assert_eq!(b1.intersect_xy(&b2), res); + } + + #[test] + fn test_intersect_xy() { + let false_coords = [ + [(0, 0, 1, 0, 0, 1), (1, 0, 1, 4, 0, 1)], + [(0, 0, 1, 0, 0, 1), (0, 1, 1, 0, 4, 1)], + [(0, 0, 1, 10, 0, 1), (1, 1, 1, 1, 1, 1)], + [(0, 0, 1, 10, 0, 1), (1, 1, 1, 1, 10, 1)], + ]; + for row in false_coords.iter() { + let one = row[0]; + let two = row[1]; + perform_test_intersect(&one, &two, false); + } + let true_coords = [ + [(0, 0, 1, 0, 0, 1), (0, 0, 1, 4, 0, 1)], + [(0, 0, 1, 10, 0, 1), (5, 0, 1, 5, 5, 1)], + ]; + for row in true_coords.iter() { + let one = row[0]; + let two = row[1]; + perform_test_intersect(&one, &two, true); + } + } }