const std = @import("std"); const path = "data/day04/input.txt"; const retSize = u16; const grid_item_size = u7; const Grid = struct { boardMap: std.AutoHashMap(grid_item_size, Point), row: [5]u3 = .{ 0, 0, 0, 0, 0 }, col: [5]u3 = .{ 0, 0, 0, 0, 0 }, pub fn getValue(self: @This()) retSize { var sum: retSize = 0; var it = self.boardMap.iterator(); while (it.next()) |item| { if (!item.value_ptr.marked) { sum += item.key_ptr.*; } } return sum; } }; const Point = struct { row: u3, // < 5 col: u3, // < 5 marked: bool = false, }; const Bingo = struct { numbers: std.ArrayList(grid_item_size), boards: std.ArrayList(Grid), }; fn first() anyerror!retSize { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; const allocator = gpa.allocator(); var bg = try parseInput(allocator); defer { // cleanup code for (bg.boards.items) |_, idx| { bg.boards.items[idx].boardMap.deinit(); } bg.boards.deinit(); bg.numbers.deinit(); } for (bg.numbers.items) |num| { for (bg.boards.items) |_, idx| { // std.debug.print("{any} {any}\n", .{ num, idx }); var curr = &bg.boards.items[idx]; const coord = curr.boardMap.get(num) orelse continue; curr.row[coord.row] += 1; curr.col[coord.col] += 1; try curr.boardMap.put(num, .{ .row = coord.row, .col = coord.col, .marked = true }); // std.debug.print("{any} {any} {any}\n", .{num, curr.row, curr.col}); if ((curr.row[coord.row] == 5) or (curr.col[coord.col] == 5)) { return num * curr.getValue(); } } } unreachable; } fn second() anyerror!retSize { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; const allocator = gpa.allocator(); var bg = try parseInput(allocator); defer { // cleanup code for (bg.boards.items) |_, idx| { bg.boards.items[idx].boardMap.deinit(); } bg.boards.deinit(); bg.numbers.deinit(); } var to_remove = std.ArrayList(usize).init(allocator); defer to_remove.deinit(); for (bg.numbers.items) |num| { for (bg.boards.items) |_, idx| { var curr = &bg.boards.items[idx]; const coord = curr.boardMap.get(num) orelse continue; curr.row[coord.row] += 1; curr.col[coord.col] += 1; try curr.boardMap.put(num, .{ .row = coord.row, .col = coord.col, .marked = true }); if ((curr.row[coord.row] == 5) or (curr.col[coord.col] == 5)) { if (bg.boards.items.len == 1) { return num * curr.getValue(); } else { try to_remove.append(idx); } } } std.sort.sort(usize, to_remove.items, {}, comptime std.sort.desc(usize)); for (to_remove.items) |idx| { bg.boards.items[idx].boardMap.deinit(); _ = bg.boards.swapRemove(idx); } try to_remove.resize(0); } unreachable; } fn parseInput(allocator: std.mem.Allocator) anyerror!Bingo { const input = @embedFile(path); var lines = std.mem.split(u8, input, "\n"); var ret = Bingo{ .numbers = std.ArrayList(grid_item_size).init(allocator), .boards = std.ArrayList(Grid).init(allocator), }; var gridRow: u3 = 0; var gridCol: u3 = 0; var g = Grid{ .boardMap = std.AutoHashMap(grid_item_size, Point).init(allocator) }; var idx: usize = 0; while (lines.next()) |line| : (idx += 1) { // read in numbers if (idx == 0) { var nums = std.mem.tokenize(u8, line, ","); while (nums.next()) |num| { try ret.numbers.append(try std.fmt.parseUnsigned(grid_item_size, num, 10)); } continue; } // std.debug.print("line: {any}\n", .{line}); // read in Grids var items = std.mem.tokenize(u8, line, " "); while (items.next()) |item| { const value = try std.fmt.parseUnsigned(grid_item_size, item, 10); // std.debug.print("{any} -> {any} {any}\n", .{value, gridRow, gridCol}); try g.boardMap.put(value, .{ .row = gridRow, .col = gridCol }); gridCol += 1; } gridCol = 0; if (line.len != 0) gridRow += 1; // append grid if (gridRow == 5) { try ret.boards.append(g); gridRow = 0; gridCol = 0; // create new grid g = Grid{ .boardMap = std.AutoHashMap(grid_item_size, Point).init(allocator) }; } } return ret; } pub fn main() anyerror!void { var timer = try std.time.Timer.start(); _ = try first(); const f = timer.lap() / 1000; _ = try second(); const s = timer.lap() / 1000; std.debug.print("Day 4 \t\tfirst: {d}us \t\tsecond: {d}us\n", .{ f, s }); } test "first" { try std.testing.expectEqual(@as(retSize, 11536), try first(std.testing.allocator)); } test "second" { try std.testing.expectEqual(@as(retSize, 1284), try second()); }