AoC 2024: Day 13

Main thread for Day 13 of the 2024 advent of code. Feel free to discuss the challenge and ask questions. If particular discussions become large, we can break them off into new threads.

Some Rules:

  1. Please try to keep spoilers within spoiler tags. This is done by typing [spoiler]text to be blurred[\spoiler]. If you have longer sections you can also use the [details=“Title”] tags.
  2. Have Fun

Day 13 Challenge

Templates:

Resources:

Previous days discussions

Day 1: AoC 2024: Day 1
Day 2: AoC 2024: Day 2
Day 3: AoC 2024: Day 3
Day 4: AoC 2024: Day 4
Day 5: AoC 2024: Day 5
Day 6: AoC 2024: Day 6
Day 7: AoC 2024: Day 7
Day 8: AoC 2024: Day 8
Day 9: AoC 2024: Day 9
Day 10: AoC 2024: Day 10
Day 11: AoC 2024: Day 11
Day 12: AoC 2024: Day 12

And people say maths has no real world application!

https://zigbin.io/ce6dff

3 Likes

I brute forced part 1, fast enough. But with part 2 once i figured out how i had to do it mathematically, I looked at your solution to see if there was a simple way to do the math.

1 Like

50us. I almost started doing some sort of dynamic programming extravaganza. Decided to look at the math first, and saw that it was just 2 equations, 2 unknowns. I trained for this.

https://zigbin.io/92a6ea

Spent too much time on getting lapack to work. It is total overkill to solve the systems of equation with lapack, but I wanted to learn how to use it in zig.

Solution 1
const std = @import("std");

const lapack = @cImport(@cInclude("lapacke.h"));
const laint = lapack.lapack_int;

//const input = @embedFile("./small.inp");
const input = @embedFile("./big.inp");

const n: laint = 2;

const claw_machine_t = struct {
    butA: [n]i32,
    butB: [n]i32,
    npressA: i32,
    npressB: i32,
    prizes: [n]i32,
    winnable: bool,

    const Self = @This();

    pub fn read(lines: *std.mem.TokenIterator(u8, .scalar)) ?Self {
        var claw_machine = Self{
            .butA = undefined,
            .butB = undefined,
            .npressA = 0,
            .npressB = 0,
            .prizes = undefined,
            .winnable = undefined,
        };
        for (0..2) |_| {
            if (lines.next()) |line| {
                var fields = std.mem.tokenizeScalar(u8, line, ' ');
                _ = fields.next() orelse return null;
                const butname = fields.next() orelse return null;
                const Xstr = fields.next() orelse return null;
                const Xval = std.fmt.parseInt(i32, Xstr[2 .. Xstr.len - 1], 10) catch |err| {
                    std.debug.print("{?}\n", .{err});
                    return null;
                };
                const Ystr = fields.next() orelse return null;
                const Yval = std.fmt.parseInt(i32, Ystr[2..Ystr.len], 10) catch |err| {
                    std.debug.print("{?}\n", .{err});
                    return null;
                };

                if (butname[0] == 'A') {
                    claw_machine.butA = [n]i32{ Xval, Yval };
                } else if (butname[0] == 'B') {
                    claw_machine.butB = [n]i32{ Xval, Yval };
                }
            } else {
                return null;
            }
        }

        var fields = std.mem.tokenizeScalar(u8, lines.next() orelse "", ' ');
        _ = fields.next() orelse return null;
        const Xstr = fields.next() orelse return null;
        const Xval = std.fmt.parseInt(i32, Xstr[2 .. Xstr.len - 1], 10) catch |err| {
            std.debug.print("{?}\n", .{err});
            return null;
        };
        const Ystr = fields.next() orelse return null;
        const Yval = std.fmt.parseInt(i32, Ystr[2..Ystr.len], 10) catch |err| {
            std.debug.print("{?}\n", .{err});
            return null;
        };
        claw_machine.prizes = [n]i32{ Xval, Yval };
        return claw_machine;
    }

    pub fn solve(self: *Self) void {
        const nrhs: laint = 1;
        const lda: laint = n;
        const ldb: laint = n;

        var buttons: [n * n]f64 = undefined;
        var prizes: [n]f64 = undefined;
        for (0..n) |i| {
            buttons[i] = @floatFromInt(self.butA[i]);
            buttons[i + n] = @floatFromInt(self.butB[i]);
            prizes[i] = @floatFromInt(self.prizes[i]);
        }
        var ipiv = [n]laint{ 0, 0 };
        const info = lapack.LAPACKE_dgesv( //
            lapack.LAPACK_COL_MAJOR, // matrix orientation
            n, // number of equations in matrix
            nrhs, // number of colums in result vector
            &buttons, // the matrix to be diagonalized
            lda, // leading dimension of matrix
            &ipiv, // pivot indices
            &prizes, // result vector
            ldb); // leading dimension of result vector

        if (info == 0) {
            self.npressA = @intFromFloat(prizes[0] + 0.5);
            self.npressB = @intFromFloat(prizes[1] + 0.5);
            self.winnable = self.npressA >= 0 and self.npressA <= 100;
            self.winnable = self.winnable and self.npressB >= 0 and self.npressB <= 100;
            self.winnable = self.winnable and self.butA[0] * self.npressA + self.butB[0] * self.npressB == self.prizes[0];
            self.winnable = self.winnable and self.butA[1] * self.npressA + self.butB[1] * self.npressB == self.prizes[1];
        } else {
            std.debug.print("diagonalization failed\n", .{});
        }
    }

    pub fn cost(self: Self) i32 {
        return 3 * self.npressA + 1 * self.npressB;
    }
};

