Skip to content

Commit 4c09d5c

Browse files
committed
Day 17 part 1-2
1 parent 6392a98 commit 4c09d5c

File tree

3 files changed

+331
-0
lines changed

3 files changed

+331
-0
lines changed

day17/input_test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
target area: x=20..30, y=-10..-5

day17/part1.zig

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
const std = @import("std");
2+
3+
const sqrt = std.math.sqrt;
4+
const abs = std.math.absInt;
5+
const max = std.math.max;
6+
const min = std.math.min;
7+
8+
fn floor(x: f64) i32 {
9+
return @floatToInt(i32, @floor(x));
10+
}
11+
12+
fn ceil(x: f64) i32 {
13+
return @floatToInt(i32, @ceil(x));
14+
}
15+
16+
fn float(x: i32) f64 {
17+
return @intToFloat(f64, x);
18+
}
19+
20+
fn xExists(y: i32, s: i32, x1: i32, x2: i32) bool {
21+
// Solve for x:
22+
// if s < x:
23+
// x1 <= sum x-s+1 .. x <= x2 <==>
24+
// x1 <= s*x - (s-1)*s/2 <= x2 <==>
25+
// x1/s + (s - 1)/2 <= x <= x2/s + (s - 1)/2
26+
{
27+
const min_sx = x1 + @divExact((s - 1) * s, 2);
28+
const max_sx = x2 + @divExact((s - 1) * s, 2);
29+
if (min_sx <= max_sx and s * s < min_sx) {
30+
return true;
31+
}
32+
}
33+
34+
// if s <= x:
35+
// x1 <= (x*x + x) / 2 <= x2
36+
// Solve:
37+
// sx1 = (-1 - sqrt(1 + 8*x1)) / 2
38+
// sx2 = (-1 + sqrt(1 + 8*x1)) / 2
39+
//
40+
// sx3 = (-1 - sqrt(1 + 8*x2)) / 2
41+
// sx4 = (-1 + sqrt(1 + 8*x2)) / 2
42+
//
43+
// so:
44+
// (x <= sx1 or sx2 <= x) and
45+
// (sx3 <= x <= sx4) <==>
46+
// (max(sx3, s) <= x <= min(sx1, sx4)) or
47+
// (max(sx2, sx3, s) <= x <= sx4)
48+
{
49+
const sx1 = floor((-1 - sqrt(float(1 + 8 * x1))) / 2);
50+
const sx2 = ceil((-1 + sqrt(float(1 + 8 * x1))) / 2);
51+
const sx3 = ceil((-1 - sqrt(float(1 + 8 * x2))) / 2);
52+
const sx4 = floor((-1 + sqrt(float(1 + 8 * x2))) / 2);
53+
{
54+
const min_x = max(sx3, 0);
55+
const max_x = min(min(sx1, sx4), s);
56+
if (min_x <= max_x) {
57+
return true;
58+
}
59+
}
60+
{
61+
const min_x = max(max(sx2, sx3), 0);
62+
const max_x = min(sx4, s);
63+
if (min_x <= max_x) {
64+
return true;
65+
}
66+
}
67+
}
68+
return false;
69+
}
70+
71+
pub fn main() !void {
72+
var file = try std.fs.cwd().openFile("input", .{});
73+
defer file.close();
74+
75+
var buf_reader = std.io.bufferedReader(file.reader());
76+
var in_stream = buf_reader.reader();
77+
78+
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
79+
defer arena.deinit();
80+
81+
const allocator = &arena.allocator;
82+
var read_buf: [1024]u8 = undefined;
83+
84+
const line = (try in_stream.readUntilDelimiterOrEof(&read_buf, '\n')).?;
85+
// e.g. "target area: x=20..30, y=-10..-5"
86+
87+
var split = std.mem.split(line["target area: ".len..], ", ");
88+
var x_str = std.mem.split(split.next().?["x=".len..], "..");
89+
const x1 = try std.fmt.parseInt(i32, x_str.next().?, 10);
90+
const x2 = try std.fmt.parseInt(i32, x_str.next().?, 10);
91+
92+
var y_str = std.mem.split(split.next().?["y=".len..], "..");
93+
const y1 = try std.fmt.parseInt(i32, y_str.next().?, 10);
94+
const y2 = try std.fmt.parseInt(i32, y_str.next().?, 10);
95+
96+
// s: step
97+
// x: initial x velocity
98+
// y: initial y velocity
99+
100+
// f(s,x) =
101+
// if (s < x) -(s-x)(s-x-1)/2 + x(x+1)/2
102+
// if (s >= x) x(x+1)/2
103+
104+
// find max y such that there exists an s, there exists an x
105+
// y1 <= (-s^2 + 2sy + s)/2 <= y2
106+
// x1 <= f(s,x) <= x2
107+
108+
// We hit target with y iff there exist a step s:
109+
// y1 <= -(s-y)(s-y-1)/2 + y*(y+1)/2 <= y2 <==>
110+
// y1 <= (-s*s + s*y + s + y*s)/2 <= y2 <==>
111+
// y1 <= (-s^2 + s*(2*y + 1))/2 <= y2 <==>
112+
// 0 <= -s^2 + s*(2*y + 1) - 2*y1
113+
// and 0 >= -s^2 + s*(2*y + 1) - 2*y2
114+
115+
// solve for s:
116+
// y1s1 = (2*y + 1 - sqrt(4*y^2 + 4*y + 1 - 8*y1)) / 2
117+
// y1s2 = (2*y + 1 + sqrt(4*y^2 + 4*y + 1 - 8*y1)) / 2
118+
119+
// y2s1 = (2*y + 1 - sqrt(4*y^2 + 4*y + 1 - 8*y2)) / 2
120+
// y2s1 = (2*y + 1 + sqrt(4*y^2 + 4*y + 1 - 8*y2)) / 2
121+
122+
// y1s1 <= s <= y1s2 and (y2s2 <= s or s <= y2s1) <==>
123+
// max(y2s2,y1s1) <= s <= y1s2 or y1s1 <= s <= min(y1s2,y2s1)
124+
125+
// Otherwise we cannot hit target with y:
126+
const highest_y: i32 = max(try abs(y1), try abs(y2));
127+
const lowest_y: i32 = 0;
128+
129+
var y: i32 = highest_y;
130+
var s: i32 = undefined;
131+
y_loop: while (y >= lowest_y) : (y -= 1) {
132+
const y1s1 = (float(2 * y + 1) - sqrt(float(4 * y * y + 4 * y + 1 - 8 * y1))) / 2;
133+
const y1s2 = (float(2 * y + 1) + sqrt(float(4 * y * y + 4 * y + 1 - 8 * y1))) / 2;
134+
135+
const y2s1 = (float(2 * y + 1) - sqrt(float(4 * y * y + 4 * y + 1 - 8 * y2))) / 2;
136+
const y2s2 = (float(2 * y + 1) + sqrt(float(4 * y * y + 4 * y + 1 - 8 * y2))) / 2;
137+
138+
// iterate s
139+
s = max(max(ceil(y2s2), ceil(y1s1)), 0);
140+
while (s <= floor(y1s2)) : (s += 1) {
141+
if (xExists(y, s, x1, x2)) break :y_loop;
142+
}
143+
s = max(ceil(y1s1), 0);
144+
while (s <= min(floor(y1s2), floor(y2s1))) : (s += 1) {
145+
if (xExists(y, s, x1, x2)) break :y_loop;
146+
}
147+
}
148+
149+
const stdout = std.io.getStdOut().writer();
150+
try stdout.print("{d}\n", .{y_s(y, y)});
151+
}
152+
153+
fn y_s(y: i32, s: i32) i32 {
154+
return @divExact(-s * s + 2 * s * y + s, 2);
155+
}

