120 lines
3.5 KiB
Zig
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);
|
|
}
|