pub fn main() !void {
    var lines = std.mem.tokenizeScalar(u8, input, '\n');
    var sum: i32 = 0;
    while (true) {
        //std.debug.print("{d}: {s}\n", .{ i, line });
        var claw_machine = claw_machine_t.read(&lines) orelse break;
        claw_machine.solve();
        const cost = claw_machine.cost();
        if (claw_machine.winnable) {
            sum += cost;
        }
        //std.debug.print("{?} -> {d} tokens \n", .{ claw_machine, cost });
    }
    std.debug.print("Number of required tokens: {d}\n", .{sum});
}
Solution 2
const std = @import("std");

const lapack = @cImport(@cInclude("lapacke.h"));
const laint = lapack.lapack_int;

//const input = @embedFile("./small.inp");
const input = @embedFile("./big.inp");

const n: laint = 2;

const claw_machine_t = struct {
    butA: [n]i64,
    butB: [n]i64,
    npressA: i64,
    npressB: i64,
    prizes: [n]i64,
    winnable: bool,

    const Self = @This();

    pub fn read(lines: *std.mem.TokenIterator(u8, .scalar)) ?Self {
        var claw_machine = Self{
            .butA = undefined,
            .butB = undefined,
            .npressA = 0,
            .npressB = 0,
            .prizes = undefined,
            .winnable = undefined,
        };
        for (0..2) |_| {
            if (lines.next()) |line| {
                var fields = std.mem.tokenizeScalar(u8, line, ' ');
                _ = fields.next() orelse return null;
                const butname = fields.next() orelse return null;
                const Xstr = fields.next() orelse return null;
                const Xval = std.fmt.parseInt(i64, Xstr[2 .. Xstr.len - 1], 10) catch |err| {
                    std.debug.print("{?}\n", .{err});
                    return null;
                };
                const Ystr = fields.next() orelse return null;
                const Yval = std.fmt.parseInt(i64, Ystr[2..Ystr.len], 10) catch |err| {
                    std.debug.print("{?}\n", .{err});
                    return null;
                };

                if (butname[0] == 'A') {
                    claw_machine.butA = [n]i64{ Xval, Yval };
                } else if (butname[0] == 'B') {
                    claw_machine.butB = [n]i64{ Xval, Yval };
                }
            } else {
                return null;
            }
        }

        var fields = std.mem.tokenizeScalar(u8, lines.next() orelse "", ' ');
        _ = fields.next() orelse return null;
        const Xstr = fields.next() orelse return null;
        var Xval = std.fmt.parseInt(i64, Xstr[2 .. Xstr.len - 1], 10) catch |err| {
            std.debug.print("{?}\n", .{err});
            return null;
        };
        const Ystr = fields.next() orelse return null;
        var Yval = std.fmt.parseInt(i64, Ystr[2..Ystr.len], 10) catch |err| {
            std.debug.print("{?}\n", .{err});
            return null;
        };
        Xval += 10000000000000;
        Yval += 10000000000000;
        claw_machine.prizes = [n]i64{ Xval, Yval };
        return claw_machine;
    }

    pub fn solve(self: *Self) void {
        const nrhs: laint = 1;
        const lda: laint = n;
        const ldb: laint = n;

        var buttons: [n * n]f64 = undefined;
        var prizes: [n]f64 = undefined;
        for (0..n) |i| {
            buttons[i] = @floatFromInt(self.butA[i]);
            buttons[i + n] = @floatFromInt(self.butB[i]);
            prizes[i] = @floatFromInt(self.prizes[i]);
        }
        var ipiv = [n]laint{ 0, 0 };
        const info = lapack.LAPACKE_dgesv( //
            lapack.LAPACK_COL_MAJOR, // matrix orientation
            n, // number of equations in matrix
            nrhs, // number of colums in result vector
            &buttons, // the matrix to be diagonalized
            lda, // leading dimension of matrix
            &ipiv, // pivot indices
            &prizes, // result vector
            ldb); // leading dimension of result vector

        if (info == 0) {
            self.npressA = @intFromFloat(prizes[0] + 0.5);
            self.npressB = @intFromFloat(prizes[1] + 0.5);
            self.winnable = self.npressA >= 0;
            self.winnable = self.winnable and self.npressB >= 0;
            self.winnable = self.winnable and self.butA[0] * self.npressA + self.butB[0] * self.npressB == self.prizes[0];
            self.winnable = self.winnable and self.butA[1] * self.npressA + self.butB[1] * self.npressB == self.prizes[1];
        } else {
            std.debug.print("diagonalization failed\n", .{});
        }
    }

    pub fn cost(self: Self) i64 {
        return 3 * self.npressA + 1 * self.npressB;
    }
};

pub fn main() !void {
    var lines = std.mem.tokenizeScalar(u8, input, '\n');
    var sum: i64 = 0;
    while (true) {
        //std.debug.print("{d}: {s}\n", .{ i, line });
        var claw_machine = claw_machine_t.read(&lines) orelse break;
        claw_machine.solve();
        const cost = claw_machine.cost();
        if (claw_machine.winnable) {
            sum += cost;
        }
        //std.debug.print("{?} -> {d} tokens \n", .{ claw_machine, cost });
    }
    std.debug.print("Number of required tokens: {d}\n", .{sum});
}