//! 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,
}
})
}