const std = @import("std"); const PATH = "input/day06.txt"; const Str = []const u8; const CoordType = u10; const Coords = [2]CoordType; const Actions = enum { on, off, toggle, }; pub fn first(allocator: std.mem.Allocator) !usize { _ = allocator; return switchLights(@embedFile(PATH)); } pub fn second(allocator: std.mem.Allocator) !usize { _ = allocator; return changeLights(@embedFile(PATH)); } fn switchLights(commands: Str) !usize { const Lights = [1000]std.StaticBitSet(1000); var lights: Lights = undefined; for (lights) |*row| { row.* = (std.StaticBitSet(1000)).initEmpty(); } var lines = std.mem.tokenize(u8, commands, "\n"); while (lines.next()) |line| { var words = std.mem.tokenize(u8, line, " "); // parse action var action: Actions = undefined; // turn or toggle if (words.next().?[1] == 'u') { // on or off if (words.next().?[1] == 'n') action = .on else action = .off; } else { action = .toggle; } // parse coordinates const top = try parseCoords(words.next().?); _ = words.next(); // drop through const bottom = try parseCoords(words.next().?); const range = std.bit_set.Range{ .start = top[0], .end = bottom[0] + 1, }; // do the action switch (action) { .on => { var row: usize = top[1]; while (row <= bottom[1]) : (row += 1) { lights[row].setRangeValue(range, true); } }, .off => { var row: usize = top[1]; while (row <= bottom[1]) : (row += 1) { lights[row].setRangeValue(range, false); } }, .toggle => { var ts = std.StaticBitSet(1000).initEmpty(); ts.setRangeValue(range, true); var row: usize = top[1]; while (row <= bottom[1]) : (row += 1) { lights[row].toggleSet(ts); } }, } } var sum: usize = 0; for (lights) |row| { sum += row.count(); } return sum; } fn changeLights(commands: Str) !usize { const LightType = u8; var lights = [_][1000]LightType{[_]LightType{0} ** 1000} ** 1000; var lines = std.mem.tokenize(u8, commands, "\n"); while (lines.next()) |line| { var words = std.mem.tokenize(u8, line, " "); // parse action var action: Actions = undefined; // turn or toggle if (words.next().?[1] == 'u') { // on or off if (words.next().?[1] == 'n') action = .on else action = .off; } else { action = .toggle; } // parse coordinates const top = try parseCoords(words.next().?); _ = words.next(); // drop through const bottom = try parseCoords(words.next().?); // do the action switch (action) { .on => { var x: usize = top[0]; while (x <= bottom[0]) : (x += 1) { var y: usize = top[1]; while (y <= bottom[1]) : (y += 1) { lights[x][y] += 1; } } }, .toggle => { var x: usize = top[0]; while (x <= bottom[0]) : (x += 1) { var y: usize = top[1]; while (y <= bottom[1]) : (y += 1) { lights[x][y] += 2; } } }, .off => { var x: usize = top[0]; while (x <= bottom[0]) : (x += 1) { var y: usize = top[1]; while (y <= bottom[1]) : (y += 1) { lights[x][y] -|= 1; } } }, } } var sum: usize = 0; for (lights) |row| { for (row) |item| { sum += item; } } return sum; } fn parseCoords(a: Str) !Coords { var crd: Coords = undefined; var parts = std.mem.tokenize(u8, a, ","); crd[0] = try std.fmt.parseUnsigned(CoordType, parts.next().?, 10); crd[1] = try std.fmt.parseUnsigned(CoordType, parts.next().?, 10); return crd; } test "switchLights" { try std.testing.expectEqual(@as(usize, 1000 * 1000), try switchLights("turn on 0,0 through 999,999")); try std.testing.expectEqual(@as(usize, 1000), try switchLights("toggle 0,0 through 999,0")); } test "changeLights" { try std.testing.expectEqual(@as(usize, 1), try changeLights("turn on 0,0 through 0,0")); try std.testing.expectEqual(@as(usize, 2 * 1000 * 1000), try changeLights("toggle 0,0 through 999,999")); } test "day06a" { try std.testing.expectEqual(@as(usize, 569999), try first(std.testing.allocator)); } test "day06b" { try std.testing.expectEqual(@as(usize, 17836115), try second(std.testing.allocator)); }