const std = @import("std");
const builtin = @import("builtin");
const Input = struct {
draw_numbers: []u32 = undefined,
boards: [][25]u32 = undefined,
};
fn readInputFile(allocator: std.mem.Allocator, filename: []const u8) anyerror!Input {
var result = Input{};
var draw_numbers = std.ArrayList(u32).init(allocator);
var boards = std.ArrayList([25]u32).init(allocator);
const file = try std.fs.cwd().openFile(filename, .{ .read = true });
defer file.close();
const reader = file.reader();
var line: []u8 = undefined;
if (builtin.os.tag == .windows) {
// NOTE: Read another byte on windows due to two-byte eol.
line = reader.readUntilDelimiterAlloc(allocator, '\r', 512) catch unreachable;
_ = try reader.readByte();
} else {
line = reader.readUntilDelimiterAlloc(allocator, '\n', 512) catch unreachable;
}
{
var token = std.mem.split(u8, line, ",");
while (token.next()) |slice| {
const number = try std.fmt.parseUnsigned(u32, slice, 10);
try draw_numbers.append(number);
}
allocator.free(line);
}
result.draw_numbers = draw_numbers.items;
while (true) {
// NOTE: Skip line.
_ = reader.readByte() catch break;
if (builtin.os.tag == .windows) {
// NOTE: Read another byte on windows due to two-byte eol.
_ = reader.readByte() catch break;
}
var i: u32 = 0;
var board = std.mem.zeroes([25]u32);
var board_index: u32 = 0;
while (i < 5) : (i += 1) {
if (builtin.os.tag == .windows) {
// NOTE: Read another byte on windows due to two-byte eol.
line = reader.readUntilDelimiterAlloc(allocator, '\r', 512) catch unreachable;
_ = try reader.readByte();
} else {
line = reader.readUntilDelimiterAlloc(allocator, '\n', 512) catch unreachable;
}
var token = std.mem.tokenize(u8, line, " ");
while (token.next()) |slice| {
board[board_index] = try std.fmt.parseUnsigned(u32, slice, 10);
board_index += 1;
}
allocator.free(line);
}
try boards.append(board);
}
result.boards = boards.items;
return result;
}
const Marker: u32 = 999;
inline fn boardTickNumber(number: u32, board: []u32) void {
for (board) |value, i| {
if (value == number) {
board[i] = Marker;
}
}
}
inline fn checkBoard(board_w: u32, board_h: u32, board: []u32) bool {
var x: u32 = 0;
var y: u32 = 0;
while (y < board_h) : (y += 1) {
var foundNumber = false;
x = 0;
while (x < board_w) : (x += 1) {
if (board[(y * board_w) + x] != Marker) {
foundNumber = true;
}
}
if (!foundNumber) {
return true;
}
}
x = 0;
while (x < board_w) : (x += 1) {
var foundNumber = false;
y = 0;
while (y < board_h) : (y += 1) {
if (board[(y * board_w) + x] != Marker) {
foundNumber = true;
}
}
if (!foundNumber) {
return true;
}
}
return false;
}
inline fn getBoardValue(board: []u32) u32 {
var result: u32 = 0;
for (board) |value| {
if (value != Marker) {
result += value;
}
}
return result;
}
pub fn task1(draw_numbers: []const u32, board_w: u32, board_h: u32, boards: [][25]u32) u32 {
var result: u32 = 0;
done: for (draw_numbers) |number| {
std.log.debug("drew {}", .{number});
for (boards) |*b, i| {
var board = b[0..];
boardTickNumber(number, board);
if (checkBoard(board_w, board_h, board)) {
result = getBoardValue(board) * number;
break :done;
}
std.log.debug("board {}:{any}", .{ i, board.* });
}
}
return result;
}
pub fn task2(draw_numbers: []const u32, board_w: u32, board_h: u32, boards: [][25]u32) u32 {
var result: u32 = 0;
for (draw_numbers) |number| {
std.log.debug("drew {}", .{number});
for (boards) |*b, i| {
var board = b[0..];
if (checkBoard(board_w, board_h, board)) {
continue;
}
boardTickNumber(number, board);
if (checkBoard(board_w, board_h, board)) {
result = getBoardValue(board) * number;
std.log.debug("result {}:{}->{}", .{ i, number, result });
}
std.log.debug("board {}:{any}", .{ i, board.* });
}
}
return result;
}
pub fn main() anyerror!void {
var buffer: [65536]u8 = undefined;
var fixed_buffer = std.heap.FixedBufferAllocator.init(&buffer);
const allocator = fixed_buffer.allocator();
const input = try readInputFile(allocator, "input.txt");
var draw_numbers = try allocator.dupe(u32, input.draw_numbers);
var boards = try allocator.dupe([25]u32, input.boards);
const task_1_result = task1(draw_numbers, 5, 5, boards);
std.log.info("Task 1 result: {}", .{task_1_result});
allocator.free(draw_numbers);
allocator.free(boards);
draw_numbers = try allocator.dupe(u32, input.draw_numbers);
boards = try allocator.dupe([25]u32, input.boards);
const task_2_result = task2(input.draw_numbers, 5, 5, input.boards);
std.log.info("Task 2 result: {}", .{task_2_result});
allocator.free(draw_numbers);
allocator.free(boards);
}