use std::cmp::max; use std::str::FromStr; use aoc_2022::*; type Input = Ship; type Output = String; #[derive(Debug)] struct Move { count: usize, from: usize, to: usize, } impl FromStr for Move { type Err = Report; fn from_str(s: &str) -> Result { let split_str: Vec<_> = s.split_whitespace().collect(); let ints = vec![split_str[1], split_str[3], split_str[5]] .iter() .map(|x| x.parse::().unwrap()) .collect_vec(); let (count, from, to) = (ints[0], ints[1], ints[2]); Ok(Move { count, from, to }) } } #[derive(Debug)] struct Ship(Vec>, Vec); impl FromStr for Ship { type Err = Report; fn from_str(s: &str) -> Result { let split_s = s.split("\n\n").collect_vec(); let (stacks_str, moves_str) = (split_s[0], split_s[1]); let mut stacks: Vec> = Vec::new(); stacks_str .lines() .rev() .skip(1) .map(|l| l.to_string() + " ") .for_each(|l| { l.chars().skip(1).step_by(4).enumerate().for_each(|(i, c)| { if i >= stacks.len() { stacks.push(Vec::new()); } if c != ' ' { stacks[i].push(c); } }); }); let moves = strings_to_structs(moves_str.lines()); Ok(Ship(stacks, moves)) } } fn stacks_to_string(stacks: &[Vec]) -> String { stacks.iter().fold(String::new(), |acc, stack| { if let Some(c) = stack.last() { acc + &c.to_string() } else { acc } }) } fn move_crates(input: &Input, one_by_one: bool) -> Output { let Ship(stacks, moves) = input; let mut stacks = stacks.clone(); moves.iter().for_each(|m| { let size = stacks[m.from - 1].len(); let mut s = stacks[m.from - 1].split_off(max(0, size - m.count)); if one_by_one { s.reverse(); } stacks[m.to - 1].append(&mut s); }); stacks_to_string(&stacks) } struct Day05; impl Solution for Day05 { fn parse_input>(pathname: P) -> Input { file_to_string(pathname).parse().unwrap() } fn part_1(input: &Input) -> Output { move_crates(input, true) } fn part_2(input: &Input) -> Output { move_crates(input, false) } } fn main() -> Result<()> { Day05::main() } test_sample!(day_05, Day05, "CMZ".to_string(), "MCD".to_string());