Day 5: If You Give a Seed a Fertilizer


Megathread guidelines

  • Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
  • Code block support is not fully rolled out yet but likely will be in the middle of the event. Try to share solutions as both code blocks and using something such as https://topaz.github.io/paste/ , pastebin, or github (code blocks to future proof it for when 0.19 comes out and since code blocks currently function in some apps and some instances as well if they are running a 0.19 beta)

FAQ


🔒This post will be unlocked when there is a decent amount of submissions on the leaderboard to avoid cheating for top spots

🔓 Unlocked after 27 mins (current record for time, hard one today)

  • abclop99@beehaw.org
    link
    fedilink
    English
    arrow-up
    1
    ·
    1 year ago

    [Rust]

    Part 2
    use std::fs;
    use std::path::PathBuf;
    
    use clap::Parser;
    
    #[derive(Parser)]
    #[command(author, version, about, long_about = None)]
    struct Cli {
        input_file: PathBuf,
    }
    
    /// Start < end
    #[derive(Debug, Copy, Clone)]
    struct Range {
        start: Idx,
        end: Idx,
    }
    
    impl From> for Range {
        fn from(item: std::ops::Range) -> Self {
            Range {
                start: item.start,
                end: item.end,
            }
        }
    }
    
    impl std::ops::Add for Range {
        type Output = Self;
    
        fn add(self, offset: i64) -> Self {
            Range {
                start: self.start + offset,
                end: self.end + offset,
            }
        }
    }
    
    #[derive(Debug, Copy, Clone)]
    enum Value {
        Unmapped(Range),
        Mapped(Range),
    }
    
    impl Value {
        fn value(self) -> Range {
            match self {
                Self::Mapped(n) => n,
                Self::Unmapped(n) => n,
            }
        }
    
        fn min(self) -> i64 {
            self.value().start
        }
    }
    
    fn main() {
        // Parse CLI arguments
        let cli = Cli::parse();
    
        // Read file
        let input_text = fs::read_to_string(&cli.input_file)
            .expect(format!("File \"{}\" not found", cli.input_file.display()).as_str());
    
        println!("{}", input_text);
    
        // Split into seeds and individual maps
        let mut maps_text = input_text.split("\n\n");
        let seeds_text = maps_text.next().expect("Seeds");
    
        // Seed numbers; will be mapped to the final locations
        let seed_numbers: Vec = parse_seeds(seeds_text);
        let seed_numbers = parse_seed_ranges(seed_numbers);
        let mut seed_values: Vec = seed_numbers.iter().map(|n| Value::Unmapped(*n)).collect();
    
        eprintln!("Seeds: {:?}", seed_values);
    
        // Apply maps to seeds
        for map_text in maps_text {
            let mut map_ranges_text = map_text.lines();
            let map_description = map_ranges_text
                .next()
                .expect("There should be a description of the map here");
            eprintln!("Map: {}", map_description);
    
            // Apply ranges to seeds
            // Map structure:
            // dest start, source start, range length
            for range_text in map_ranges_text {
                let range_numbers: Vec = range_text
                    .split_ascii_whitespace()
                    .map(|n| n.parse().expect("Should be a number here"))
                    .collect();
    
                eprintln!("\trange: {:?}", range_numbers);
    
                let source_range = range_numbers[1]..range_numbers[1] + range_numbers[2];
                let offset = range_numbers[0] - range_numbers[1];
    
                eprintln!("\t\tSource range: {:?}\tOffset: {}", source_range, offset);
    
                // Apply the range map to the seeds
                seed_values = seed_values
                    .iter()
                    .map(|n| match n {
                        Value::Unmapped(n) => map_seed_range(*n, source_range.clone().into(), offset),
                        Value::Mapped(_) => vec![*n],
                    })
                    .flatten()
                    .collect();
    
                eprintln!("\t\tSeed values: {:?}", seed_values);
            }
    
            // Reset seed values to unmapped
            seed_values = seed_values
                .iter()
                .map(|v| Value::Unmapped(v.value()))
                .collect();
        }
    
        // Find minimum
        let min_location = seed_values.iter().map(|v| v.min()).min().unwrap();
    
        println!();
        println!("Part 2: {}", min_location);
    }
    
    /// Parse string to vec of string numbers
    fn parse_seeds(seeds_text: &str) -> Vec {
        seeds_text
            .split_ascii_whitespace()
            .skip(1)
            .map(|n| n.parse().expect("Should be a number here"))
            .collect()
    }
    
    /// Fill out ranges of seed numbers
    fn parse_seed_ranges(seed_numbers: Vec) -> Vec> {
        seed_numbers
            .chunks(2)
            .map(|v| (v[0]..v[0] + v[1]).into())
            .collect()
    }
    
    /// Maps a seed range to possibly multiple based on a source range and offset
    fn map_seed_range(seed_range: Range, source_range: Range, offset: i64) -> Vec {
        let start_cmp = seed_range.start < source_range.start;
        let end_cmp = seed_range.end <= source_range.end;
    
        match (start_cmp, end_cmp) {
            (false, false) => {
                if source_range.end <= seed_range.start {
                    vec![Value::Unmapped(seed_range)]
                } else {
                    vec![
                        Value::Mapped((seed_range.start + offset..source_range.end + offset).into()),
                        Value::Unmapped((source_range.end..seed_range.end).into()),
                    ]
                }
            }
            (false, true) => vec![Value::Mapped(seed_range + offset)],
            (true, false) => vec![
                Value::Unmapped((seed_range.start..source_range.start).into()),
                Value::Mapped((source_range.start + offset..source_range.end + offset).into()),
                Value::Unmapped((source_range.end..seed_range.end).into()),
            ],
            (true, true) => {
                if seed_range.end <= source_range.start {
                    vec![Value::Unmapped(seed_range)]
                } else {
                    vec![
                        Value::Unmapped((seed_range.start..source_range.start).into()),
                        Value::Mapped((source_range.start + offset..seed_range.end + offset).into()),
                    ]
                }
            }
        }
    }