diff --git a/2022/day13-part2/Cargo.toml b/2022/day13-part2/Cargo.toml new file mode 100644 index 0000000..3717338 --- /dev/null +++ b/2022/day13-part2/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "day13-part2" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/2022/day13-part2/src/main.rs b/2022/day13-part2/src/main.rs new file mode 100644 index 0000000..c49e3f8 --- /dev/null +++ b/2022/day13-part2/src/main.rs @@ -0,0 +1,166 @@ +use std::{cmp::Ordering, iter::zip, str::Chars}; + +#[derive(Debug)] +enum Packet { + Number(i32), + List(Vec), +} + +impl Packet { + // Parse a single packet line into the Packet data structure. + fn from_string(s: &mut Chars) -> Vec { + // println!("Entering with {}", s.clone().collect::()); + // We're going to iterate through this character by character. + // It is assumed that the opening '[' is already stripped. + // Create an empty vector packet to fill in a loop. + let mut packet_list: Vec = Vec::new(); + // Now iterate through all the remaining characters. + loop { + // Collect a single element, i.e. all characters until the first occurence of '[', ']' or ','. + // The delimiter itself will be collected into `c`. + let mut elem = String::new(); + let mut c: char; + loop { + c = s.next().unwrap(); + if c == '[' || c == ']' || c == ',' { + break; + } else { + elem.push(c); + } + } + // Parse the element if it isn't empty and add it to the list. + if !elem.is_empty() { + let num = Packet::Number(elem.parse().unwrap()); + packet_list.push(num); + } + + // Encountering a new list? + // Call recursively and collect everything there. + if c == '[' { + let sublist = Packet::from_string(s); + packet_list.push(Packet::List(sublist)); + } + + // Encountered the end of the list? + // Then we're done here. + if c == ']' { + break; + } + } + packet_list + } +} + +fn main() { + // Use command line arguments to specify the input filename. + let args: Vec = std::env::args().collect(); + if args.len() < 2 { + panic!("Usage: ./main \nNo input file provided. Exiting."); + } + + // Next, read the contents of the input file into a string for easier processing. + let input = std::fs::read_to_string(&args[1]).expect("Error opening file"); + + // --- TASK BEGIN --- + + // Collect all actual packets into a big packet vector. + let mut packets: Vec<_> = input + .lines() + .filter(|l| !l.is_empty()) + .map(|l| Packet::from_string(&mut l[1..].chars())) + .collect(); + + // Create copies of the divider packages and add them to the big vector. + packets.push(vec![Packet::List(vec![Packet::Number(2)])]); + packets.push(vec![Packet::List(vec![Packet::Number(6)])]); + + // The magic line. Sort the vector using `packet_compare`. + packets.sort_unstable_by(packet_compare); + + // Find the indices of the 2 and 6 divider. + let idx2 = packets.iter().enumerate().find(|(_, x)| { + packet_compare( + x, + &vec![Packet::List(vec![Packet::Number(2)])], + ).is_eq() + }).unwrap().0; + + let idx6 = packets.iter().enumerate().find(|(_, x)| { + packet_compare( + x, + &vec![Packet::List(vec![Packet::Number(6)])], + ).is_eq() + }).unwrap().0; + + print_packet_list(&packets); + println!("Decoder key: {}", (idx2 + 1) * (idx6 + 1)); +} + +fn print_packet_list(packet_list: &Vec>) { + for packet in packet_list { + print_packet(packet); + // Add newlines after every "top-level" packet. + println!(); + } +} + +fn print_packet(packet: &Vec) { + print!("["); + for e in packet { + match e { + Packet::Number(x) => { + print!("{x},"); + } + Packet::List(l) => { + print_packet(l); + } + } + } + print!("]"); +} + +fn packet_compare(left: &Vec, right: &Vec) -> Ordering { + let mut result: Ordering; + let mut zipper = zip(left, right); + loop { + match zipper.next() { + Some(d) => { + // Determine the Ordering result for the next two "elements", whatever they may be. + result = match d { + (Packet::Number(lv), Packet::Number(rv)) => { + // Compare the integers directly. + lv.cmp(rv) + } + (Packet::Number(lv), Packet::List(rl)) => { + // Left is a number, right is a list. + // Convert the number to a list and then compare those. + let ll: Vec = vec![Packet::Number(*lv)]; + packet_compare(&ll, rl) + } + (Packet::List(ll), Packet::Number(rv)) => { + // Left is a list, right is a number. + // Convert the number to a list and then compare those. + let rl: Vec = vec![Packet::Number(*rv)]; + packet_compare(ll, &rl) + } + (Packet::List(ll), Packet::List(rl)) => { + // Recursively step into the lists. + packet_compare(ll, rl) + } + }; + } + None => { + // Zipper empty? + // We need to remember which of the lists ran out first. + result = left.len().cmp(&right.len()); + // And definitely break, ofc, since the zipper is done. + break; + } + } + // Have we reached a conclusion already? If so, return. + if result != Ordering::Equal { + break; + } + } + result +}