const std = @import("std"); const PATH = "input/day08.txt"; const Str = []const u8; pub fn first(allocator: ?std.mem.Allocator) anyerror!usize { const lines = try parseInstructions(allocator.?); defer allocator.?.free(lines); var visited = try std.DynamicBitSet.initEmpty(allocator.?, lines.len); defer visited.deinit(); var accumulator: isize = 0; var idx: isize = 0; while (idx < lines.len) : (idx += 1) { if (visited.isSet(@intCast(usize, idx))) break; visited.set(@intCast(usize, idx)); const line = lines[@intCast(usize, idx)]; switch (line[0]) { 'n' => {}, 'a' => { var parts = std.mem.tokenize(u8, line, " "); _ = parts.next(); const diff = try std.fmt.parseInt(isize, parts.next().?, 0); accumulator += diff; }, 'j' => { var parts = std.mem.tokenize(u8, line, " "); _ = parts.next(); const diff = try std.fmt.parseInt(isize, parts.next().?, 0); idx += diff - 1; }, else => unreachable, } } return @intCast(usize, accumulator); } fn parseInstructions(allocator: std.mem.Allocator) ![]Str { const file = @embedFile(PATH); var lines = std.mem.split(u8, file, "\n"); var instr = std.ArrayList(Str).init(allocator); while (lines.next()) |line| { if (line.len == 0) break; try instr.append(line); } return instr.toOwnedSlice(); } pub fn second(allocator: ?std.mem.Allocator) anyerror!usize { const lines = try parseInstructions(allocator.?); defer allocator.?.free(lines); for (lines) |l, i| { const changed_line = switch (l[0]) { 'j', 'n' => @intCast(isize, i), else => continue, }; var accumulator: isize = 0; var visited = try std.DynamicBitSet.initEmpty(allocator.?, lines.len); defer visited.deinit(); var idx: isize = 0; while (idx < lines.len) : (idx += 1) { if (visited.isSet(@intCast(usize, idx))) break; visited.set(@intCast(usize, idx)); const line = lines[@intCast(usize, idx)]; var op: u8 = line[0]; if (idx == changed_line) { // flip op on changed_line, so the next switch part can be unchanged if (op == 'j') op = 'n' else op = 'j'; } switch (op) { 'n' => {}, 'a' => { var parts = std.mem.tokenize(u8, line, " "); _ = parts.next(); const diff = try std.fmt.parseInt(isize, parts.next().?, 0); accumulator += diff; }, 'j' => { var parts = std.mem.tokenize(u8, line, " "); _ = parts.next(); const diff = try std.fmt.parseInt(isize, parts.next().?, 0); idx += diff - 1; }, else => unreachable, } } // If all instructions called, then the program is finished. if (idx == lines.len) return @intCast(usize, accumulator); } unreachable; } test "day08a" { try std.testing.expectEqual(@as(usize, 1727), try first(std.testing.allocator)); } test "day08b" { try std.testing.expectEqual(@as(usize, 552), try second(std.testing.allocator)); }