1const std = @import("std");
2const Allocator = std.mem.Allocator;
3const StringTokenIterator = std.mem.TokenIterator(u8, .scalar);
4
5const MAX_WIDTH = 137;
6
7const Grid = struct {
8 data: [MAX_WIDTH][]u8,
9 maxX: usize,
10 maxY: usize,
11
12 pub fn isRoll(self: *const Grid, x: isize, y: isize) bool {
13 if (x < 0 or x >= self.maxX) {
14 return false;
15 }
16 if (y < 0 or y >= self.maxY) {
17 return false;
18 }
19
20 return self.data[@intCast(x)][@intCast(y)] == '@';
21 }
22};
23
24fn newGrid(lines: *StringTokenIterator) !Grid {
25 const line_len = lines.peek().?.len;
26 var grid: [MAX_WIDTH][]u8 = undefined;
27
28 var i: usize = 0;
29 while (lines.next()) |line| : (i += 1) {
30 grid[i] = @constCast(line);
31 }
32
33 return .{ .data = grid, .maxX = line_len, .maxY = i };
34}
35
36pub fn run(_: Allocator, input: []u8) !struct { u64, u64 } {
37 var lines = std.mem.tokenizeScalar(u8, input, '\n');
38
39 const grid = try newGrid(&lines);
40
41 var answer1: u64 = 0;
42 var answer2: u64 = 0;
43
44 // Need to figure out how to not make this ugly, but too tired
45 for (0..grid.maxX) |i| {
46 for (0..grid.maxY) |j| {
47 if (grid.isRoll(@as(isize, @intCast(i)), @as(isize, @intCast(j)))) {
48 var adjacent_count: usize = 0;
49
50 adjacent_count += @intFromBool(grid.isRoll(@as(isize, @intCast(i)) - 1, @as(isize, @intCast(j)) - 1));
51 adjacent_count += @intFromBool(grid.isRoll(@as(isize, @intCast(i)) - 1, @as(isize, @intCast(j))));
52 adjacent_count += @intFromBool(grid.isRoll(@as(isize, @intCast(i)), @as(isize, @intCast(j)) - 1));
53
54 adjacent_count += @intFromBool(grid.isRoll(@as(isize, @intCast(i)) + 1, @as(isize, @intCast(j)) - 1));
55 adjacent_count += @intFromBool(grid.isRoll(@as(isize, @intCast(i)) - 1, @as(isize, @intCast(j)) + 1));
56
57 adjacent_count += @intFromBool(grid.isRoll(@as(isize, @intCast(i)) + 1, @as(isize, @intCast(j)) + 1));
58 adjacent_count += @intFromBool(grid.isRoll(@as(isize, @intCast(i)) + 1, @as(isize, @intCast(j))));
59 adjacent_count += @intFromBool(grid.isRoll(@as(isize, @intCast(i)), @as(isize, @intCast(j)) + 1));
60
61 if (adjacent_count < 4) {
62 answer1 += 1;
63 }
64 }
65 }
66 }
67
68 var removed = true;
69
70 while (removed) {
71 removed = false;
72
73 for (0..grid.maxX) |i| {
74 for (0..grid.maxY) |j| {
75 if (grid.isRoll(@as(isize, @intCast(i)), @as(isize, @intCast(j)))) {
76 var adjacent_count: usize = 0;
77
78 adjacent_count += @intFromBool(grid.isRoll(@as(isize, @intCast(i)) - 1, @as(isize, @intCast(j)) - 1));
79 adjacent_count += @intFromBool(grid.isRoll(@as(isize, @intCast(i)) - 1, @as(isize, @intCast(j))));
80 adjacent_count += @intFromBool(grid.isRoll(@as(isize, @intCast(i)), @as(isize, @intCast(j)) - 1));
81
82 adjacent_count += @intFromBool(grid.isRoll(@as(isize, @intCast(i)) + 1, @as(isize, @intCast(j)) - 1));
83 adjacent_count += @intFromBool(grid.isRoll(@as(isize, @intCast(i)) - 1, @as(isize, @intCast(j)) + 1));
84
85 adjacent_count += @intFromBool(grid.isRoll(@as(isize, @intCast(i)) + 1, @as(isize, @intCast(j)) + 1));
86 adjacent_count += @intFromBool(grid.isRoll(@as(isize, @intCast(i)) + 1, @as(isize, @intCast(j))));
87 adjacent_count += @intFromBool(grid.isRoll(@as(isize, @intCast(i)), @as(isize, @intCast(j)) + 1));
88
89 if (adjacent_count < 4) {
90 answer2 += 1;
91 grid.data[i][j] = '.';
92 removed = true;
93 }
94 }
95 }
96 }
97 }
98
99 return .{ answer1, answer2 };
100}