const std = @import("std"); pub const Error = error{InvalidInput}; pub var debug = false; pub var debuglvl: u32 = 0; pub fn debugfmt(comptime fmt: []const u8, args: anytype) void { if (debug) std.debug.print(fmt, args); } const part_type = fn (alloc: std.mem.Allocator, input: []u8, args: [][]u8) anyerror!?[]u8; pub fn main(comptime part1: part_type, comptime part2: part_type, comptime sols: [2]?[]const u8) fn () anyerror!void { const impl = struct { fn main() !void { const envvar = std.os.getenv("AOC_DEBUG"); if (envvar != null) { debuglvl = try std.fmt.parseInt(u32, envvar.?, 10); debug = debuglvl != 0; } var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); var heapalloc = gpa.allocator(); const args = try std.process.argsAlloc(heapalloc); defer heapalloc.free(args); if (args.len < 2) return; var filename: []const u8 = std.mem.span("input"); for (std.os.environ) |v| { const kv = std.mem.span(v); if (std.mem.indexOfScalar(u8, kv, '=')) |sep| { if (sep == kv.len - 1) continue; if (std.mem.eql(u8, kv[0..sep], "AOC_INPUT")) { filename = kv[sep + 1 ..]; std.debug.print("Using input file: {s}\n", .{filename}); break; } else if (std.mem.eql(u8, kv[0..sep], "AOCDEBUG")) { debug = true; debuglvl = try std.fmt.parseInt(u32, kv[sep + 1 ..], 10); } } } const file = try std.fs.cwd().openFile(filename, .{}); const text = try file.reader().readAllAlloc(heapalloc, std.math.maxInt(u32)); defer heapalloc.free(text); var part = try std.fmt.parseInt(u8, args[1], 10); var answer: ?[]u8 = null; switch (part) { 1 => answer = try part1(heapalloc, text, args[2..]), 2 => answer = try part2(heapalloc, text, args[2..]), else => { std.debug.print("Invalid part number!\n", .{}); std.os.exit(1); }, } part = part - 1; if (answer != null) { const stdout = std.io.getStdOut().writer(); try std.fmt.format(stdout, "{s}\n", .{answer.?}); if (sols[part] != null and !std.mem.eql(u8, answer.?, sols[part].?)) { std.debug.print("Invalid answer\n", .{}); std.os.exit(1); } heapalloc.free(answer.?); } if (sols[part] == null) { std.debug.print("Missing solution\n", .{}); std.os.exit(1); } } }; return impl.main; } pub const Pos = struct { x: i64, y: i64, const Self = @This(); pub fn add(self: Self, other: Self) Self { return Self{ .x = self.x + other.x, .y = self.y + other.y }; } pub fn mult(self: Self, val: i64) Self { return Self{ .x = self.x * val, .y = self.y * val }; } }; pub const Dir = struct { pub const East = Pos{ .x = 1, .y = 0 }; pub const West = Pos{ .x = -1, .y = 0 }; pub const South = Pos{ .x = 0, .y = -1 }; pub const North = Pos{ .x = 0, .y = 1 }; pub const Name = enum(u8) { NORTH = 0, EAST = 1, SOUTH = 2, WEST = 3 }; pub const dirs = [_]Pos{ North, East, South, West }; pub fn get(name: Name) Pos { return dirs[@enumToInt(name)]; } pub fn nextCW(dir: usize, offset: usize) usize { return (dir + @intCast(u32, offset)) % @intCast(u32, dirs.len); } pub fn nextCCW(dir: usize, offset: usize) usize { const constrained = offset % dirs.len; if (dir >= constrained) { return dir - constrained; } else { return dirs.len - (constrained - dir); } } const cos90vs = [_]i32{ 1, 0, -1, 0 }; const sin90vs = [_]i32{ 0, 1, 0, -1 }; pub fn rotCW(pos: Pos, offset: usize) Pos { const constrained = (4 - offset % 4) % 4; return Pos{ .x = cos90vs[constrained] * pos.x - sin90vs[constrained] * pos.y, .y = sin90vs[constrained] * pos.x + cos90vs[constrained] * pos.y, }; } pub fn rotCCW(pos: Pos, offset: usize) Pos { const constrained = offset % 4; return Pos{ .x = cos90vs[constrained] * pos.x - sin90vs[constrained] * pos.y, .y = sin90vs[constrained] * pos.x + cos90vs[constrained] * pos.y, }; } }; pub fn unwrap(v: anytype) !@TypeOf(v.?) { if (v == null) return Error.InvalidInput; return v.?; }