aboutsummaryrefslogtreecommitdiff
path: root/day6.zig
blob: 94023a069eb3514b03bc42b5102af167e3f63e93 (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
const std = @import("std");

pub fn main() !void {
    try std.fmt.format(std.io.getStdOut().writer(), "day6 pt1: {}\n", .{try solve_pt1(std.heap.page_allocator, puzzle_input)});
    try std.fmt.format(std.io.getStdOut().writer(), "day6 pt2: {}\n", .{try solve_pt1(std.heap.page_allocator, puzzle_input2)});
}

fn readnums(a: std.mem.Allocator, input: []const u8) ![]u64 {
    var toks = std.mem.tokenize(u8, input, " ");
    _  = toks.next() orelse return error.NoLabel;
    var al = std.ArrayList(u64).init(a);
    defer al.deinit();
    while (toks.next()) |tok| {
        try al.append(try std.fmt.parseInt(u64, tok, 10));
    }
    return try al.toOwnedSlice();
}

fn solve_pt1(a: std.mem.Allocator, input: []const u8) !u64 {
    var spl = std.mem.split(u8, input, "\n");
    const times = try readnums(a, spl.first());
    defer a.free(times);
    const distances = try readnums(a, spl.next() orelse return error.NoDistance);
    defer a.free(distances);

    var sum: ?u64 = null;
    for (times, distances) |time, distance| {
        const ftime: f64 = @floatFromInt(time);
        const fdistance: f64 = @floatFromInt(distance);
        const disc = (std.math.pow(f64, ftime, 2)) - 4 * fdistance;
        if (disc <= 0) {
            // std.log.warn("discriminant {}, no possible solutions", .{disc});
            continue;
        }
        const x_low: f64 = ((ftime) - @sqrt(disc)) / 2 ;
        const x_high: f64 = ((ftime) + @sqrt(disc)) / 2 ;
        // std.log.warn("x_low {} x_high {}", .{x_low, x_high});
        // So these are _strict_ bounds. I want the next integer _strictly_ greater than x_low
        // and the next integer _strictly_ less than x_high
        const x_low_i: u64 = if (@ceil(x_low) == x_low) (@as(u64, @intFromFloat(x_low)) + 1) else @intFromFloat(@ceil(x_low));
        const x_high_i: u64 = if (@floor(x_high) == x_high) (@as(u64, @intFromFloat(x_high)) - 1) else @intFromFloat(@floor(x_high));
        const diff = (x_high_i + 1) - x_low_i; // inclusive range
        
        if (sum) |s| {
            sum = s * diff;
        } else {
            sum = diff;
        }
    }
    return sum orelse error.NoRaces;
}

test "pt1" {
    try std.testing.expectEqual(@as(u64, 288), try solve_pt1(std.testing.allocator, test_input));
}

const test_input = 
\\Time:      7  15   30
\\Distance:  9  40  200
;
const puzzle_input = 
\\Time:        49     97     94     94
\\Distance:   263   1532   1378   1851
;
const puzzle_input2 = 
\\Time:        49979494
\\Distance:   263153213781851
;