1
0
Advent-of-Code/day13/part2.zig
2024-12-15 10:56:23 +01:00

136 lines
4.6 KiB
Zig

const std = @import("std");
const print = std.debug.print;
const input = @embedFile("input");
const Usize = std.atomic.Value(usize);
const vec2 = @Vector(2, usize);
// I am not proud of this solution, it take like 6h to run on 16 thread :/
// I am sure I can change the bruteForce function to use modulo to reduce the number of clickA
// But it was late, I was tired so I juste started this version and the next morning it was complete soooo
// I will no go futher, I am already 2 days behind :/
const State = enum {
ReadButtonA,
ReadButtonB,
ReadPrize,
ReadJump,
};
pub fn main() !void {
var iter = std.mem.splitAny(u8, input, "\n");
var state = State.ReadButtonA;
var buttonA: vec2 = undefined;
var buttonB: vec2 = undefined;
var prize: vec2 = undefined;
var total = Usize.init(0);
var finished = Usize.init(0);
var to_wait: usize = 0;
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.allocator();
var thread_arena = std.heap.ThreadSafeAllocator{
.child_allocator = allocator,
};
var thread_pool: std.Thread.Pool = undefined;
thread_pool.init(std.Thread.Pool.Options{
.allocator = thread_arena.allocator(),
.n_jobs = 16,
}) catch @panic("=(");
while (iter.next()) |line| switch (state) {
.ReadButtonA => {
var iterLine = std.mem.splitAny(u8, line, " ");
_ = iterLine.next(); // Skip Button
_ = iterLine.next(); // Skip A
const buf = iterLine.next().?;
buttonA[0] = try std.fmt.parseInt(usize, buf[2 .. buf.len - 1], 10);
buttonA[1] = try std.fmt.parseInt(usize, iterLine.next().?[2..], 10);
state = .ReadButtonB;
},
.ReadButtonB => {
var iterLine = std.mem.splitAny(u8, line, " ");
_ = iterLine.next(); // Skip Button
_ = iterLine.next(); // Skip B
const buf = iterLine.next().?;
buttonB[0] = try std.fmt.parseInt(usize, buf[2 .. buf.len - 1], 10);
buttonB[1] = try std.fmt.parseInt(usize, iterLine.next().?[2..], 10);
state = .ReadPrize;
},
.ReadPrize => {
var iterLine = std.mem.splitAny(u8, line, " ");
_ = iterLine.next(); // Skip Price
const buf = iterLine.next().?;
prize[0] = try std.fmt.parseInt(usize, buf[2 .. buf.len - 1], 10) + 10000000000000;
prize[1] = try std.fmt.parseInt(usize, iterLine.next().?[2..], 10) + 10000000000000;
state = .ReadJump;
},
.ReadJump => {
state = .ReadButtonA;
try thread_pool.spawn(bruteforce, .{ buttonA, buttonB, prize, &total, &finished, to_wait });
to_wait += 1;
},
};
while (to_wait > finished.load(.monotonic)) {
std.time.sleep(1000000);
printProgressOverall(finished.load(.monotonic), to_wait);
}
print("Total: {d}\n", .{total.load(.monotonic)});
}
const Result = struct {
clickA: usize = 0,
clickB: usize = 0,
cost: usize = 999999,
};
fn bruteforce(buttonA: vec2, buttonB: vec2, prize: vec2, total: *Usize, finished: *Usize, id: usize) void {
var min_cost: usize = std.math.maxInt(usize);
const max_clickA = @min(@divFloor(prize[0], buttonA[0]), @divFloor(prize[1], buttonA[1])) + 10;
var clickA: usize = 0;
while (clickA <= max_clickA) : (clickA += 1) {
if (clickA % 1000000000 == 0) printProgress(clickA, max_clickA, id);
const remaining = prize - buttonA * @as(vec2, @splat(clickA));
if (@reduce(.Or, remaining < @as(vec2, @splat(0)))) continue;
const clickB = @divFloor(remaining, buttonB);
if (clickB[0] != clickB[1]) continue;
if (@reduce(.Or, remaining % buttonB != @as(vec2, @splat(0)))) continue;
const cost = clickA * 3 + clickB[0];
if (cost < min_cost) min_cost = cost;
}
if (min_cost != std.math.maxInt(usize)) {
_ = total.fetchAdd(min_cost, .monotonic);
}
_ = finished.fetchAdd(1, .monotonic);
}
fn printProgress(value: usize, max: usize, id: usize) void {
print("Thread {d}: {d}% | {d}/{d}\n", .{ id, @divFloor(value * 100, max), value, max });
}
fn printProgressOverall(finished: usize, total: usize) void {
std.debug.print("Overall: {d}/{d} ({d}%) \r", .{ finished, total, @divFloor(finished * 100, total) });
}
fn gcd(a: u64, b: u64) u64 {
if (b == 0) {
return a;
} else {
return gcd(b, a % b);
}
}
fn isPossible(a: u64, b: u64, c: u64) bool {
return c % gcd(a, b) == 0;
}