day22: problem 2 solution.
no help but annoyingly i misread the hint about low/high and didn't submit my *correct* answer for over an hour while I bug hunted!
This commit is contained in:
		
							
								
								
									
										157
									
								
								22/src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										157
									
								
								22/src/main.rs
									
									
									
									
									
								
							@@ -1,7 +1,8 @@
 | 
			
		||||
use itertools::Itertools;
 | 
			
		||||
use ndarray::prelude::*;
 | 
			
		||||
use std::borrow::BorrowMut;
 | 
			
		||||
use std::collections::{BinaryHeap, HashMap, VecDeque};
 | 
			
		||||
use petgraph::prelude::*;
 | 
			
		||||
use petgraph::visit::{IntoNodeReferences, Walker};
 | 
			
		||||
use std::collections::BinaryHeap;
 | 
			
		||||
use std::fmt::{Display, Write};
 | 
			
		||||
use std::fs::File;
 | 
			
		||||
use std::io::{BufRead, BufReader, Lines};
 | 
			
		||||
@@ -67,16 +68,6 @@ impl std::fmt::Debug for BrickBlock {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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![
 | 
			
		||||
@@ -137,6 +128,7 @@ struct BlockPile {
 | 
			
		||||
    blocks: Vec<BrickBlock>,
 | 
			
		||||
    block_map: Array3<MapTile>,
 | 
			
		||||
    bounds: (usize, usize, usize),
 | 
			
		||||
    graph: Graph<BrickBlock, (), Directed, usize>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BlockPile {
 | 
			
		||||
@@ -172,31 +164,26 @@ impl BlockPile {
 | 
			
		||||
            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()
 | 
			
		||||
        directly_above.iter().filter_map(|v| v.clone()).unique().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
 | 
			
		||||
            z_plane - 1..z_plane // the layer below
 | 
			
		||||
        ]);
 | 
			
		||||
        println!(
 | 
			
		||||
            "  {} directly below {:?}",
 | 
			
		||||
            directly_below.iter().filter_map(|v| v.clone()).dedup().count(),
 | 
			
		||||
            block
 | 
			
		||||
        );
 | 
			
		||||
        directly_below.iter().filter_map(|v| v.clone()).dedup().count()
 | 
			
		||||
        directly_below.iter().filter_map(|v| v.clone()).unique().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
 | 
			
		||||
            .any(|b| b == 1) // block we support will move if we are their only support
 | 
			
		||||
    }
 | 
			
		||||
    fn blocks_supported_by_at_all(&self, block: &BrickBlock) -> Vec<BrickBlock> {
 | 
			
		||||
        self.blocks_directly_above(&block).iter().map(|b| b.clone()).collect()
 | 
			
		||||
    }
 | 
			
		||||
    /// Find the plane of the first block directly below us
 | 
			
		||||
    fn supporting_plane(&self, block: &BrickBlock) -> Option<usize> {
 | 
			
		||||
@@ -222,51 +209,38 @@ impl BlockPile {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn drop_blocks(&mut self) {
 | 
			
		||||
        // VecDeque doesn't sort and Vec isn't convenient for pushback and popfront, so eh... use a heap.
 | 
			
		||||
        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);
 | 
			
		||||
                }
 | 
			
		||||
            let z_move = match self.supporting_plane(&block) {
 | 
			
		||||
                Some(z) if z + 1 != block.bottom_z_plane() => block.bottom_z_plane() - (z + 1),
 | 
			
		||||
                None if block.bottom_z_plane() != 1 => block.bottom_z_plane() - 1,
 | 
			
		||||
                _ => {
 | 
			
		||||
                    continue;
 | 
			
		||||
                } // we are in position already with nothing below us
 | 
			
		||||
            }
 | 
			
		||||
            };
 | 
			
		||||
            self.remove_block(&block);
 | 
			
		||||
            block.c1.z -= z_move;
 | 
			
		||||
            block.c2.z -= z_move;
 | 
			
		||||
            self.add_block(&block);
 | 
			
		||||
            blocks_to_move.push(block);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn can_move(&self, block: &BrickBlock) -> bool {
 | 
			
		||||
        match self.supporting_plane(&block) {
 | 
			
		||||
            Some(z) if z + 1 != block.bottom_z_plane() => {
 | 
			
		||||
                return true;
 | 
			
		||||
    fn build_graph(&mut self) {
 | 
			
		||||
        self.blocks.sort_by_key(|b| b.bottom_z_plane());
 | 
			
		||||
        for b in 0..self.blocks.len() {
 | 
			
		||||
            self.graph.add_node(self.blocks[b].clone());
 | 
			
		||||
        }
 | 
			
		||||
        for b in 0..self.blocks.len() {
 | 
			
		||||
            let block = &self.blocks[b];
 | 
			
		||||
            let depends_on_us = self.blocks_supported_by_at_all(block);
 | 
			
		||||
            for dependent in depends_on_us {
 | 
			
		||||
                self.graph.add_edge(
 | 
			
		||||
                    b.into(),
 | 
			
		||||
                    self.blocks.iter().position(|b| b == &dependent).unwrap().into(),
 | 
			
		||||
                    (),
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
            None if block.bottom_z_plane() != 1 => {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            _ => {
 | 
			
		||||
                return false;
 | 
			
		||||
            } // we are in position already with nothing below us
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -277,6 +251,7 @@ impl<T: BufRead> From<Lines<T>> for BlockPile {
 | 
			
		||||
            blocks: lines.map(|line| BrickBlock::from(line.unwrap().as_str())).collect(),
 | 
			
		||||
            block_map: Array3::from_elem([0, 0, 0], None),
 | 
			
		||||
            bounds: (0, 0, 0),
 | 
			
		||||
            graph: Graph::default(),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        for block in &new.blocks {
 | 
			
		||||
@@ -338,25 +313,63 @@ fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
 | 
			
		||||
    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
 | 
			
		||||
    let 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
 | 
			
		||||
    let mut pile = BlockPile::from(input);
 | 
			
		||||
    pile.drop_blocks();
 | 
			
		||||
    pile.build_graph();
 | 
			
		||||
 | 
			
		||||
    println!("{}", pile);
 | 
			
		||||
    println!("block 0 {:?}", pile.blocks[0]);
 | 
			
		||||
 | 
			
		||||
    let mut accum = 0;
 | 
			
		||||
    let fixed_nodes = pile
 | 
			
		||||
        .graph
 | 
			
		||||
        .node_references()
 | 
			
		||||
        .filter(|(_idx, b)| b.bottom_z_plane() == 1)
 | 
			
		||||
        .map(|(idx, _b)| idx)
 | 
			
		||||
        .collect_vec();
 | 
			
		||||
    for node in pile.graph.node_indices() {
 | 
			
		||||
        // remove links to node's neighbors
 | 
			
		||||
        let dependents = pile.graph.neighbors(node).collect_vec();
 | 
			
		||||
        let edges = pile.graph.edges(node).map(|v| v.id()).collect_vec();
 | 
			
		||||
        for edge in edges {
 | 
			
		||||
            pile.graph.remove_edge(edge);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // find how many nodes are reachable from z = 1 - these won't move
 | 
			
		||||
        let safe_blocks = fixed_nodes
 | 
			
		||||
            .iter()
 | 
			
		||||
            .flat_map(|origin| {
 | 
			
		||||
                Bfs::new(&pile.graph, *origin)
 | 
			
		||||
                    .iter(&pile.graph)
 | 
			
		||||
                    .map(|n| pile.graph[n].clone())
 | 
			
		||||
            })
 | 
			
		||||
            .unique()
 | 
			
		||||
            .count();
 | 
			
		||||
        // we are looking for the nodes that *will* disintegrate
 | 
			
		||||
        println!(
 | 
			
		||||
            "From {}, {} safe, {} disintegrate",
 | 
			
		||||
            node.index(),
 | 
			
		||||
            safe_blocks,
 | 
			
		||||
            pile.graph.node_count() - safe_blocks
 | 
			
		||||
        );
 | 
			
		||||
        accum += pile.graph.node_count() - safe_blocks;
 | 
			
		||||
        // put the graph back how it was
 | 
			
		||||
        for neigh in dependents {
 | 
			
		||||
            pile.graph.add_edge(node, neigh, ());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    println!("blocks: {} nodes: {}", pile.blocks.len(), pile.graph.node_count());
 | 
			
		||||
    accum as u64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
@@ -381,6 +394,6 @@ mod tests {
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn problem2_example() {
 | 
			
		||||
        let c = Cursor::new(EXAMPLE);
 | 
			
		||||
        assert_eq!(problem2(c.lines()), 0);
 | 
			
		||||
        assert_eq!(problem2(c.lines()), 7);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user