use nom::{ branch::alt, bytes::complete::tag, character::complete::{ anychar, char as nom_char, line_ending, not_line_ending, u32 as nom_u32, }, combinator::map, multi::separated_list1, sequence::tuple, IResult, }; use std::fs; fn main() -> Result<(), Box> { let input = fs::read_to_string("inputs/day_5.txt")?; let state = CraneState::parser(&input).unwrap().1; let mut state_part_1 = state.clone(); while !state_part_1.done() { state_part_1.process_next_instruction(false); } dbg!(state_part_1.read_top_row()); let mut state_part_2 = state.clone(); while !state_part_2.done() { state_part_2.process_next_instruction(true); } dbg!(state_part_2.read_top_row()); Ok(()) } #[derive(Debug, PartialEq, Eq, Clone)] struct CraneState { towers: Vec, instructions: Vec, } #[derive(Debug, Default, PartialEq, Eq, Clone)] struct Tower { crates: Vec, } #[derive(Debug, PartialEq, Eq, Clone)] struct Instruction { number: usize, src: usize, dest: usize, } impl CraneState { fn parser(input: &str) -> IResult<&str, CraneState> { let single_crate = alt(( map(tuple((tag("["), anychar, tag("]"))), |(_, c, _)| Some(c)), map(tag(" "), |_| None), )); let crate_row = separated_list1(nom_char(' '), single_crate); map( tuple(( separated_list1(line_ending, crate_row), line_ending, not_line_ending, line_ending, line_ending, separated_list1(line_ending, Instruction::parser), )), |(crate_rows, _, _, _, _, mut instructions)| { let mut towers = Vec::new(); for row in &crate_rows { for (i, c) in row .iter() .enumerate() .filter_map(|(i, c)| c.map(|some_c| (i, some_c))) { while i >= towers.len() { towers.push(Tower::default()); } towers[i].crates.push(c); } } for tower in &mut towers { tower.crates.reverse(); } instructions.reverse(); CraneState { towers, instructions, } }, )(input) } fn process_next_instruction(&mut self, maintain_order: bool) { let Some(instruction) = self.instructions.pop() else { return }; let mut to_move = Vec::new(); for _ in 0..instruction.number { let Some(crate_to_move) = self.towers[instruction.src].crates.pop() else { panic!("Invalid puzzle input: failed to get crate"); }; to_move.push(crate_to_move); } if maintain_order { to_move.reverse(); } for crate_to_move in to_move { self.towers[instruction.dest].crates.push(crate_to_move); } } fn done(&self) -> bool { self.instructions.len() == 0 } fn read_top_row(&self) -> String { let mut res = String::new(); for tower in &self.towers { if let Some(top) = tower.crates.last() { res.push(*top); } } res } } impl Instruction { fn parser(input: &str) -> IResult<&str, Instruction> { map( tuple(( tag("move "), nom_u32, tag(" from "), nom_u32, tag(" to "), nom_u32, )), |(_, number, _, src, _, dest)| Instruction { number: number as usize, src: (src - 1) as usize, dest: (dest - 1) as usize, }, )(input) } }