mirror of
https://github.com/MorizzG/aoc-2024.git
synced 2025-12-06 04:22:43 +00:00
282 lines
6.2 KiB
Zig
282 lines
6.2 KiB
Zig
const std = @import("std");
|
|
|
|
const utils = @import("utils.zig");
|
|
|
|
const isDigit = std.ascii.isDigit;
|
|
|
|
pub fn main() !void {
|
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
const alloc = gpa.allocator();
|
|
|
|
const filename = "inputs/day3.txt";
|
|
|
|
{
|
|
const file_reader = try utils.FileReader.init(alloc, filename);
|
|
defer file_reader.deinit();
|
|
|
|
const result = try part1(alloc, file_reader.reader());
|
|
|
|
try std.io.getStdOut().writer().print("Day 3, part 1: {}\n", .{result});
|
|
}
|
|
|
|
{
|
|
const file_reader = try utils.FileReader.init(alloc, filename);
|
|
defer file_reader.deinit();
|
|
|
|
const result = try part2(alloc, file_reader.reader());
|
|
|
|
try std.io.getStdOut().writer().print("Day 3, part 2: {}\n", .{result});
|
|
}
|
|
}
|
|
|
|
fn part1(alloc: std.mem.Allocator, reader: anytype) !u64 {
|
|
const input = try reader.readAllAlloc(alloc, std.math.maxInt(u64));
|
|
defer alloc.free(input);
|
|
|
|
const mul_prefix = "mul(";
|
|
|
|
std.debug.assert(mul_prefix.len == 4);
|
|
|
|
var total: u64 = 0;
|
|
|
|
var idx: usize = 0;
|
|
var i: usize = 0;
|
|
|
|
outer: while (idx < input.len) : (idx += @max(i, 1)) {
|
|
const rest = input[idx..];
|
|
|
|
i = 0;
|
|
|
|
while (i < mul_prefix.len) : (i += 1) {
|
|
if (rest[i] != mul_prefix[i]) {
|
|
continue :outer;
|
|
}
|
|
}
|
|
|
|
// parse first number
|
|
|
|
var start: usize = i;
|
|
|
|
// next char must be a digit
|
|
if (isDigit(rest[i])) {
|
|
i += 1;
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
// consume up to 2 more digits
|
|
for (0..2) |_| {
|
|
if (isDigit(rest[i])) {
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// already validated we're parsing a valid number
|
|
const left = std.fmt.parseUnsigned(u32, rest[start..i], 10) catch unreachable;
|
|
|
|
if (rest[i] != ',') {
|
|
continue;
|
|
}
|
|
|
|
// consume ','
|
|
i += 1;
|
|
|
|
// parse second number
|
|
|
|
start = i;
|
|
|
|
// next char must be a digit
|
|
if (isDigit(rest[i])) {
|
|
i += 1;
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
// consume up to 2 more digits
|
|
for (0..2) |_| {
|
|
if (isDigit(rest[i])) {
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// already validated we're parsing a valid number
|
|
const right = std.fmt.parseUnsigned(u32, rest[start..i], 10) catch unreachable;
|
|
|
|
if (rest[i] != ')') {
|
|
continue;
|
|
}
|
|
|
|
// consume ')'
|
|
i += 1;
|
|
|
|
total += left * right;
|
|
}
|
|
|
|
return total;
|
|
}
|
|
|
|
fn part2(alloc: std.mem.Allocator, reader: anytype) !u64 {
|
|
const input = try reader.readAllAlloc(alloc, std.math.maxInt(u64));
|
|
defer alloc.free(input);
|
|
|
|
// std.debug.print("{s}\n", .{input});
|
|
|
|
// var tok_it = std.mem.tokenizeSequence(u8, input, "mul(");
|
|
|
|
const mul_prefix = "mul(";
|
|
const do = "do()";
|
|
const dont = "don't()";
|
|
|
|
var total: u64 = 0;
|
|
|
|
var idx: usize = 0;
|
|
var i: usize = 0;
|
|
|
|
var enabled = true;
|
|
|
|
outer: while (idx < input.len) : (idx += @max(i, 1)) {
|
|
const rest = input[idx..];
|
|
|
|
i = 0;
|
|
|
|
if (!enabled) {
|
|
while (i < do.len) : (i += 1) {
|
|
if (rest[i] != do[i]) {
|
|
continue :outer;
|
|
}
|
|
}
|
|
|
|
enabled = true;
|
|
continue;
|
|
}
|
|
|
|
if (rest[i] == 'd') {
|
|
i += 1;
|
|
|
|
while (i < do.len) : (i += 1) {
|
|
if (rest[i] != dont[i]) {
|
|
continue :outer;
|
|
}
|
|
}
|
|
|
|
enabled = false;
|
|
continue;
|
|
}
|
|
|
|
while (i < mul_prefix.len) : (i += 1) {
|
|
if (rest[i] != mul_prefix[i]) {
|
|
continue :outer;
|
|
}
|
|
}
|
|
|
|
// parse first number
|
|
|
|
var start: usize = i;
|
|
|
|
// next char must be a digit
|
|
if (isDigit(rest[i])) {
|
|
i += 1;
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
// consume up to 2 more digits
|
|
for (0..2) |_| {
|
|
if (isDigit(rest[i])) {
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// already validated we're parsing a valid number
|
|
const left = std.fmt.parseUnsigned(u32, rest[start..i], 10) catch unreachable;
|
|
|
|
if (rest[i] != ',') {
|
|
continue;
|
|
}
|
|
|
|
// consume ','
|
|
i += 1;
|
|
|
|
// parse second number
|
|
|
|
start = i;
|
|
|
|
// next char must be a digit
|
|
if (isDigit(rest[i])) {
|
|
i += 1;
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
// consume up to 2 more digits
|
|
for (0..2) |_| {
|
|
if (isDigit(rest[i])) {
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// already validated we're parsing a valid number
|
|
const right = std.fmt.parseUnsigned(u32, rest[start..i], 10) catch unreachable;
|
|
|
|
if (rest[i] != ')') {
|
|
continue;
|
|
}
|
|
|
|
// consume ')'
|
|
i += 1;
|
|
|
|
total += left * right;
|
|
}
|
|
|
|
return total;
|
|
}
|
|
|
|
test "part1 example" {
|
|
const alloc = std.testing.allocator;
|
|
|
|
const example = "xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))";
|
|
|
|
var stream = std.io.fixedBufferStream(example);
|
|
|
|
const result = try part1(alloc, stream.reader());
|
|
|
|
try std.testing.expect(result == 161);
|
|
}
|
|
|
|
test "part1 input" {
|
|
const alloc = std.testing.allocator;
|
|
|
|
const filename = "inputs/day3.txt";
|
|
|
|
const file_reader = try utils.FileReader.init(alloc, filename);
|
|
defer file_reader.deinit();
|
|
|
|
const result = try part1(alloc, file_reader.reader());
|
|
|
|
try std.testing.expect(result == 156388521);
|
|
}
|
|
|
|
test "part2 example" {
|
|
const alloc = std.testing.allocator;
|
|
|
|
const example = "xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))";
|
|
|
|
var stream = std.io.fixedBufferStream(example);
|
|
|
|
const result = try part2(alloc, stream.reader());
|
|
|
|
try std.testing.expect(result == 48);
|
|
}
|
|
|
|
test "part2 input" {
|
|
const alloc = std.testing.allocator;
|
|
|
|
const filename = "inputs/day3.txt";
|
|
|
|
const file_reader = try utils.FileReader.init(alloc, filename);
|
|
defer file_reader.deinit();
|
|
|
|
const result = try part2(alloc, file_reader.reader());
|
|
|
|
try std.testing.expect(result == 75920122);
|
|
}
|