aoc-2023/src/day4.zig
2023-12-11 22:11:40 +02:00

120 lines
3.5 KiB
Zig

const aoc = @import("./aoc.zig");
const std = @import("std");
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const BoundedUintArray = std.BoundedArray(u32, 32);
const Card = struct {
winning: BoundedUintArray,
mine: BoundedUintArray
};
const CardArray = std.ArrayList(Card);
fn parse_numbers(result: *BoundedUintArray, text: []const u8) !void {
var i: usize = 0;
var tokens = std.mem.tokenizeScalar(u8, text, ' ');
while (tokens.next()) |token| : (i += 1) {
const number = try std.fmt.parseInt(u32, token, 10);
try result.append(number);
}
}
fn parse_input(allocator: Allocator, lines: []const []const u8) !CardArray {
var cards = CardArray.init(allocator);
for (lines) |line| {
var card = Card{
.winning = try BoundedUintArray.init(0),
.mine = try BoundedUintArray.init(0)
};
const colon = std.mem.indexOfScalar(u8, line, ':') orelse @panic("Invalid format");
const bar = std.mem.indexOfScalar(u8, line, '|') orelse @panic("Invalid format");
const winning_str = line[(colon+2)..(bar-1)];
try parse_numbers(&card.winning, winning_str);
const mine_str = line[(bar+2)..];
try parse_numbers(&card.mine, mine_str);
try cards.append(card);
}
return cards;
}
pub fn part1(input: *aoc.Input) !aoc.Result {
const allocator = input.allocator;
const cards = try parse_input(allocator, input.lines);
defer cards.deinit();
var result: u32 = 0;
for (cards.items) |card| {
var matches: u32 = 0;
for (card.mine.slice()) |num| {
if (std.mem.indexOfScalar(u32, card.winning.slice(), num) != null) {
matches += 1;
}
}
if (matches >= 1) {
result += try std.math.powi(u32, 2, matches-1);
}
}
return .{ .uint = result };
}
pub fn part2(input: *aoc.Input) !aoc.Result {
const allocator = input.allocator;
const cards = try parse_input(allocator, input.lines);
defer cards.deinit();
var multipliers = try allocator.alloc(u32, cards.items.len);
defer allocator.free(multipliers);
@memset(multipliers, 1);
for (cards.items, 0..) |card, i| {
var matches: u32 = 0;
for (card.mine.slice()) |num| {
if (std.mem.indexOfScalar(u32, card.winning.slice(), num) != null) {
matches += 1;
}
}
for (0..matches) |j| {
multipliers[i+j+1] += multipliers[i];
}
}
var result: u32 = 0;
for (multipliers) |count| {
result += count;
}
return .{ .uint = result };
}
const example_input = [_][]const u8{
"Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53",
"Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19",
"Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1",
"Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83",
"Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36",
"Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11"
};
test "part 1 example" {
var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator };
var actual = try part1(&input);
var expected: u32 = 13;
try std.testing.expectEqual(expected, actual.uint);
}
test "part 2 example" {
var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator };
var actual = try part2(&input);
var expected: u32 = 30;
try std.testing.expectEqual(expected, actual.uint);
}