Part 3
For this part we are going to finally check for vertical and horizontal percolation and display the cluster number that percolates and if they percolate horizontally or vertically.
We are also going to add in some runtime flags to allow you do define the array size at runtime and to default to not print the array (basically because large array sizes are not practical to print to the console.
The Rust code for part 3
use std::collections::{HashMap, HashSet}; use std::env; extern crate chrono; use chrono::Local; fn print_array(array:&Vec>, height: usize, width: usize){ //function to print out the array to the teminal for i in 0..height{ for j in 0..width{ print!("{:0>3} ",array[i][j]); } println!(""); } // end print_array function } // Function to count clusters fn count_clusters(array:&Vec >, height: usize, width: usize) -> HashMap { let mut clusters = HashMap::new(); for i in 1..height-1{ for j in 1..width-1{ if array[i][j] > 0 { *clusters.entry(array[i][j]).or_insert(0) += 1; } } } clusters //end count_cluster function } // Function to sort clusters fn sort_clusters(clusters: &HashMap ) -> Vec<(usize, usize)> { let mut sorted_clusters: Vec<_> = clusters.iter().collect(); sorted_clusters.sort_by(|a, b| b.1.cmp(a.1)); sorted_clusters.into_iter().map(|(&key, &value)| (key, value)).collect() //end sort_cluster function } //Function to check if a cluster percolates fn check_percolation(array:&Vec >, height: usize, width: usize, clusters: &HashMap ) { // Check top and bottom rows for cluster ids let top_row: HashSet<_> = array[1].iter().cloned().collect(); let bottom_row: HashSet<_> = array[height - 2].iter().cloned().collect(); // Check left and right columns for cluster ids let left_column: HashSet<_> = array.iter().map(|row| row[1]).collect(); let right_column: HashSet<_> = array.iter().map(|row| row[width - 2]).collect(); for (&cluster_id, _) in clusters.iter() { let touches_top = top_row.contains(&cluster_id); let touches_bottom = bottom_row.contains(&cluster_id); let touches_left = left_column.contains(&cluster_id); let touches_right = right_column.contains(&cluster_id); let percolates_vertically = touches_top && touches_bottom; let percolates_horizontally = touches_left && touches_right; if percolates_vertically && percolates_horizontally { println!("Cluster {:0>3} percolates both vertically and horizontally.", cluster_id); } else if percolates_vertically { println!("Cluster {:0>3} percolates vertically.", cluster_id); } else if percolates_horizontally { println!("Cluster {:0>3} percolates horizontally.", cluster_id); } } //end check__percolation function } fn main() { //define array, constants and variables let mut width: usize = 24; let mut height: usize = 16; let mut n = 0; let mut change:usize = 0; let mut loops:usize = 0; let mut debug_flag: i32 = 0; let now = Local::now(); let date_time_string = now.format("%Y%m%d%H%M%S").to_string(); //get commandline args let args: Vec = env::args().collect(); for mut i in 1..args.len(){ if args[i]=="help" { println!("cargo run -- debug height 99 width 99 help"); println!("debug - print the before and after array to the console"); println!("height 99 - numeric value for heigh (y) of array"); println!("width 99 - numeric value for width (x) of the array"); println!("help - print this helpful information"); std::process::exit(0); } if args[i]=="debug" {debug_flag=1;} if args[i]=="width" { i+=1; width=match args[i].trim().parse:: (){ Ok(value) => {value}, Err(_) => { eprintln!("Width must be numeric value"); std::process::exit(1); }, }; } if args[i]=="height" { i+=1; height=match args[i].trim().parse:: (){ Ok(value) => {value}, Err(_) => { eprintln!("Height must be numeric value"); std::process::exit(1); }, }; } } //define array and populat with zeros let nn: usize = width * height; let mut array = vec![vec![0; width]; height]; //populate array with consecutive numbers where 0 is rock and give it a rock boarder for i in 1..height-1{ for j in 1..width-1{ if ((i*height)+j)%5>0{ array[i][j]=(i*height)+j; } } } //print array if debug_flag==1 { print_array(&array, height, width); } //perculate array while n < nn { for i in 1..height-1{ for j in 1..width-1{ if array[i][j] > 0{ if array[i][j-1]>array[i][j] {array[i][j]=array[i][j-1];change+=1;} if array[i][j+1]>array[i][j] {array[i][j]=array[i][j+1];change+=1;} if array[i-1][j]>array[i][j] {array[i][j]=array[i-1][j];change+=1;} if array[i+1][j]>array[i][j] {array[i][j]=array[i+1][j];change+=1;} } } } loops+=1; if change > 0 { n+=1; change = 0; } else { n = nn; } } //print array if debug_flag==1 { println!(""); print_array(&array, height, width); } println!(""); //count the clusters let clusters = count_clusters(&array, height, width); let sorted_clusters = sort_clusters(&clusters); for (cluster, size) in sorted_clusters.iter() { println!("Cluster {:0>3} size: {}", cluster, size); } println!(""); //check for clusters that percolate check_percolation(&array, height, width, &clusters); println!(""); //print totals println!("Total clusters: {}", clusters.len()); println!(""); println!("Percolation completed in: {} loops (Max loops {})", loops, nn); //end of program }
The code output
me@pi:~/rust/percolate-part3$ cargo run Compiling percolate-part2 v0.1.0 (/home/ubuntu/rust/percolate-part3) Finished dev [unoptimized + debuginfo] target(s) in 0.81s Running `target/debug/percolate-part3` Cluster 229 size: 56 Cluster 234 size: 55 Cluster 209 size: 46 Cluster 239 size: 38 Cluster 129 size: 26 Cluster 244 size: 18 Cluster 049 size: 6 Cluster 246 size: 1 Cluster 234 percolates vertically. Cluster 229 percolates vertically. Total clusters: 8 Percolation completed in: 14 loops (Max loops 384) me@pi:~/rust/percolate-part3$
Mission Accomplished
And there we have it, we have percolation! Before we looking at parallelisation of this code we will spend the next part adding in the ability to load and save percolation map and log filed and to set up percolation maps from a random seed.