const std = @import("std"); const aoc = @import("./aoc.zig"); const cli = @import("./cli.zig"); const Allocator = std.mem.Allocator; const StringArray = std.ArrayList([]const u8); const DayHashMap = std.StringHashMap(aoc.Day); fn create_day(comptime module: anytype) aoc.Day { var part1: ?aoc.Solver = null; if (@hasDecl(module, "part1")) { part1 = @field(module, "part1"); } var part2: ?aoc.Solver = null; if (@hasDecl(module, "part2")) { part2 = @field(module, "part2"); } return .{ .part1 = part1, .part2 = part2 }; } const Days = [_]aoc.Day{ create_day(@import("./day1.zig")), create_day(@import("./day2.zig")), create_day(@import("./day3.zig")), create_day(@import("./day4.zig")), create_day(@import("./day5.zig")), create_day(@import("./day6.zig")), create_day(@import("./day7.zig")), create_day(@import("./day8.zig")), create_day(@import("./day9.zig")), create_day(@import("./day10.zig")), create_day(@import("./day11.zig")), create_day(@import("./day12.zig")), create_day(@import("./day13.zig")), create_day(@import("./day14.zig")), create_day(@import("./day15.zig")), create_day(@import("./day16.zig")), create_day(@import("./day17.zig")), }; fn kilobytes(count: u32) u32 { return count * 1024; } fn run(allocator: Allocator, args: *cli) !u8 { if (args.day > Days.len) { std.debug.print("ERROR: Day {} does not exist\n", .{args.day}); return 255; } const day = Days[args.day-1]; if ((args.part == 1 and day.part1 == null) or (args.part == 2 and day.part2 == null)) { std.debug.print("ERROR: Part {} for day {} does not exist\n", .{args.part, args.day}); return 255; } var file = std.fs.cwd().openFile(args.input_file, .{}) catch |err| switch (err) { error.FileNotFound => { std.debug.print("ERROR: Input file '{s}' does not exist\n", .{args.input_file}); return 255; }, else => return err }; defer file.close(); var buf_reader = std.io.bufferedReader(file.reader()); var reader = buf_reader.reader(); var lines = StringArray.init(allocator); defer { for (lines.items) |line| { allocator.free(line); } lines.deinit(); } while (try reader.readUntilDelimiterOrEofAlloc(allocator, '\n', kilobytes(32))) |line| { try lines.append(std.mem.trimRight(u8, line, &.{'\r'})); } var input = aoc.Input{ .allocator = allocator, .lines = lines.items }; const start_time = std.time.microTimestamp(); var result: aoc.Result = undefined; if (args.part == 1) { result = try day.part1.?(&input); } else if (args.part == 2) { result = try day.part2.?(&input); } else { unreachable; } const end_time = std.time.microTimestamp(); switch (result) { .uint => std.debug.print("{}\n", .{result.uint}), .int => std.debug.print("{}\n", .{result.int}), } const duration = end_time - start_time; std.debug.print("Time taken: {}ms ({}us)\n", .{@divTrunc(duration, std.time.us_per_ms), duration}); return 0; } fn get_input(allocator: Allocator, args: *cli) !u8 { var client = std.http.Client{ .allocator = allocator }; defer client.deinit(); if (!try std.process.hasEnvVar(allocator, "AOC_SESSION")) { std.debug.print("ERROR: Missing environment variable AOC_SESSION\n", .{}); return 255; } const aoc_session = try std.process.getEnvVarOwned(allocator, "AOC_SESSION"); defer allocator.free(aoc_session); const cookie_header = try std.fmt.allocPrint(allocator, "session={s}", .{aoc_session}); defer allocator.free(cookie_header); const url_str = try std.fmt.allocPrint(allocator, "https://adventofcode.com/2023/day/{}/input", .{args.day}); defer allocator.free(url_str); const uri = std.Uri.parse(url_str) catch unreachable; var server_header_buffer: [1024]u8 = undefined; var request = try client.open(.GET, uri, .{ .server_header_buffer = &server_header_buffer, .extra_headers = &.{ .{ .name = "cookie", .value = cookie_header }, .{ .name = "accept", .value = "*/*" }, } }); defer request.deinit(); try request.send(); try request.wait(); if (request.response.status != .ok) { if (request.response.status == .not_found) { std.debug.print("ERROR: Day not found\n", .{}); } else if (request.response.status == .bad_request) { std.debug.print("ERROR: Invalid session token\n", .{}); } else { std.debug.print("ERROR: unknown error, status code {}\n", .{request.response.status}); } return 255; } const body = try request.reader().readAllAlloc(allocator, kilobytes(128)); defer allocator.free(body); var file = try std.fs.cwd().createFile(args.input_file, .{ }); defer file.close(); try file.writeAll(body); return 0; } pub fn main() !u8 { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; const allocator = gpa.allocator(); defer { if (gpa.deinit() == .leak) @panic("Leaked memory"); } var args = cli.init(allocator) catch |err| switch (err) { error.cli => return 255, else => return err } ; defer args.deinit(); if (args.action == .Run) { return run(allocator, &args); } else { return get_input(allocator, &args); } } test { _ = @import("./day1.zig"); _ = @import("./day2.zig"); _ = @import("./day3.zig"); _ = @import("./day4.zig"); _ = @import("./day5.zig"); _ = @import("./day6.zig"); _ = @import("./day7.zig"); _ = @import("./day8.zig"); _ = @import("./day9.zig"); _ = @import("./day10.zig"); _ = @import("./day11.zig"); _ = @import("./day12.zig"); _ = @import("./day13.zig"); _ = @import("./day14.zig"); _ = @import("./day15.zig"); _ = @import("./day16.zig"); _ = @import("./day17.zig"); }