aboutsummaryrefslogtreecommitdiff
path: root/2022/src/day05.rs
blob: f45cd0aecd96c87f5b6291cfbe180fcc9c8290b5 (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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//! 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<Option<&'a Crate<'a>>>,
}

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<Option<&'arena Crate<'arena>>>;

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<Item = Move> + '_ {
    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::<usize>(line);
            let line = parse_static(" to ", line);
            let (to, _) = parse_num::<usize>(line);

            Move {
                reps,
                from: from - 1,
                to: to - 1,
            }
        })
}