summaryrefslogtreecommitdiff
path: root/src/bin/day12.rs
blob: 85bbe0d7dcf6d9943d0e32be361a0a5090289c9e (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
use std::collections::HashMap;

static DAY: u8 = 12;

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

#[derive(PartialEq,Eq,Hash,Clone,Copy)]
struct Position {
    x: isize,
    y: isize,
}

fn parse_map(input: &[String]) -> (HashMap<Position,u32>, Position, Position) {
    let mut map = HashMap::new();
    let mut start = Position { x: 0, y: 0};
    let mut end = Position { x: 0, y: 0};

    for (y, line) in input.iter().enumerate() {
        for (x, c) in line.chars().enumerate() {
            let pos = Position { x: x as isize, y: y as isize };
            let elevation = match c {
                'S' => { start = pos; 'a' }
                'E' => { end = pos; 'z' }
                e => e,
            };
            let elevation = elevation.to_digit(36).unwrap();
            map.insert(pos, elevation);
        }
    }

    (map, start, end)
}

fn steps_required(map: &HashMap<Position,u32>, end: &Position, found_position: impl Fn(&Position, u32) -> bool) -> u32 {
    let mut distances = HashMap::new();
    distances.insert(*end, 0);

    loop {
        let positions = distances.keys().cloned().collect::<Vec<_>>();
        for pos in positions {
            let next_dist = distances[&pos] + 1;
            let neighbors = [
                Position { x: pos.x + 1, y: pos.y },
                Position { x: pos.x, y: pos.y + 1 },
                Position { x: pos.x - 1, y: pos.y },
                Position { x: pos.x, y: pos.y - 1 },
            ];
            for neighbor in neighbors {
                if distances.contains_key(&neighbor) || !map.contains_key(&neighbor){
                    continue;
                }
                if map[&pos] <= map[&neighbor] || map[&pos] == map[&neighbor] + 1 {
                    distances.insert(neighbor, next_dist);
                    if found_position(&neighbor, map[&neighbor]) {
                        return next_dist;
                    }
                }
            }
        }
    }
}

fn steps_required_start(input: &[String]) -> u32 {
    let (map, start, end) = parse_map(input);
    let found_position = |pos: &Position, _ele: u32| -> bool {
        /* looking for the starting position */
        *pos == start
    };
    steps_required(&map, &end, found_position)
}

fn steps_required_any_a(input: &[String]) -> u32 {
    let (map, _, end) = parse_map(input);
    let found_position = |_pos: &Position, ele: u32| -> bool {
        /* looking for any location with elevation 'a' */
        ele == 'a'.to_digit(36).unwrap()
    };
    steps_required(&map, &end, found_position)
}

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

    #[test]
    fn test() {
        let input = [
            "Sabqponm",
            "abcryxxl",
            "accszExk",
            "acctuvwj",
            "abdefghi",
        ].iter().map(|&x| String::from(x)).collect::<Vec<_>>();

        assert_eq!(steps_required_start(&input), 31);
        assert_eq!(steps_required_any_a(&input), 29);
    }
}