summaryrefslogtreecommitdiff
path: root/2018/src/day10.rs
blob: 99be24413530388eaa5e51ccb5c2467930eaecc3 (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
use std::io;
use std::io::BufRead;
use std::cmp::max;
use std::collections::HashSet;

fn parse_pt(l: &str) -> ((i32, i32), (i32, i32)) {
    let i1 = l.find('<').unwrap();
    let i2 = l[i1..].find(',').unwrap() + i1;
    let i3 = l[i2..].find('>').unwrap() + i2;
    let i4 = l[i3..].find('<').unwrap() + i3;
    let i5 = l[i4..].find(',').unwrap() + i4;
    let i6 = l[i5..].find('>').unwrap() + i5;

    let s1 = &l[i1+1..i2];
    let s2 = &l[i2+1..i3];
    let s3 = &l[i4+1..i5];
    let s4 = &l[i5+1..i6];

    ((s1.trim().parse().unwrap(), s2.trim().parse().unwrap()),
        (s3.trim().parse().unwrap(), s4.trim().parse().unwrap()))
}

fn dist((x, y): (i32, i32), (u, v): (i32, i32)) -> i32 {
    return ((u - x) as f32 * (u - x) as f32 + (v - y) as f32 * (v - y) as f32).sqrt() as i32;
}

pub fn main<T: BufRead>(reader: T) -> io::Result<(String, String)> {
    let dots: Vec<((i32, i32), (i32, i32))> =
        reader.lines().map(|l| l.unwrap())
            .map(|l| parse_pt(&l))
            .collect();

    let mut pts: Vec<_> = dots.iter().map(|&(xy, _)| xy).collect();
    let vels: Vec<_> = dots.iter().map(|&(_, v)| v).collect();

    let maxvel = vels.iter().map(|&xy| dist(xy, (0, 0)) + 1).max().unwrap();

    let mut maxscore = -1;
    let mut maxat;
    let mut iter = 0;
    loop {
        let mut has_dot: HashSet<(i32, i32)> = HashSet::new();

        let mut avg = (0, 0);
        let mut score = 0;
        for &xy in &pts {
            if has_dot.contains(&xy) { score += 1; }
            else { has_dot.insert(xy); }
            avg.0 += xy.0;
            avg.1 += xy.1;
        }
        avg.0 /= dots.len() as i32;
        avg.1 /= dots.len() as i32;

        let maxdist = pts.iter().map(|&xy| dist(xy, avg)).max().unwrap();

        if score > maxscore {
            maxscore = score;
            maxat = (iter, pts.clone(), has_dot.clone());
            if maxscore > dots.len() as i32 / 2 {
                break;
            }
        }

        let multiplier =
                if maxdist > maxvel && maxdist > 100 { max(1, (maxdist - 100) / maxvel) }
                else { 1 };

        // println!("iter={} multiplier={} maxdist={} maxvel={}", iter, multiplier, maxdist, maxvel);

        for i in 0..dots.len() {
            pts[i].0 += multiplier * vels[i].0;
            pts[i].1 += multiplier * vels[i].1;
        }

        iter += multiplier;
    }

    let mut part1 = String::new();

    let part2 = maxat.0.to_string();
    let pts = maxat.1;
    let has_dot = maxat.2;
    let minx = pts.iter().map(|&(x, _)| x).min().unwrap();
    let miny = pts.iter().map(|&(_, y)| y).min().unwrap();
    let maxx = pts.iter().map(|&(x, _)| x).max().unwrap();
    let maxy = pts.iter().map(|&(_, y)| y).max().unwrap();
    for y in miny..maxy+1 {
        if y != miny { part1 += "\n"; }
        for x in minx..maxx + 1 {
            if has_dot.contains(&(x, y)) { part1 += "#"; }
            else { part1 += "."; }
        }
    }

    Ok((part1, part2))
}