1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//! # Supply Stacks
//!
//! There are 2 main challenges to this problem:
//! * Parsing the input!
//! * (Rust Specific): The borrow checker prevents mutating a nested `vec` at 2 indices at once.
use crate::util::iter::*;
use crate::util::parse::*;

type Stack = Vec<Vec<char>>;
type Move = [usize; 3];
type Input = (Stack, Vec<Move>);

/// Parses the input in 2 stages.
///
/// First, the input is split into a prefix and suffix, using a blank line (or 2 newline characters
/// one after another) as the delimiter.
///
/// The suffix consisting of triplets of (amount, from, to) can be parsed using our utility
/// [`iter_unsigned`] and [`chunk`] methods to tokenize the string into numbers, then group it into
/// triples. One minor nuance is that the `from` and `to` field are *1* based indexing, so we
/// convert them to 0 based for convenience.
///
/// The prefix is a little more complex. The number of columns is the width in characters plus 1
/// divided by 4 (the last column has no trailing space). Then we build the vectors from the bottom
/// up by iterating through the rows in reverse. This places the elements at the top of each stack
/// at the end of the `vec` which is a more natural location for mutation (as removing elements from
/// the start of a `vec` involved moving all remaining elements).
///
/// [`iter_unsigned`]: ParseOps::iter_unsigned
/// [`chunk`]: ChunkOps::chunk
pub fn parse(input: &str) -> Input {
    let (prefix, suffix) = input.split_once("\n\n").unwrap();
    let lines: Vec<_> = prefix.lines().collect();
    let width = (lines[0].len() + 1) / 4;

    let mut stack: Stack = vec![Vec::new(); width];
    for row in lines.iter().rev().skip(1) {
        for (i, c) in row.chars().skip(1).step_by(4).enumerate() {
            if c.is_ascii_alphabetic() {
                stack[i].push(c);
            }
        }
    }

    let moves: Vec<_> = suffix
        .iter_unsigned()
        .chunk::<3>()
        .map(|[amount, from, to]| [amount, from - 1, to - 1])
        .collect();

    (stack, moves)
}

/// Move elements from stack to stack, reversing each time.
pub fn part1(input: &Input) -> String {
    play(input, true)
}

/// Move elements from stack to stack without reversing.
pub fn part2(input: &Input) -> String {
    play(input, false)
}

/// Rust's borrow checker won't allow us to mutate 2 nested `vec`s simulataneously, so we need
/// to use an temporary intermediate `vec` to store the moving crates. For efficiency we can re-use
/// the same vec to prevent unnecessary memory allocations.
///
/// A nice standard library feature is that we can collect an iterator of `char`s into a `String`
/// for the final answer.
fn play(input: &Input, reverse: bool) -> String {
    let (initial, moves) = input;
    let mut stack = initial.clone();
    let mut crates = Vec::new();

    for &[amount, from, to] in moves {
        let start = stack[from].len() - amount;
        crates.extend(stack[from].drain(start..));
        if reverse {
            stack[to].extend(crates.iter().rev());
        } else {
            stack[to].extend(crates.iter());
        }
        crates.clear();
    }

    stack.iter().map(|v| v.last().unwrap()).collect()
}