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