Advent of Code 2024: Day 4

$ zig version
0.13.0

This problem requires looking for two kinds of pattern on a grid of characters. There are no new Zig features here to discuss.

Final Program

const std = @import("std");

pub fn main() !void {
    // get allocator
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    const allocator = arena.allocator();
    defer _ = arena.deinit();

    // get command-line args
    const args = try std.process.argsAlloc(allocator);
    defer std.process.argsFree(allocator, args);

    // see if filename is on command-line
    if (args.len == 2) {
        const filename = args[1];
        solveDay(allocator, filename) catch |err| {
            switch (err) {
                error.FileNotFound => {
                    std.debug.print("Error: File {s} was not found.\n",
                        .{filename});
                    std.process.exit(1);
                },
                else => {
                    std.debug.print("Error: in processing file.\n", .{}
                    );
                    std.process.exit(1);
                },
            }
        };
    } else {
        std.debug.print("Provide a filename of input data.\n", .{});
    }
}

fn solveDay (allocator: std.mem.Allocator, filename: []const u8) !void {
    var grid = std.ArrayList([]const u8).init(allocator);
    defer grid.deinit();
    try readData (allocator, filename, &grid);

    std.debug.print("Part 1: {d}\n", .{part1(&grid)});
    std.debug.print("Part 2: {d}\n", .{part2(&grid)});
}

fn part1 (grid: *std.ArrayList([]const u8)) usize {
    var total: usize = 0;

    for (grid.items, 0..) |_, row| {
        for (grid.items[row], 0..) |_, col| {
            // look for xmas/samx in four directions
            if (foundXmas(grid, row, col, 1, 0)) {
                total += 1;
            }
            if (foundXmas(grid, row, col, 0, 1)) {
                total += 1;
            }
            if (foundXmas(grid, row, col, 1, 1)) {
                total += 1;
            }
            // can't do this last one without a negative direction, so call directly
            if (isXmas(safe_get(grid, row, col+3), safe_get(grid, row+1, col+2), 
                    safe_get(grid, row+2, col+1), safe_get(grid, row+3, col))) {
                total += 1;
            }
        }
    }

    return total;
}

fn part2 (grid: *std.ArrayList([]const u8)) usize {
    var total: usize = 0;

    for (grid.items, 0..) |_, row| {
        for (grid.items[row], 0..) |_, col| {
            // look for mas centred on (+1, +1)
            if (('A' == safe_get(grid, row + 1, col + 1)) and
                isMAndS(safe_get(grid, row, col), safe_get(grid, row + 2, col + 2)) and
                isMAndS(safe_get(grid, row + 2, col), safe_get(grid, row, col + 2))) {
                total += 1;
            }
        }
    }

    return total;
}

fn isXmas (charA: u8, charB: u8, charC: u8, charD: u8) bool {
    return ('X' == charA and 'M' == charB and 'A' == charC and 'S' == charD) or
        ('X' == charD and 'M' == charC and 'A' == charB and 'S' == charA);
}

fn foundXmas(grid: *std.ArrayList([] const u8), row: usize, col: usize, rowDelta: usize, colDelta: usize) bool {
    return isXmas(safe_get(grid, row, col), 
        safe_get(grid, row + rowDelta, col + colDelta),
        safe_get(grid, row + rowDelta + rowDelta, col + colDelta + colDelta), 
        safe_get(grid, row + rowDelta + rowDelta + rowDelta, col + colDelta + colDelta + colDelta)); 
}

fn isMAndS (charA: u8, charB: u8) bool {
    return ('M' == charA and 'S' == charB) or ('S' == charA and 'M' == charB);
}

// When (row,col) within grid bounds, return char at that point, else return '.'.
fn safe_get (grid: *std.ArrayList([]const u8), row: usize, col: usize) u8 {
    if (0 <= row and row < grid.items.len and 0 <= col and col < grid.items[row].len) {
        return grid.items[row][col];
    } else {
        return '.';
    }
}

fn readData (allocator: std.mem.Allocator, filename: []const u8, grid: *std.ArrayList([]const u8)) !void 
{ 
    const file = try std.fs.cwd().openFile(filename, .{});
    defer file.close();
    const stat = try file.stat();
    const data = try file.readToEndAlloc(allocator, stat.size);

    var lines = std.mem.tokenizeScalar(u8, data, '\n');
    while (lines.next()) |line| {
        try grid.append(line);
    }
}

Page from Peter's Scrapbook, output from a VimWiki on 2024-12-10.