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
146
147
148
//! # Trick Shot
//!
//! Although this problem is easy to brute force, we can apply some reasoning and simplify both parts.
//!
//! ## Part One
//! Part one can be solved analytically. Movement upwards in the positive y direction is symmetrical.
//! For example launching a probe at a y-velocity of 5 initially,
//! would result in a  speed and y-position:
//!
//! ```text
//!     Time:       0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12
//!     Speed:      5,  4,  3,  2,  1,  0, -1, -2, -3, -4, -5, -6, -7
//!     Y-Position: 0,  5,  9, 12, 14, 15, 15, 14, 12,  9,  5,  0, -6
//! ```
//!
//! The maximum y velocity is reached when we *just* touch the target area on the way down at the
//! bottom y-coordinate. For the example above, if the bottom y coordinate was -6 then the maximum
//! initial upwards velocity is one less, our starting velocity of 5.
//!
//! The maximum height is `5 + 4 + 3 + 2 + 1`, which is the sum from 1 to n given by the formula for
//! triangular numbers [`(n * (n + 1) / 2`](https://en.wikipedia.org/wiki/Triangular_number#Formula).
//!
//! ## Part Two
//! A brute force solution would check every possible combination of `x` and `y` for a total
//! complexity of `O(xy)`. By thinking in terms of time `t` instead and applying a dynamic
//! programming solution we can instead solve in a complexity of `O(x + y)`by treating `x` and `y`
//! independently.
//!
//! We create 2 `vecs`. The first `new` counts how many x-velocity values enter the target area at
//! time `t` for the first time, only considering horizontal movement. The second `continuing`
//! counts how many are still in the target area at time `t`.
//!
//! For example using the sample `target area: x=20..30, y=-10..-5` gives a progression:
//!
//! ```text
//!     X-Velocity : 6
//!     Time:        0,  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
//!     New:         0,  0, 0, 0, 0, 1, 0, 0, 0, 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
//!     Continuing:  0,  0, 0, 0, 0, 0, 1, 1, 1, 1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1
//!
//!     X-Velocity : 7
//!     Time:        0,  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
//!     New:         0,  0, 0, 0, 1, 1, 0, 0, 0, 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
//!     Continuing:  0,  0, 0, 0, 0, 1, 2, 2, 2, 2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2
//!
//!     X-Velocity : 8
//!     Time:        0,  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
//!     New:         0,  0, 0, 1, 1, 1, 0, 0, 0, 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
//!     Continuing:  0,  0, 0, 0, 1, 2, 2, 2, 2, 2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2
//!
//!     ...
//!
//!     X-Velocity : 30
//!     Time:        0,  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
//!     New:         0, 11, 5, 3, 1, 1, 0, 0, 0, 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
//!     Continuing:  0,  0, 0, 1, 2, 2, 2, 2, 2, 2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2
//! ```
//!
//! Then for each y velocity value we find the time when it enters the target area. The first time
//! this happens we add *both* `new` and `continuing` to the total. For subsequent times while we're
//! still in the target area we add only the `new` values, as the `continuing` are trajectories
//! that we've already considered. For example for an initial y-velocity of 0:
//!
//! ```text
//!     Time:       0,   1,   2,     3,   4
//!     Speed:      0,  -1,  -2,    -3,  -4
//!     Y-Position: 0,  -1,  -3,    -6, -10
//!     Total:      0,   0,   0, 3 + 1,   5
//! ```
//!
//! Summing this for all y-velocity values gives the desired result.
use crate::util::iter::*;
use crate::util::parse::*;

type Input = [i32; 4];

pub fn parse(input: &str) -> Input {
    input.iter_signed().chunk::<4>().next().unwrap()
}

pub fn part1(input: &Input) -> i32 {
    let &[_, _, bottom, _] = input;
    let n = -(bottom + 1);
    n * (n + 1) / 2
}

pub fn part2(input: &Input) -> usize {
    let &[left, right, bottom, top] = input;

    let mut n = 1;
    while n * (n + 1) / 2 < left {
        n += 1;
    }

    let min_dx = n;
    let max_dx = right + 1;
    let min_dy = bottom;
    let max_dy = -bottom;

    let max_t = (1 - 2 * bottom) as usize;
    let mut new = vec![0; max_t];
    let mut continuing = vec![0; max_t];
    let mut total = 0;

    for dx in min_dx..max_dx {
        let mut x = 0;
        let mut dx = dx;
        let mut first = true;

        for t in 0..max_t {
            if x > right {
                break;
            }
            if x >= left {
                if first {
                    first = false;
                    new[t] += 1;
                } else {
                    continuing[t] += 1;
                }
            }
            x += dx;
            dx = (dx - 1).max(0);
        }
    }

    for dy in min_dy..max_dy {
        let mut y = 0;
        let mut dy = dy;
        let mut t = 0;
        let mut first = true;

        while y >= bottom {
            if y <= top {
                if first {
                    first = false;
                    total += continuing[t];
                }
                total += new[t];
            }
            y += dy;
            dy -= 1;
            t += 1;
        }
    }

    total
}