summaryrefslogtreecommitdiff
path: root/src/bin/day3.rs
blob: cb442ed230d1e18fa09794bf492dfb828bb212e6 (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
fn main() {
    let input = advent::read_lines(3);
    println!("3a: {}", power_consumption(&input));
    println!("3b: {}", life_support_rating(&input));
}


fn power_consumption<T: AsRef<str>>(input: &[T]) -> u32 {
    let bits = count_ones(input);
    let gamma = rate(&bits, input.len(), true);
    let epsilon = rate(&bits, input.len(), false);

    gamma * epsilon
}

fn count_ones_at_pos<T: AsRef<str>>(input: &[T], pos: usize) -> usize {
    input.iter()
         .filter(|&x| x.as_ref().chars().nth(pos).unwrap() == '1')
         .count()
}

fn count_ones<T: AsRef<str>>(input: &[T]) -> Vec<usize> {
    input[0].as_ref()
            .chars()
            .enumerate()
            .map(|(i, _)| count_ones_at_pos(input, i))
            .collect()
}

fn rate(bits: &[usize], max_count: usize, gamma: bool) -> u32 {
    let mut result = 0;
    let max_count = max_count;

    for count in bits.iter() {
        result <<= 1;
        if gamma && 2 * count > max_count {
            result += 1;
        }
        if !gamma && 2 * count < max_count {
            result += 1;
        }
    }
    result
}

fn life_support_rating<T: AsRef<str>>(input: &[T]) -> usize {
    let oxygen = oxygen_rating(input);
    let co2 = co2_rating(input);

    oxygen * co2
}

fn co2_rating<T: AsRef<str>>(input: &[T]) -> usize {
    rating(input, false)
}

fn oxygen_rating<T: AsRef<str>>(input: &[T]) -> usize {
    rating(input, true)
}

fn rating<T: AsRef<str>>(input: &[T], oxygen: bool) -> usize {
    let mut numbers = vec!["0"; input.len()];
    for (i, number) in input.iter().enumerate() {
        numbers[i] = number.as_ref();
    }

    let len = numbers[0].len();
    for i in 0 .. len {
        let ones = count_ones_at_pos(&numbers, i);
        let keep = if (oxygen && 2 * ones >= numbers.len()) || (!oxygen && 2 * ones < numbers.len()) {
            '1'
        } else {
            '0'
        };
        numbers = numbers.iter()
                         .filter(|&x| x.chars().nth(i).unwrap() == keep)
                         .copied()
                         .collect();
        if numbers.len() == 1 {
            break;
        }
    }
    usize::from_str_radix(numbers[0], 2).unwrap()
}

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

    #[test]
    fn test() {
        let input = [
            "00100",
            "11110",
            "10110",
            "10111",
            "10101",
            "01111",
            "00111",
            "11100",
            "10000",
            "11001",
            "00010",
            "01010",
        ];
        assert_eq!(power_consumption(&input), 198);
        assert_eq!(life_support_rating(&input), 230);
    }
}