diff --git a/src/day5.zig b/src/day5.zig index e4b490c..13a631e 100644 --- a/src/day5.zig +++ b/src/day5.zig @@ -1,6 +1,5 @@ const aoc = @import("./aoc.zig"); const std = @import("std"); -const expect = std.testing.expect; const MappingRange = struct { src_start: u64, diff --git a/src/day6.zig b/src/day6.zig new file mode 100644 index 0000000..a7a9bcc --- /dev/null +++ b/src/day6.zig @@ -0,0 +1,115 @@ +const std = @import("std"); +const aoc = @import("./aoc.zig"); +const assert = std.debug.assert; + +const BoundedU32 = std.BoundedArray(u32, 32); + +const Input = struct { + time: BoundedU32, + distance: BoundedU32, +}; + +fn parse_numbers(result: *BoundedU32, text: []const u8) !void { + var i: usize = 0; + var tokens = std.mem.tokenizeScalar(u8, text, ' '); + while (tokens.next()) |token| : (i += 1) { + const number = try std.fmt.parseInt(u32, token, 10); + try result.append(number); + } +} + +fn parse_input(lines: []const []const u8) !Input { + var time = try BoundedU32.init(0); + var distance = try BoundedU32.init(0); + try parse_numbers(&time, lines[0][9..]); + try parse_numbers(&distance, lines[1][9..]); + assert(time.len == distance.len); + return Input{ .time = time, .distance = distance }; +} + +pub fn part1(input: *aoc.Input) !aoc.Result { + var parsed = try parse_input(input.lines); + + var answer: u32 = 1; + for (0..parsed.time.len) |i| { + const required_distance = parsed.distance.get(i); + const max_allowed_time = parsed.time.get(i); + + var min_time: u32 = 0; + var max_time: u32 = 0; + for (1..(max_allowed_time+1)) |speed_usize| { + const speed: u32 = @intCast(speed_usize); + const distance = speed*(max_allowed_time - speed); + if (distance <= required_distance) continue; + + if (max_time == 0) { + min_time = speed; + max_time = speed; + } else { + max_time = @max(max_time, speed); + } + } + + const margin = max_time - min_time + 1; + answer *= margin; + } + + return .{ .uint = answer }; +} + +pub fn part2(input: *aoc.Input) !aoc.Result { + var parsed = try parse_input(input.lines); + + var allowed_time: u64 = 0; + var required_distance: u64 = 0; + for (0..parsed.time.len) |i| { + const time = parsed.time.get(i); + if (allowed_time > 0) { + allowed_time *= try std.math.powi(u32, 10, std.math.log10(time)+1); + } + allowed_time += time; + + const distance = parsed.distance.get(i); + if (required_distance > 0) { + required_distance *= try std.math.powi(u32, 10, std.math.log10(distance)+1); + } + required_distance += distance; + } + + var min_time: u64 = 0; + var max_time: u64 = 0; + for (1..(allowed_time+1)) |speed_usize| { + const speed: u64 = @intCast(speed_usize); + const distance = speed*(allowed_time - speed); + if (distance <= required_distance) continue; + + if (max_time == 0) { + min_time = speed; + max_time = speed; + } else { + max_time = @max(max_time, speed); + } + } + + var answer = max_time - min_time + 1; + return .{ .uint = @intCast(answer) }; +} + +const example_input = [_][]const u8{ + "Time: 7 15 30", + "Distance: 9 40 200" +}; + +test "part 1 example" { + var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator }; + var actual = try part1(&input); + var expected: u32 = 288; + try std.testing.expectEqual(expected, actual.uint); +} + +test "part 2 example" { + var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator }; + var actual = try part2(&input); + var expected: u32 = 71503; + try std.testing.expectEqual(expected, actual.uint); +} diff --git a/src/main.zig b/src/main.zig index be07d51..4d7bcaf 100644 --- a/src/main.zig +++ b/src/main.zig @@ -26,6 +26,7 @@ const Days = [_]aoc.Day{ create_day(@import("day3.zig")), create_day(@import("day4.zig")), create_day(@import("day5.zig")), + create_day(@import("day6.zig")), }; fn kilobytes(count: u32) u32 { @@ -172,4 +173,5 @@ test { _ = @import("day3.zig"); _ = @import("day4.zig"); _ = @import("day5.zig"); + _ = @import("day6.zig"); }