use std::collections::VecDeque; use itertools::Itertools; use nom::{ branch::alt, bytes::complete::{tag, take, take_while1}, combinator::{all_consuming, map, map_res, opt}, Finish, IResult, sequence::{delimited, preceded, tuple}, }; #[derive(Debug)] struct Instruction { quantity: usize, src: usize, dst: usize, } /// Used to parse a crate /// /// # Arguments /// /// * `i`: /// /// returns: Result<(&str, char), Err>> /// fn parse_crate(i: &str) -> IResult<&str, char> { let first_char = |s: &str| s.chars().next().unwrap(); let f = delimited(tag("["), take(1_usize), tag("]")); map(f, first_char)(i) } fn parse_hole(i: &str) -> IResult<&str, ()> { // `drop` takes a value and returns nothing, which is // perfect for our case map(tag(" "), drop)(i) } fn parse_crate_or_hole(i: &str) -> IResult<&str, Option> { alt((map(parse_crate, Some), map(parse_hole, |_| None)))(i) } fn parse_crate_line(i: &str) -> IResult<&str, Vec>> { let (mut i, c) = parse_crate_or_hole(i)?; let mut v = vec![c]; loop { let (next_i, maybe_c) = opt(preceded(tag(" "), parse_crate_or_hole))(i)?; match maybe_c { Some(c) => v.push(c), None => break, } i = next_i; } Ok((i, v)) } fn transpose_rev(v: Vec>>) -> Vec> { assert!(!v.is_empty()); let len = v .iter() .max_by(|x, y| x.len().cmp(&y.len())) .unwrap_or_else(|| panic!("")) .len(); let mut iters: Vec<_> = v.into_iter().map(|n| n.into_iter()).collect(); (0..len) .map(|_| { iters .iter_mut() .filter_map(|n| n.next()) .flatten() .collect::>() }) .collect() } fn parse_number(i: &str) -> IResult<&str, usize> { map_res(take_while1(|c: char| c.is_ascii_digit()), |s: &str| { s.parse::() })(i) } fn parse_pile_number(i: &str) -> IResult<&str, usize> { map(parse_number, |i| i - 1)(i) } fn parse_instruction(i: &str) -> IResult<&str, Instruction> { map( tuple(( preceded(tag("move "), parse_number), preceded(tag(" from "), parse_pile_number), preceded(tag(" to "), parse_pile_number), )), |(quantity, src, dst)| Instruction { quantity, src, dst }, )(i) } fn parser(content: &str) -> (Vec>, Vec) { let mut lines = content.lines(); let crate_lines = (&mut lines) .map_while(|line| { all_consuming(parse_crate_line)(line) .finish() .ok() .map(|(_, line)| line) }) .collect(); let crate_columns = { let mut tmp = transpose_rev(crate_lines); tmp.iter_mut().for_each(|item| { item.make_contiguous(); }); tmp }; assert!(lines.next().unwrap().is_empty()); let instructions = lines .map(|line| all_consuming(parse_instruction)(line).finish().unwrap().1) .collect::>(); (crate_columns, instructions) } pub fn solve_part1(file_path: &str) -> String { let content = std::fs::read_to_string(file_path).unwrap_or_else(|err| panic!("{err}")); let (mut crate_columns, instructions) = parser(&content); instructions.iter().for_each(|ins| { (0..ins.quantity).for_each(|_| { let tmp = crate_columns .get_mut(ins.src) .unwrap_or_else(|| panic!("Index {} is out of bound of crate_columns.", ins.src)) .pop_front() .unwrap_or_else(|| panic!("Could not pop front the column {}", ins.src)); crate_columns .get_mut(ins.dst) .unwrap_or_else(|| panic!("Index {} is out of bound", ins.dst)) .push_front(tmp); }); }); crate_columns .iter() .map(|item| item.front().unwrap()) .join("") } pub fn solve_part2(file_path: &str) -> String { let content = std::fs::read_to_string(file_path).unwrap_or_else(|err| panic!("{err}")); let (mut crate_columns, instructions) = parser(&content); instructions.iter().for_each(|ins| { let mut test = vec!['\0'; ins.quantity]; (0..ins.quantity).for_each(|i| { test[i] = crate_columns[ins.src] .pop_front() .unwrap_or_else(|| panic!("Could not pop front the column {}", ins.src)); //crate_columns[ins.dst].push_front(tmp); }); test.reverse(); test.iter_mut() .for_each(|tmp| crate_columns[ins.dst].push_front(*tmp)); }); crate_columns .iter() .map(|item| item.front().unwrap()) .join("") }