use nom::{ branch::alt, bytes::complete::{tag, take, take_while1}, combinator::{all_consuming, map, map_res, opt}, sequence::{delimited, preceded, tuple}, Finish, IResult, }; #[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[0].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().unwrap()) .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) { 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 = transpose_rev(crate_lines); crate_columns.iter().for_each(|item| println!("{item:?}")); assert!(lines.next().unwrap().is_empty()); let instructions: Vec<_> = lines .map(|line| all_consuming(parse_instruction)(line).finish().unwrap().1) .collect(); for ins in &instructions { println!("{ins:?}"); } } pub fn solve(file_path: &str) -> i32 { let content = std::fs::read_to_string(file_path).unwrap_or_else(|err| panic!("{err}")); parser(&content); 0 }