summaryrefslogtreecommitdiff
path: root/src/bin/day5.rs
blob: a5e28fdd423f298889b34ca920adad90c9eb2f17 (plain)
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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use std::collections::VecDeque;
use regex::Regex;

static DAY: u8 = 5;

fn main() {
    let input = advent::read_lines(DAY);
    println!("{DAY}a: {}", find_top_crates(&input));
    println!("{DAY}b: {}", find_top_crates_9001(&input));
}

struct CrateMover {
    instructions: Vec<Instruction>,
    stacks: Vec<VecDeque<char>>,
}

struct Instruction {
    amount: usize,
    from: usize,
    to: usize,
}

impl CrateMover {
    fn new(input: &[String]) -> CrateMover {
        let re_instruction = Regex::new("move ([0-9]+) from ([0-9]+) to ([0-9]+)").unwrap();

        let mut instructions = Vec::new();
        let mut stacks = Vec::new();
        for line in input {
            if let Some(cap) = re_instruction.captures(line) {
                instructions.push(Instruction {
                    amount: cap[1].parse::<usize>().unwrap(),
                    from: cap[2].parse::<usize>().unwrap() - 1,
                    to: cap[3].parse::<usize>().unwrap() - 1,
                });
            }

            if line.find('[').is_some() {
                let chars = line.chars().collect::<Vec<_>>();
                for (index, chunk) in chars.chunks(4).enumerate() {
                    // chunk: "[x] "
                    let name = chunk[1];
                    if name == ' ' {
                        // empty position
                        continue;
                    }
                    if index >= stacks.len() {
                        stacks.resize(index + 1, VecDeque::new());
                    }
                    stacks[index].push_front(name);
                }
            }
        }

        CrateMover { stacks, instructions }
    }

    fn move_crates(&mut self) {
        for instruction in &self.instructions {
            for _ in 0 .. instruction.amount {
                let name = self.stacks[instruction.from].pop_back().expect("Stack has no crate");
                self.stacks[instruction.to].push_back(name);
            }
        }
    }

    fn move_crates_9001(&mut self) {
        for instruction in &self.instructions {
            let drain_from = self.stacks[instruction.from].len() - instruction.amount;
            let moved_stack = self.stacks[instruction.from].drain(drain_from ..).collect::<Vec<_>>();
            self.stacks[instruction.to].extend(moved_stack);
        }
    }
}

fn find_top_crates(input: &[String]) -> String {
    let mut cratemover = CrateMover::new(input);
    cratemover.move_crates();
    cratemover.stacks.iter()
                     .map(|stack| stack.back().unwrap())
                     .collect()
}

fn find_top_crates_9001(input: &[String]) -> String {
    let mut cratemover = CrateMover::new(input);
    cratemover.move_crates_9001();
    cratemover.stacks.iter()
                     .map(|stack| stack.back().unwrap())
                     .collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test() {
        let input = [
            "    [D]    ".to_string(),
            "[N] [C]    ".to_string(),
            "[Z] [M] [P]".to_string(),
            " 1   2   3 ".to_string(),
            "".to_string(),
            "move 1 from 2 to 1".to_string(),
            "move 3 from 1 to 3".to_string(),
            "move 2 from 2 to 1".to_string(),
            "move 1 from 1 to 2".to_string(),
        ];

        assert_eq!(find_top_crates(&input), "CMZ");
        assert_eq!(find_top_crates_9001(&input), "MCD");
    }
}