day22: problem 1 solution
This commit is contained in:
		
							
								
								
									
										89
									
								
								22/Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								22/Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
				
			|||||||
 | 
					# This file is automatically @generated by Cargo.
 | 
				
			||||||
 | 
					# It is not intended for manual editing.
 | 
				
			||||||
 | 
					version = 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "autocfg"
 | 
				
			||||||
 | 
					version = "1.1.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "day22"
 | 
				
			||||||
 | 
					version = "0.1.0"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "itertools",
 | 
				
			||||||
 | 
					 "ndarray",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "either"
 | 
				
			||||||
 | 
					version = "1.9.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "itertools"
 | 
				
			||||||
 | 
					version = "0.12.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "either",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "matrixmultiply"
 | 
				
			||||||
 | 
					version = "0.3.8"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "autocfg",
 | 
				
			||||||
 | 
					 "rawpointer",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "ndarray"
 | 
				
			||||||
 | 
					version = "0.15.6"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "matrixmultiply",
 | 
				
			||||||
 | 
					 "num-complex",
 | 
				
			||||||
 | 
					 "num-integer",
 | 
				
			||||||
 | 
					 "num-traits",
 | 
				
			||||||
 | 
					 "rawpointer",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "num-complex"
 | 
				
			||||||
 | 
					version = "0.4.4"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "num-traits",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "num-integer"
 | 
				
			||||||
 | 
					version = "0.1.45"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "autocfg",
 | 
				
			||||||
 | 
					 "num-traits",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "num-traits"
 | 
				
			||||||
 | 
					version = "0.2.17"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "autocfg",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "rawpointer"
 | 
				
			||||||
 | 
					version = "0.2.1"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
 | 
				
			||||||
							
								
								
									
										10
									
								
								22/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								22/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					[package]
 | 
				
			||||||
 | 
					name = "day22"
 | 
				
			||||||
 | 
					version = "0.1.0"
 | 
				
			||||||
 | 
					edition = "2021"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[dependencies]
 | 
				
			||||||
 | 
					itertools = "0.12.0"
 | 
				
			||||||
 | 
					ndarray = "0.15.6"
 | 
				
			||||||
							
								
								
									
										386
									
								
								22/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										386
									
								
								22/src/main.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,386 @@
 | 
				
			|||||||
 | 
					use itertools::Itertools;
 | 
				
			||||||
 | 
					use ndarray::prelude::*;
 | 
				
			||||||
 | 
					use std::borrow::BorrowMut;
 | 
				
			||||||
 | 
					use std::collections::{BinaryHeap, HashMap, VecDeque};
 | 
				
			||||||
 | 
					use std::fmt::{Display, Write};
 | 
				
			||||||
 | 
					use std::fs::File;
 | 
				
			||||||
 | 
					use std::io::{BufRead, BufReader, Lines};
 | 
				
			||||||
 | 
					use std::str::FromStr;
 | 
				
			||||||
 | 
					use std::time::Instant;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// BOILERPLATE
 | 
				
			||||||
 | 
					type InputIter = Lines<BufReader<File>>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn get_input() -> InputIter {
 | 
				
			||||||
 | 
					    let f = File::open("input").unwrap();
 | 
				
			||||||
 | 
					    let br = BufReader::new(f);
 | 
				
			||||||
 | 
					    br.lines()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn main() {
 | 
				
			||||||
 | 
					    let start = Instant::now();
 | 
				
			||||||
 | 
					    let ans1 = problem1(get_input());
 | 
				
			||||||
 | 
					    let duration = start.elapsed();
 | 
				
			||||||
 | 
					    println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let start = Instant::now();
 | 
				
			||||||
 | 
					    let ans2 = problem2(get_input());
 | 
				
			||||||
 | 
					    let duration = start.elapsed();
 | 
				
			||||||
 | 
					    println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PARSE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Hash, PartialEq, Eq, Clone, Debug)]
 | 
				
			||||||
 | 
					struct Coord {
 | 
				
			||||||
 | 
					    x: usize,
 | 
				
			||||||
 | 
					    y: usize,
 | 
				
			||||||
 | 
					    z: usize,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl FromStr for Coord {
 | 
				
			||||||
 | 
					    type Err = Box<dyn std::error::Error>;
 | 
				
			||||||
 | 
					    fn from_str(value: &str) -> Result<Self, Self::Err> {
 | 
				
			||||||
 | 
					        let (x, y, z) = value.split(',').next_tuple().unwrap();
 | 
				
			||||||
 | 
					        Ok(Self {
 | 
				
			||||||
 | 
					            x: x.parse()?,
 | 
				
			||||||
 | 
					            y: y.parse()?,
 | 
				
			||||||
 | 
					            z: z.parse()?,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone, Hash, PartialEq, Eq)]
 | 
				
			||||||
 | 
					struct BrickBlock {
 | 
				
			||||||
 | 
					    c1: Coord,
 | 
				
			||||||
 | 
					    c2: Coord,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl std::fmt::Debug for BrickBlock {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
				
			||||||
 | 
					        writeln!(
 | 
				
			||||||
 | 
					            f,
 | 
				
			||||||
 | 
					            "[{},{},{} - {},{},{}]",
 | 
				
			||||||
 | 
					            self.c1.x, self.c1.y, self.c1.z, self.c2.x, self.c2.y, self.c2.z
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl BrickBlock {
 | 
				
			||||||
 | 
					    fn map_remove(&self, mut map: BlockMap) -> BlockMap {
 | 
				
			||||||
 | 
					        // loop over the (inclusive) bounding coordinates and remove them all from the map
 | 
				
			||||||
 | 
					        map.slice_mut(s![
 | 
				
			||||||
 | 
					            std::cmp::min(self.c1.x, self.c2.x)..std::cmp::max(self.c1.x, self.c2.x) + 1,
 | 
				
			||||||
 | 
					            std::cmp::min(self.c1.y, self.c2.y)..std::cmp::max(self.c1.y, self.c2.y) + 1,
 | 
				
			||||||
 | 
					            std::cmp::min(self.c1.z, self.c2.z)..std::cmp::max(self.c1.z, self.c2.z) + 1
 | 
				
			||||||
 | 
					        ])
 | 
				
			||||||
 | 
					        .fill(None);
 | 
				
			||||||
 | 
					        map
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn map_into(&self, mut map: BlockMap) -> BlockMap {
 | 
				
			||||||
 | 
					        // loop over the (inclusive) bounding coordinates and add them all to the map
 | 
				
			||||||
 | 
					        map.slice_mut(s![
 | 
				
			||||||
 | 
					            std::cmp::min(self.c1.x, self.c2.x)..std::cmp::max(self.c1.x, self.c2.x) + 1,
 | 
				
			||||||
 | 
					            std::cmp::min(self.c1.y, self.c2.y)..std::cmp::max(self.c1.y, self.c2.y) + 1,
 | 
				
			||||||
 | 
					            std::cmp::min(self.c1.z, self.c2.z)..std::cmp::max(self.c1.z, self.c2.z) + 1
 | 
				
			||||||
 | 
					        ])
 | 
				
			||||||
 | 
					        .fill(Some(self.to_owned()));
 | 
				
			||||||
 | 
					        map
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn bottom_z_plane(&self) -> usize {
 | 
				
			||||||
 | 
					        std::cmp::min(self.c1.z, self.c2.z)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn top_z_plane(&self) -> usize {
 | 
				
			||||||
 | 
					        std::cmp::max(self.c1.z, self.c2.z)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn bottom_x_plane(&self) -> usize {
 | 
				
			||||||
 | 
					        std::cmp::min(self.c1.x, self.c2.x)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn top_x_plane(&self) -> usize {
 | 
				
			||||||
 | 
					        std::cmp::max(self.c1.x, self.c2.x)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn bottom_y_plane(&self) -> usize {
 | 
				
			||||||
 | 
					        std::cmp::min(self.c1.y, self.c2.y)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn top_y_plane(&self) -> usize {
 | 
				
			||||||
 | 
					        std::cmp::max(self.c1.y, self.c2.y)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<&str> for BrickBlock {
 | 
				
			||||||
 | 
					    fn from(value: &str) -> Self {
 | 
				
			||||||
 | 
					        let (c1, c2) = value.split_once('~').unwrap();
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            c1: c1.parse().unwrap(),
 | 
				
			||||||
 | 
					            c2: c2.parse().unwrap(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl PartialOrd for BrickBlock {
 | 
				
			||||||
 | 
					    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
 | 
				
			||||||
 | 
					        Some(other.bottom_z_plane().cmp(&self.bottom_z_plane()))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Note this is a reversed ordering
 | 
				
			||||||
 | 
					impl Ord for BrickBlock {
 | 
				
			||||||
 | 
					    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
 | 
				
			||||||
 | 
					        self.bottom_z_plane().cmp(&other.bottom_z_plane())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type BlockMap = Array3<MapTile>;
 | 
				
			||||||
 | 
					type MapTile = Option<BrickBlock>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct BlockPile {
 | 
				
			||||||
 | 
					    blocks: Vec<BrickBlock>,
 | 
				
			||||||
 | 
					    block_map: Array3<MapTile>,
 | 
				
			||||||
 | 
					    bounds: (usize, usize, usize),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl BlockPile {
 | 
				
			||||||
 | 
					    fn remove_block(&mut self, block: &BrickBlock) {
 | 
				
			||||||
 | 
					        // loop over the (inclusive) bounding coordinates and remove them all from the map
 | 
				
			||||||
 | 
					        self.block_map
 | 
				
			||||||
 | 
					            .slice_mut(s![
 | 
				
			||||||
 | 
					                std::cmp::min(block.c1.x, block.c2.x)..std::cmp::max(block.c1.x, block.c2.x) + 1,
 | 
				
			||||||
 | 
					                std::cmp::min(block.c1.y, block.c2.y)..std::cmp::max(block.c1.y, block.c2.y) + 1,
 | 
				
			||||||
 | 
					                std::cmp::min(block.c1.z, block.c2.z)..std::cmp::max(block.c1.z, block.c2.z) + 1
 | 
				
			||||||
 | 
					            ])
 | 
				
			||||||
 | 
					            .fill(None);
 | 
				
			||||||
 | 
					        self.blocks.remove(self.blocks.iter().position(|b| b == block).unwrap());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn add_block(&mut self, block: &BrickBlock) {
 | 
				
			||||||
 | 
					        // loop over the (inclusive) bounding coordinates and remove them all from the map
 | 
				
			||||||
 | 
					        self.block_map
 | 
				
			||||||
 | 
					            .slice_mut(s![
 | 
				
			||||||
 | 
					                std::cmp::min(block.c1.x, block.c2.x)..std::cmp::max(block.c1.x, block.c2.x) + 1,
 | 
				
			||||||
 | 
					                std::cmp::min(block.c1.y, block.c2.y)..std::cmp::max(block.c1.y, block.c2.y) + 1,
 | 
				
			||||||
 | 
					                std::cmp::min(block.c1.z, block.c2.z)..std::cmp::max(block.c1.z, block.c2.z) + 1
 | 
				
			||||||
 | 
					            ])
 | 
				
			||||||
 | 
					            .fill(Some(block.to_owned()));
 | 
				
			||||||
 | 
					        self.blocks.push(block.clone());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn blocks_directly_above(&self, block: &BrickBlock) -> Vec<BrickBlock> {
 | 
				
			||||||
 | 
					        // find the top plane of the block
 | 
				
			||||||
 | 
					        // get the array range of all squares at z_plane + 1 within the bounds of x & y
 | 
				
			||||||
 | 
					        // i think there is a built in way in ndarray to do this...
 | 
				
			||||||
 | 
					        let directly_above = self.block_map.slice(s![
 | 
				
			||||||
 | 
					            block.bottom_x_plane()..block.top_x_plane() + 1,
 | 
				
			||||||
 | 
					            block.bottom_y_plane()..block.top_y_plane() + 1,
 | 
				
			||||||
 | 
					            block.top_z_plane() + 1..std::cmp::min(block.top_z_plane() + 2, self.bounds.2)
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        directly_above.iter().filter_map(|v| v.clone()).dedup().collect()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn supported_by(&self, block: &BrickBlock) -> usize {
 | 
				
			||||||
 | 
					        println!("{:?} is supported by:", block);
 | 
				
			||||||
 | 
					        let z_plane = std::cmp::min(block.c1.z, block.c2.z);
 | 
				
			||||||
 | 
					        println!(" z plane: {}", z_plane);
 | 
				
			||||||
 | 
					        // get the slice of tiles below us
 | 
				
			||||||
 | 
					        let directly_below = self.block_map.slice(s![
 | 
				
			||||||
 | 
					            block.bottom_x_plane()..block.top_x_plane() + 1,
 | 
				
			||||||
 | 
					            block.bottom_y_plane()..block.top_y_plane() + 1,
 | 
				
			||||||
 | 
					            z_plane - 1..z_plane // don't include our own plane
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					        println!(
 | 
				
			||||||
 | 
					            "  {} directly below {:?}",
 | 
				
			||||||
 | 
					            directly_below.iter().filter_map(|v| v.clone()).dedup().count(),
 | 
				
			||||||
 | 
					            block
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        directly_below.iter().filter_map(|v| v.clone()).dedup().count()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn blocks_above_will_move_if_we_are_gone(&mut self, block: &BrickBlock) -> bool {
 | 
				
			||||||
 | 
					        println!("checking directly above block {:?}", block);
 | 
				
			||||||
 | 
					        self.blocks_directly_above(&block)
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .map(|b| self.supported_by(b))
 | 
				
			||||||
 | 
					            .any(|b| b == 1) // 0 can happen at the origin, and an empty iterator (falsy) will happen at the top
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /// Find the plane of the first block directly below us
 | 
				
			||||||
 | 
					    fn supporting_plane(&self, block: &BrickBlock) -> Option<usize> {
 | 
				
			||||||
 | 
					        // find the bottom plane of ourselves
 | 
				
			||||||
 | 
					        let z_plane = std::cmp::min(block.c1.z, block.c2.z);
 | 
				
			||||||
 | 
					        // get the slice of tiles below us
 | 
				
			||||||
 | 
					        let directly_below = self.block_map.slice(s![
 | 
				
			||||||
 | 
					            block.bottom_x_plane()..block.top_x_plane() + 1,
 | 
				
			||||||
 | 
					            block.bottom_y_plane()..block.top_y_plane() + 1,
 | 
				
			||||||
 | 
					            1..z_plane // don't include our own plane
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					        // find the highest z value
 | 
				
			||||||
 | 
					        let block_below = directly_below
 | 
				
			||||||
 | 
					            .indexed_iter()
 | 
				
			||||||
 | 
					            .filter_map(|(idx, v)| if let Some(val) = v { Some((idx, val)) } else { None })
 | 
				
			||||||
 | 
					            .max_by(|(_, a), (_, b)| a.top_z_plane().cmp(&b.top_z_plane()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if let Some(block) = block_below {
 | 
				
			||||||
 | 
					            Some(block.1.top_z_plane())
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            None
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn drop_blocks(&mut self) {
 | 
				
			||||||
 | 
					        let mut blocks_to_move = BinaryHeap::from(self.blocks.clone());
 | 
				
			||||||
 | 
					        while let Some(mut block) = blocks_to_move.pop() {
 | 
				
			||||||
 | 
					            match self.supporting_plane(&block) {
 | 
				
			||||||
 | 
					                Some(z) if z + 1 != block.bottom_z_plane() => {
 | 
				
			||||||
 | 
					                    // we can move down to this position
 | 
				
			||||||
 | 
					                    // set old position to None
 | 
				
			||||||
 | 
					                    self.remove_block(&block);
 | 
				
			||||||
 | 
					                    // set new positions
 | 
				
			||||||
 | 
					                    let z_height = block.c1.z.abs_diff(block.c2.z);
 | 
				
			||||||
 | 
					                    block.c1.z = z + 1;
 | 
				
			||||||
 | 
					                    block.c2.z = z + 1 + z_height;
 | 
				
			||||||
 | 
					                    self.add_block(&block);
 | 
				
			||||||
 | 
					                    // queue the block again
 | 
				
			||||||
 | 
					                    blocks_to_move.push(block);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                None if block.bottom_z_plane() != 1 => {
 | 
				
			||||||
 | 
					                    // we can move down to this position
 | 
				
			||||||
 | 
					                    // set old position to None
 | 
				
			||||||
 | 
					                    let z = 1;
 | 
				
			||||||
 | 
					                    self.remove_block(&block);
 | 
				
			||||||
 | 
					                    // set new positions
 | 
				
			||||||
 | 
					                    let z_height = block.c1.z.abs_diff(block.c2.z);
 | 
				
			||||||
 | 
					                    block.c1.z = z;
 | 
				
			||||||
 | 
					                    block.c2.z = z + z_height;
 | 
				
			||||||
 | 
					                    self.add_block(&block);
 | 
				
			||||||
 | 
					                    // queue the block again
 | 
				
			||||||
 | 
					                    blocks_to_move.push(block);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                _ => {
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                } // we are in position already with nothing below us
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn can_move(&self, block: &BrickBlock) -> bool {
 | 
				
			||||||
 | 
					        match self.supporting_plane(&block) {
 | 
				
			||||||
 | 
					            Some(z) if z + 1 != block.bottom_z_plane() => {
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            None if block.bottom_z_plane() != 1 => {
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            _ => {
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            } // we are in position already with nothing below us
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T: BufRead> From<Lines<T>> for BlockPile {
 | 
				
			||||||
 | 
					    fn from(lines: Lines<T>) -> Self {
 | 
				
			||||||
 | 
					        let mut new: BlockPile = Self {
 | 
				
			||||||
 | 
					            blocks: lines.map(|line| BrickBlock::from(line.unwrap().as_str())).collect(),
 | 
				
			||||||
 | 
					            block_map: Array3::from_elem([0, 0, 0], None),
 | 
				
			||||||
 | 
					            bounds: (0, 0, 0),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for block in &new.blocks {
 | 
				
			||||||
 | 
					            new.bounds.0 = std::cmp::max(block.c1.x + 1, std::cmp::max(block.c2.y + 1, new.bounds.0));
 | 
				
			||||||
 | 
					            new.bounds.1 = std::cmp::max(block.c1.y + 1, std::cmp::max(block.c2.y + 1, new.bounds.1));
 | 
				
			||||||
 | 
					            new.bounds.2 = std::cmp::max(block.c1.z + 1, std::cmp::max(block.c2.z + 1, new.bounds.2));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let mut block_map = BlockMap::from_elem(new.bounds, None);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for block in &new.blocks {
 | 
				
			||||||
 | 
					            block_map = block.map_into(block_map);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        new.block_map = block_map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        new
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Display for BlockPile {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
				
			||||||
 | 
					        // XZ view
 | 
				
			||||||
 | 
					        writeln!(f, "XZ: [{}, {}]", self.bounds.0, self.bounds.2)?;
 | 
				
			||||||
 | 
					        for z in (0..self.bounds.2).rev() {
 | 
				
			||||||
 | 
					            for x in 0..self.bounds.0 {
 | 
				
			||||||
 | 
					                let y = self.block_map.slice(s![x..x + 1, .., z..z + 1]);
 | 
				
			||||||
 | 
					                f.write_char(match y.iter().filter(|v| v.is_some()).count() {
 | 
				
			||||||
 | 
					                    0 => '.',
 | 
				
			||||||
 | 
					                    1 => '#',
 | 
				
			||||||
 | 
					                    _ => '?',
 | 
				
			||||||
 | 
					                })?;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            writeln!(f, " {}", z)?;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // YZ view
 | 
				
			||||||
 | 
					        writeln!(f)?;
 | 
				
			||||||
 | 
					        writeln!(f, "YZ: [{}, {}]", self.bounds.1, self.bounds.2)?;
 | 
				
			||||||
 | 
					        for z in (0..self.bounds.2).rev() {
 | 
				
			||||||
 | 
					            for y in 0..self.bounds.1 {
 | 
				
			||||||
 | 
					                let x = self.block_map.slice(s![.., y..y + 1, z..z + 1]);
 | 
				
			||||||
 | 
					                f.write_char(match x.iter().filter(|v| v.is_some()).count() {
 | 
				
			||||||
 | 
					                    0 => '.',
 | 
				
			||||||
 | 
					                    1 => '#',
 | 
				
			||||||
 | 
					                    _ => '?',
 | 
				
			||||||
 | 
					                })?;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            writeln!(f, " {}", z)?;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PROBLEM 1 solution
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
 | 
				
			||||||
 | 
					    let mut pile = BlockPile::from(input);
 | 
				
			||||||
 | 
					    println!("{}", pile);
 | 
				
			||||||
 | 
					    println!("dropping blocks!");
 | 
				
			||||||
 | 
					    pile.drop_blocks();
 | 
				
			||||||
 | 
					    println!("{}", pile);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let blocks = pile.blocks.clone();
 | 
				
			||||||
 | 
					    let mut must_keep: Vec<_> = blocks
 | 
				
			||||||
 | 
					        .iter()
 | 
				
			||||||
 | 
					        .filter(|b| pile.blocks_above_will_move_if_we_are_gone(*b))
 | 
				
			||||||
 | 
					        .collect();
 | 
				
			||||||
 | 
					    let mut removable: Vec<_> = blocks
 | 
				
			||||||
 | 
					        .iter()
 | 
				
			||||||
 | 
					        .filter(|b| !pile.blocks_above_will_move_if_we_are_gone(*b))
 | 
				
			||||||
 | 
					        .collect();
 | 
				
			||||||
 | 
					    removable.sort_by(|a, b| a.bottom_z_plane().cmp(&b.bottom_z_plane()));
 | 
				
			||||||
 | 
					    must_keep.sort_by(|a, b| a.bottom_z_plane().cmp(&b.bottom_z_plane()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    println!("remove: {:?}", removable);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    removable.len() as u64
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PROBLEM 2 solution
 | 
				
			||||||
 | 
					fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
 | 
				
			||||||
 | 
					    0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
					    use crate::*;
 | 
				
			||||||
 | 
					    use std::io::Cursor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const EXAMPLE: &str = &"1,0,1~1,2,1
 | 
				
			||||||
 | 
					0,0,2~2,0,2
 | 
				
			||||||
 | 
					0,2,3~2,2,3
 | 
				
			||||||
 | 
					0,0,4~0,2,4
 | 
				
			||||||
 | 
					2,0,5~2,2,5
 | 
				
			||||||
 | 
					0,1,6~2,1,6
 | 
				
			||||||
 | 
					1,1,8~1,1,9";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn problem1_example() {
 | 
				
			||||||
 | 
					        let c = Cursor::new(EXAMPLE);
 | 
				
			||||||
 | 
					        assert_eq!(problem1(c.lines()), 5);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn problem2_example() {
 | 
				
			||||||
 | 
					        let c = Cursor::new(EXAMPLE);
 | 
				
			||||||
 | 
					        assert_eq!(problem2(c.lines()), 0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user