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); }