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));
}