const std = @import("std"); const v2d = [2]i32; pub fn main() void { const input = @embedFile("input"); std.debug.print("Part 1: {}\n", .{nVisitedSnake(input, 2)}); std.debug.print("Part 2: {}\n", .{nVisitedSnake(input, 10)}); } pub fn nVisitedSnake(input: []const u8, comptime len: usize) usize { if (len < 2) @compileError("The length has to be greater than 1."); var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); const allocator = arena.allocator(); var knots = [_]v2d{v2d{ 0, 0 }} ** len; var visited = std.ArrayList(v2d).init(allocator); var iter = std.mem.split(u8, input, "\n"); while (iter.next()) |line| { // Line not valid if (line.len < 2) continue; // Get the number, continue on error const num = std.fmt.parseInt(usize, line[2..], 10) catch continue; // Direction of said movement const dir: v2d = switch (line[0]) { 'U' => .{ 0, 1 }, 'D' => .{ 0, -1 }, 'R' => .{ 1, 0 }, 'L' => .{ -1, 0 }, else => unreachable, }; // Move the head one step at a time var step: usize = 0; while (step < num) : (step += 1) { // Move the head knots[0][0] += dir[0]; knots[0][1] += dir[1]; // Go through each pair of "head/tail" knots var tailIdx: usize = 1; while (tailIdx < knots.len) : (tailIdx += 1) { const head = &knots[tailIdx - 1]; const tail = &knots[tailIdx]; var axis: u8 = 0; while (axis < 2) : (axis += 1) { const diff = head[axis] - tail[axis]; const dist = std.math.absInt(diff) catch unreachable; if (dist >= 2) { // Move it to where the head is -1 tail[axis] = head[axis] - @divTrunc(dist, diff); // If one axis is at a +2 distance, snap the other one const otherAxis = (axis + 1) % 2; tail[otherAxis] = head[otherAxis]; break; } } } // Register the poisition again if it hasn't been saved before const tail = knots[knots.len - 1]; for (visited.items) |coord| { if (coord[0] == tail[0] and coord[1] == tail[1]) break; } else { visited.append(tail) catch unreachable; } } } return visited.items.len; }