day17/part2.zig

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
const std = @import("std");
2+
3+
const sqrt = std.math.sqrt;
4+
const abs = std.math.absInt;
5+
const max = std.math.max;
6+
const min = std.math.min;
7+
8+
fn floor(x: f64) i32 {
9+
return @floatToInt(i32, @floor(x));
10+
}
11+
12+
fn ceil(x: f64) i32 {
13+
return @floatToInt(i32, @ceil(x));
14+
}
15+
16+
fn float(x: i32) f64 {
17+
return @intToFloat(f64, x);
18+
}
19+
20+
fn divCeil(x: i32, y: i32) i32 {
21+
const res = @divFloor(x, y);
22+
return if (res * y == x) res else res + 1;
23+
}
24+
25+
pub fn main() !void {
26+
var file = try std.fs.cwd().openFile("input", .{});
27+
defer file.close();
28+
29+
var buf_reader = std.io.bufferedReader(file.reader());
30+
var in_stream = buf_reader.reader();
31+
32+
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
33+
defer arena.deinit();
34+
35+
const allocator = &arena.allocator;
36+
var read_buf: [1024]u8 = undefined;
37+
38+
const line = (try in_stream.readUntilDelimiterOrEof(&read_buf, '\n')).?;
39+
// e.g. "target area: x=20..30, y=-10..-5"
40+
41+
var split = std.mem.split(line["target area: ".len..], ", ");
42+
var x_str = std.mem.split(split.next().?["x=".len..], "..");
43+
const x1 = try std.fmt.parseInt(i32, x_str.next().?, 10);
44+
const x2 = try std.fmt.parseInt(i32, x_str.next().?, 10);
45+
46+
var y_str = std.mem.split(split.next().?["y=".len..], "..");
47+
const y1 = try std.fmt.parseInt(i32, y_str.next().?, 10);
48+
const y2 = try std.fmt.parseInt(i32, y_str.next().?, 10);
49+
50+
// s: step
51+
// x: initial x velocity
52+
// y: initial y velocity
53+
54+
// f(s,x) =
55+
// if (s < x) -(s-x)(s-x-1)/2 + x(x+1)/2
56+
// if (s >= x) x(x+1)/2
57+
58+
// find max y such that there exists an s, there exists an x
59+
// y1 <= (-s^2 + 2sy + s)/2 <= y2
60+
// x1 <= f(s,x) <= x2
61+
62+
// We hit target with y iff there exist a step s:
63+
// y1 <= -(s-y)(s-y-1)/2 + y*(y+1)/2 <= y2 <==>
64+
// y1 <= (-s*s + s*y + s + y*s)/2 <= y2 <==>
65+
// y1 <= (-s^2 + s*(2*y + 1))/2 <= y2 <==>
66+
// 0 <= -s^2 + s*(2*y + 1) - 2*y1
67+
// and 0 >= -s^2 + s*(2*y + 1) - 2*y2
68+
69+
// solve for s:
70+
// y1s1 = (2*y + 1 - sqrt(4*y^2 + 4*y + 1 - 8*y1)) / 2
71+
// y1s2 = (2*y + 1 + sqrt(4*y^2 + 4*y + 1 - 8*y1)) / 2
72+
73+
// y2s1 = (2*y + 1 - sqrt(4*y^2 + 4*y + 1 - 8*y2)) / 2
74+
// y2s1 = (2*y + 1 + sqrt(4*y^2 + 4*y + 1 - 8*y2)) / 2
75+
76+
// y1s1 <= s <= y1s2 and (y2s2 <= s or s <= y2s1) <==>
77+
// max(y2s2,y1s1) <= s <= y1s2 or y1s1 <= s <= min(y1s2,y2s1)
78+
79+
// Otherwise we cannot hit target with y:
80+
const highest_y: i32 = max(try abs(y1), try abs(y2));
81+
const lowest_y: i32 = -highest_y;
82+
83+
var y: i32 = highest_y;
84+
var s: i32 = undefined;
85+
var num: u32 = 0;
86+
while (y >= lowest_y) : (y -= 1) {
87+
const y1s1 = (float(2 * y + 1) - sqrt(float(4 * y * y + 4 * y + 1 - 8 * y1))) / 2;
88+
const y1s2 = (float(2 * y + 1) + sqrt(float(4 * y * y + 4 * y + 1 - 8 * y1))) / 2;
89+
90+
const y2s1 = (float(2 * y + 1) - sqrt(float(4 * y * y + 4 * y + 1 - 8 * y2))) / 2;
91+
const y2s2 = (float(2 * y + 1) + sqrt(float(4 * y * y + 4 * y + 1 - 8 * y2))) / 2;
92+
93+
var xs = std.AutoHashMap(i32, void).init(allocator);
94+
defer xs.deinit();
95+
96+
// iterate s
97+
s = max(max(ceil(y2s2), ceil(y1s1)), 0);
98+
while (s <= floor(y1s2)) : (s += 1) {
99+
const intervals = intervalX(y, s, x1, x2);
100+
for (intervals) |interval| {
101+
var x: i32 = interval.from;
102+
while (x <= interval.to) : (x += 1) {
103+
try xs.put(x, {});
104+
}
105+
}
106+
}
107+
s = max(ceil(y1s1), 0);
108+
while (s <= min(floor(y1s2), floor(y2s1))) : (s += 1) {
109+
const intervals = intervalX(y, s, x1, x2);
110+
for (intervals) |interval| {
111+
var x: i32 = interval.from;
112+
while (x <= interval.to) : (x += 1) {
113+
try xs.put(x, {});
114+
}
115+
}
116+
}
117+
num += xs.count();
118+
}
119+
120+
const stdout = std.io.getStdOut().writer();
121+
try stdout.print("{d}\n", .{num});
122+
}
123+
124+
const Interval = struct {
125+
from: i32,
126+
to: i32,
127+
};
128+
129+
fn intervalX(y: i32, s: i32, x1: i32, x2: i32) [3]Interval {
130+
var intervals: [3]Interval = undefined;
131+
// Solve for x:
132+
// if s < x:
133+
// x1 <= sum x-s+1 .. x <= x2 <==>
134+
// x1 <= s*x - (s-1)*s/2 <= x2 <==>
135+
// x1/s + (s - 1)/2 <= x <= x2/s + (s - 1)/2
136+
137+
var num: i32 = 0;
138+
{
139+
const min_x = max(divCeil(x1 + @divExact((s - 1) * s, 2), s), s + 1);
140+
const max_x = @divFloor(x2 + @divExact((s - 1) * s, 2), s);
141+
intervals[0] = Interval{ .from = min_x, .to = max_x };
142+
}
143+
144+
// if s <= x:
145+
// x1 <= (x*x + x) / 2 <= x2
146+
// Solve:
147+
// sx1 = (-1 - sqrt(1 + 8*x1)) / 2
148+
// sx2 = (-1 + sqrt(1 + 8*x1)) / 2
149+
//
150+
// sx3 = (-1 - sqrt(1 + 8*x2)) / 2
151+
// sx4 = (-1 + sqrt(1 + 8*x2)) / 2
152+
//
153+
// so:
154+
// (x <= sx1 or sx2 <= x) and
155+
// (sx3 <= x <= sx4) <==>
156+
// (max(sx3, s) <= x <= min(sx1, sx4)) or
157+
// (max(sx2, sx3, s) <= x <= sx4)
158+
{
159+
const sx1 = floor((-1 - sqrt(float(1 + 8 * x1))) / 2);
160+
const sx2 = ceil((-1 + sqrt(float(1 + 8 * x1))) / 2);
161+
const sx3 = ceil((-1 - sqrt(float(1 + 8 * x2))) / 2);
162+
const sx4 = floor((-1 + sqrt(float(1 + 8 * x2))) / 2);
163+
{
164+
const min_x = max(sx3, 0);
165+
const max_x = min(min(sx1, sx4), s);
166+
intervals[1] = Interval{ .from = min_x, .to = max_x };
167+
}
168+
{
169+
const min_x = max(max(sx2, sx3), 0);
170+
const max_x = min(sx4, s);
171+
intervals[2] = Interval{ .from = min_x, .to = max_x };
172+
}
173+
}
174+
return intervals;
175+
}

0 commit comments

Comments
 (0)