this post was submitted on 05 Dec 2023
34 points (100.0% liked)

Advent Of Code

768 readers
1 users here now

An unofficial home for the advent of code community on programming.dev!

Advent of Code is an annual Advent calendar of small programming puzzles for a variety of skill sets and skill levels that can be solved in any programming language you like.

AoC 2023

Solution Threads

M T W T F S S
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25

Rules/Guidelines

Relevant Communities

Relevant Links

Credits

Icon base by Lorc under CC BY 3.0 with modifications to add a gradient

console.log('Hello World')

founded 1 year ago
MODERATORS
 

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)

you are viewing a single comment's thread
view the rest of the comments
[–] [email protected] 1 points 11 months 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()),
                ]
            }
        }
    }
}