use core::num::ParseIntError; const DATA: &'static str = include_str!("../data/day5.txt"); // const DATA: &'static str = " // [D] // [N] [C] // [Z] [M] [P] // 1 2 3 // move 1 from 2 to 1 // move 3 from 1 to 3 // move 2 from 2 to 1 // move 1 from 1 to 2 // "; enum BadLine { NotEnoughWords, ParseError(ParseIntError), } #[derive(Debug)] struct State { stacks: [Vec; 9], } impl State { fn process(&mut self, op: &Instruction) { for _ in 0..(op.count) { let item = self.stacks[op.src as usize - 1].pop().unwrap(); self.stacks[op.dst as usize - 1].push(item); } } fn process_inorder(&mut self, op: &Instruction) { let src_stack = &mut self.stacks[op.src as usize - 1]; let split_idx = src_stack.len() - op.count as usize; let mut substack = src_stack.split_off(split_idx); self.stacks[op.dst as usize - 1].append(&mut substack); } fn get_tops(&self) -> String { let mut res = String::with_capacity(9); for stack in &self.stacks { res.push(*stack.last().unwrap()); } res } } #[derive(Debug)] struct Instruction { count: u32, src: u32, dst: u32, } fn parse_map(map: &str) -> State { let rows: Vec<&str> = map.lines().collect(); let mut stacks: [Vec; 9] = Default::default(); // skip the first row, because it's just the stack numbers for row in rows.iter().rev().skip(1) { for (col_num, c) in row.chars().enumerate() { if c.is_ascii_uppercase() { let stack_idx = (col_num - 1) / 4; stacks[stack_idx].push(c); } } } State { stacks } } fn parse_instruction(data: &str) -> Result { let mut words = data.split(' '); Ok(Instruction { count: words.nth(1).ok_or(BadLine::NotEnoughWords)?.parse().map_err(|e| BadLine::ParseError(e))?, src: words.nth(1).ok_or(BadLine::NotEnoughWords)?.parse().map_err(|e| BadLine::ParseError(e))?, dst: words.nth(1).ok_or(BadLine::NotEnoughWords)?.parse().map_err(|e| BadLine::ParseError(e))?, }) } fn main() -> Result<(), String> { let mut splat = DATA.trim_end().split("\n\n"); let map = splat.next().unwrap(); let instruction_lines = splat.next().unwrap(); let mut state = parse_map(map); let instructions: Vec = instruction_lines.lines() .map(|line| parse_instruction(line).map_err(|_e| format!("Bad line: {line}"))) .collect::, String>>()?; for i in &instructions { state.process(i); } println!("Part 1: {}", state.get_tops()); let mut state2 = parse_map(map); for i in &instructions { state2.process_inorder(i); } println!("Part 2: {}", state2.get_tops()); Ok(()) }