Very straightforward with recursion. Part 2 saw some noticeable slowdown, but I haven’t tried to optimize anything.
Part 1
const std = @import("std");
const input = @embedFile("input.txt");
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
var inputs = std.ArrayList(u64).init(arena.allocator());
var result: u64 = 0;
var line_iter = std.mem.tokenizeScalar(u8, input, '\n');
while (line_iter.next()) |line| {
inputs.clearRetainingCapacity();
const colon = std.mem.indexOfScalar(u8, line, ':').?;
const target = try std.fmt.parseInt(u64, line[0..colon], 10);
var input_iter = std.mem.tokenizeScalar(u8, line[colon + 1 ..], ' ');
while (input_iter.next()) |n| {
try inputs.append(try std.fmt.parseInt(u64, n, 10));
}
if (canMakeTarget(target, inputs.items[0], inputs.items[1..])) {
result += target;
}
}
std.log.info("{d}", .{result});
}
fn canMakeTarget(target: u64, acc: u64, inputs: []const u64) bool {
if (inputs.len == 0) {
return acc == target;
}
return canMakeTarget(target, acc + inputs[0], inputs[1..]) or
canMakeTarget(target, acc * inputs[0], inputs[1..]);
}
Part 2
const std = @import("std");
const input = @embedFile("input.txt");
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
var inputs = std.ArrayList(u64).init(arena.allocator());
var result: u64 = 0;
var line_iter = std.mem.tokenizeScalar(u8, input, '\n');
while (line_iter.next()) |line| {
inputs.clearRetainingCapacity();
const colon = std.mem.indexOfScalar(u8, line, ':').?;
const target = try std.fmt.parseInt(u64, line[0..colon], 10);
var input_iter = std.mem.tokenizeScalar(u8, line[colon + 1 ..], ' ');
while (input_iter.next()) |n| {
try inputs.append(try std.fmt.parseInt(u64, n, 10));
}
if (canMakeTarget(target, inputs.items[0], inputs.items[1..], arena.allocator())) {
result += target;
}
}
std.log.info("{d}", .{result});
}
fn canMakeTarget(target: u64, acc: u64, inputs: []const u64, allocator: std.mem.Allocator) bool {
if (inputs.len == 0) {
return acc == target;
}
if (canMakeTarget(target, acc + inputs[0], inputs[1..], allocator) or
canMakeTarget(target, acc * inputs[0], inputs[1..], allocator))
{
return true;
}
const concat = std.fmt.allocPrint(allocator, "{d}{d}", .{ acc, inputs[0] }) catch unreachable;
const concat_num = std.fmt.parseInt(u64, concat, 10) catch unreachable;
return canMakeTarget(target, concat_num, inputs[1..], allocator);
}