Untitled

mail@pastecode.io avatar
unknown
rust
a year ago
4.6 kB
7
Indexable
Never
use itertools::Itertools;
use std::{collections::VecDeque, fs, process::exit};

#[derive(Debug)]
struct Instruction {
    from: usize,
    to: usize,
    quantity: usize,
}

impl From<&str> for Instruction {
    fn from(line: &str) -> Self {
        let (quantity, from, to) = line
            .split(" ")
            .skip(1)
            .step_by(2)
            .map(|x| x.parse::<usize>().expect("could not parse instruction"))
            .collect_tuple::<(usize, usize, usize)>()
            .expect("could not parse instruction line");

        Instruction {
            from: from - 1,
            to: to - 1,
            quantity,
        }
    }
}

#[derive(Debug)]
struct Ship {
    crates: Vec<VecDeque<char>>,
}

impl Ship {
    fn new(size: usize) -> Self {
        Self {
            crates: vec![VecDeque::default(); size],
        }
    }

    fn push_element(&mut self, element: char, to_idx: usize) {
        self.crates
            .get_mut(to_idx)
            .expect("attempting to index a crate that does not exist")
            .push_front(element);
    }

    fn move_elements<Crane: CrateMover>(
        &mut self,
        num_elements: usize,
        from_idx: usize,
        to_idx: usize,
    ) {
        let cargo_to_move: Vec<char> = self
            .crates
            .get_mut(from_idx)
            .expect("referenced non-existent crate")
            .drain(0..num_elements)
            .collect();
        Crane::move_crate(self, cargo_to_move, to_idx)
    }

    fn top_elements(&self) -> Vec<char> {
        self.crates
            .iter()
            .filter_map(|stack| stack.front())
            .map(|element| *element)
            .collect()
    }
}
impl From<&str> for Ship {
    fn from(input: &str) -> Self {
        let mut input = input.lines().rev();

        let num_crates = input
            .next()
            .expect("Missing stack count line")
            .split(" ")
            .last()
            .expect("Unable to parse stack count line")
            .parse::<usize>()
            .expect("Unable to parse stack count line");

        let mut ship = Ship::new(num_crates);

        fn parse_crate_line(line: &str) -> Vec<char> {
            line.chars().skip(1).step_by(4).collect()
        }

        input
            .map(parse_crate_line)
            .for_each(|crate_line: Vec<char>| {
                let important_elements = crate_line
                    .into_iter()
                    .enumerate()
                    .filter(|(_, c)| !c.is_whitespace());

                for (idx, element) in important_elements {
                    ship.push_element(element, idx);
                }
            });
        ship
    }
}

fn main() {
    let contents = match fs::read_to_string("input.txt") {
        Ok(content) => content,
        Err(err) => {
            println!("could not read file\nerr: {err}");
            exit(1);
        }
    };

    println!("part 1: {}", part_one(&contents));
    println!("part two: {}", part_two(&contents));
}

fn part_one(input: &str) -> String {
    let (mut ship, instructions) = parse_initial_conf(input);

    for Instruction { from, to, quantity } in instructions {
        ship.move_elements::<Crane9000>(quantity, from, to);
    }
    ship.top_elements().into_iter().collect::<String>()
}

fn part_two(input: &str) -> String {
    let (mut ship, instructions) = parse_initial_conf(input);

    for Instruction { from, to, quantity } in instructions {
        ship.move_elements::<Crane9001>(quantity, from, to);
    }
    ship.top_elements().into_iter().collect::<String>()
}

fn parse_initial_conf(input: &str) -> (Ship, Vec<Instruction>) {
    let [ship_config, instructions]: [&str; 2] = input
        .split("\n\n")
        .collect::<Vec<&str>>()
        .try_into()
        .expect("unable to parse input");

    let ship = Ship::from(ship_config);
    let instructions = instructions
        .lines()
        .map(Instruction::from)
        .collect::<Vec<Instruction>>();
    (ship, instructions)
}

struct Crane9000;
struct Crane9001;

trait CrateMover {
    fn move_crate(ship: &mut Ship, cargo: Vec<char>, to_idx: usize);
}

impl CrateMover for Crane9000 {
    fn move_crate(ship: &mut Ship, cargo: Vec<char>, to_idx: usize) {
        for element in cargo {
            ship.push_element(element, to_idx);
        }
    }
}

impl CrateMover for Crane9001 {
    fn move_crate(ship: &mut Ship, cargo: Vec<char>, to_idx: usize) {
        for element in cargo.into_iter().rev() {
            ship.push_element(element, to_idx);
        }
    }
}