//! Day 5: Supply Stacks extern crate bumpalo; mod utils; use bumpalo::Bump; use std::cell::Cell; use utils::{parse_num, parse_static, read_input}; /// Base type for a crate #[derive(Debug, Clone)] struct Crate<'a> { letter: char, below: Cell>>, } impl Default for Crate<'_> { fn default() -> Self { Crate { letter: ' ', below: None.into(), } } } /// A move the crane may make #[derive(Debug, Clone, Copy)] struct Move { reps: usize, from: usize, to: usize, } type Tops<'arena> = Vec>>; fn main() { let input = read_input(); { let arena = Bump::new(); let mut tops = construct_piles(&input, &arena); for mv in parse_moves(&input) { perform_move_9000(&mut tops, mv); } println!("Part 1: {}", top_letters(tops)); } { let arena = Bump::new(); let mut tops = construct_piles(&input, &arena); for mv in parse_moves(&input) { perform_move_9001(&mut tops, mv); } println!("Part 2: {}", top_letters(tops)); } } /// Get the message / the top letters of each stack fn top_letters(tops: Tops<'_>) -> String { tops.iter().map(|x| x.as_ref().unwrap().letter).collect() } /// Perform a move for part 1 fn perform_move_9000(tops: &mut Tops<'_>, mv: Move) { for _ in 0..mv.reps { let from = tops[mv.from].take().unwrap(); let to = tops[mv.to].take(); tops[mv.from] = from.below.replace(to); tops[mv.to] = Some(from); } } /// Perform a move for part 2 fn perform_move_9001(tops: &mut Tops<'_>, mv: Move) { let pickup_top = tops[mv.from].take().unwrap(); let mut pickup_bot = pickup_top; for _ in 1..mv.reps { pickup_bot = pickup_bot.below.get().as_ref().unwrap(); } let to = tops[mv.to].take(); tops[mv.from] = pickup_bot.below.replace(to); tops[mv.to] = Some(pickup_top); } // Annoying parsing code! fn construct_piles<'a>(input: &str, arena: &'a Bump) -> Tops<'a> { let mut piles = Vec::new(); for layer_iter in input .lines() .take_while(|x| x.chars().find(|x| *x != ' ').unwrap() == '[') .map(|x| x.chars().skip(1).step_by(4)) { for (stack, chr) in layer_iter.enumerate() { if piles.len() < stack + 1 { piles.resize(stack + 1, None); } if chr != ' ' { let val = &*arena.alloc(Crate { letter: chr, below: None.into(), }); if piles[stack].is_none() { piles[stack] = Some(val); } else { let mut top = *piles[stack].as_ref().unwrap(); while let Some(below) = top.below.get() { top = below; } top.below.set(Some(val)); } } } } piles } fn parse_moves(input: &str) -> impl Iterator + '_ { input .lines() .skip_while(|line| !line.starts_with('m')) .map(|line| { let line = parse_static("move ", line); let (reps, line) = parse_num(line); let line = parse_static(" from ", line); let (from, line) = parse_num::(line); let line = parse_static(" to ", line); let (to, _) = parse_num::(line); Move { reps, from: from - 1, to: to - 1, } }) }