summaryrefslogtreecommitdiff
path: root/2016/aoc20/src/main.rs
blob: ee14583bce1a5bb8ad159a5138191958ac9d0408 (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
use std::io::BufReader;
use std::io::prelude::*;
use std::fs::File;
use std::u32;

#[derive(Debug)]
struct IpRange {
    start: u32,
    end: u32
}

impl IpRange {
    fn new(start: u32, end: u32) -> IpRange {
        IpRange {
            start: start,
            end: end
        }
    }
    
    fn contains(&self, other: u32) -> bool {
        self.start <= other && other <= self.end
    }

    fn try_combine(&self, other: &IpRange) -> Option<IpRange> {
        if self.contains(other.start) && self.contains(other.end) {
            Some(IpRange::new(self.start, self.end))
        }
        else if other.contains(self.start) && other.contains(self.end) {
            Some(IpRange::new(other.start, other.end))
        }
        else if self.contains(other.start) && other.contains(self.end) {
            Some(IpRange::new(self.start, other.end))
        }
        else if other.contains(self.start) && self.contains(other.end) {
            Some(IpRange::new(other.start, self.end))
        }
        else {
            None
        }
    }
}

fn main() {
    let mut ranges = read_file();
    optimize_ranges(&mut ranges);

    let mut allowed = Vec::new();

    // current will be in the u32 range while it's in the loop, but
    // needs to be a u64 to pass u32::MAX. Otherwise it will just
    // overflow and run forever.
    let mut current: u64 = 0;
    while current <= u32::MAX as u64 {
        match ranges.iter().find(|range| range.contains(current as u32)) {
            Some(blacklisting_range) => {
                current = blacklisting_range.end as u64 + 1;
            },
            None => {
                allowed.push(current as u32);
                current += 1;
            }
        }
    }
    
    let min_not_in_range = allowed[0];
    let allowed_count = allowed.len();

    println!("Min not in any range: {}", min_not_in_range);
    println!("Allowed count: {}", allowed_count);
}

fn read_file() -> Vec<IpRange> {
    let file = BufReader::new(File::open("input.txt").unwrap());
    file.lines()
        .filter_map(|line| {
            let line = line.unwrap();
            let mut split = line.split('-');
            let start = split.next();
            let end = split.next();
            match (start, end) {
                (Some(start), Some(end)) => Some(IpRange {
                    start: start.parse().unwrap(),
                    end: end.parse().unwrap()
                }),
                _ => None
            }
        })
        .collect()
}

fn optimize_ranges(ranges: &mut Vec<IpRange>) {
    let mut before_count = ranges.len();
    let mut after_count = 0;
    while before_count != after_count {
        before_count = ranges.len();
        ranges.sort_by_key(|r| r.start);
        
        let mut i = 0;
        while i < ranges.len()-1 {
            match ranges[i].try_combine(&ranges[i+1]) {
                Some(combined) => {
                    ranges[i] = combined;
                    ranges.remove(i+1);
                },
                None => {}
            }
            i += 1;
        }

        after_count = ranges.len();

        println!("Number of ranges {} => {}", before_count, after_count);
    }
}