diff --git a/src/aoc.zig b/src/aoc.zig index a887f0f..05853e9 100644 --- a/src/aoc.zig +++ b/src/aoc.zig @@ -3,7 +3,7 @@ const Allocator = std.mem.Allocator; pub const Input = struct { allocator: Allocator, - lines: [][]const u8 + lines: []const []const u8 }; pub const Result = union(enum) { diff --git a/src/day2.zig b/src/day2.zig index 3604cfb..fe8735f 100644 --- a/src/day2.zig +++ b/src/day2.zig @@ -42,7 +42,7 @@ fn parse_game(game: *Game, line: []const u8) void { } } -fn parse_games(allocator: Allocator, lines: [][]const u8) ![]Game { +fn parse_games(allocator: Allocator, lines: []const []const u8) ![]Game { var games = try allocator.alloc(Game, lines.len); for (0.., lines) |i, line| { parse_game(&games[i], line); @@ -102,29 +102,23 @@ pub fn part2(input: *aoc.Input) !aoc.Result { return .{ .uint = sum }; } +const example_input = [_][]const u8{ + "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green", + "Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue", + "Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red", + "Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red", + "Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green" +}; + test "part 1 example" { - var lines = [_][]const u8{ - "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green", - "Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue", - "Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red", - "Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red", - "Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green" - }; - var input = aoc.Input{ .lines = &lines, .allocator = std.testing.allocator }; + var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator }; var actual = try part1(&input); var expected: u32 = 8; try std.testing.expectEqual(expected, actual.uint); } test "part 2 example" { - var lines = [_][]const u8{ - "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green", - "Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue", - "Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red", - "Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red", - "Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green" - }; - var input = aoc.Input{ .lines = &lines, .allocator = std.testing.allocator }; + var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator }; var actual = try part2(&input); var expected: u32 = 2286; try std.testing.expectEqual(expected, actual.uint); diff --git a/src/day3.zig b/src/day3.zig index b38a586..e4e13f5 100644 --- a/src/day3.zig +++ b/src/day3.zig @@ -28,7 +28,7 @@ fn append_unique(list: *PointList, point: PointU32) !void { try list.append(point); } -fn find_neighbouring_numbers(found_list: *PointList, lines: [][]const u8, x: usize, y: usize) !void { +fn find_neighbouring_numbers(found_list: *PointList, lines: []const []const u8, x: usize, y: usize) !void { const neighbours = .{ .{ -1, -1 }, .{ 0, -1 }, .{ 1, -1 }, .{ -1, 0 }, .{ 1, 0 }, @@ -107,39 +107,28 @@ pub fn part2(input: *aoc.Input) !aoc.Result { return .{ .uint = sum }; } +const example_input = [_][]const u8{ + "467..114..", + "...*......", + "..35..633.", + "......#...", + "617*......", + ".....+.58.", + "..592.....", + "......755.", + "...$.*....", + ".664.598.." +}; + test "part 1 example" { - var lines = [_][]const u8{ - "467..114..", - "...*......", - "..35..633.", - "......#...", - "617*......", - ".....+.58.", - "..592.....", - "......755.", - "...$.*....", - ".664.598.." - }; - var input = aoc.Input{ .lines = &lines, .allocator = std.testing.allocator }; + var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator }; var actual = try part1(&input); var expected: u32 = 4361; try std.testing.expectEqual(expected, actual.uint); } test "part 2 example" { - var lines = [_][]const u8{ - "467..114..", - "...*......", - "..35..633.", - "......#...", - "617*......", - ".....+.58.", - "..592.....", - "......755.", - "...$.*....", - ".664.598.." - }; - var input = aoc.Input{ .lines = &lines, .allocator = std.testing.allocator }; + var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator }; var actual = try part2(&input); var expected: u32 = 467835; try std.testing.expectEqual(expected, actual.uint); diff --git a/src/day4.zig b/src/day4.zig new file mode 100644 index 0000000..4827e40 --- /dev/null +++ b/src/day4.zig @@ -0,0 +1,119 @@ +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); +} diff --git a/src/main.zig b/src/main.zig index 81e7322..3aee4dd 100644 --- a/src/main.zig +++ b/src/main.zig @@ -22,7 +22,8 @@ fn create_day(comptime module: anytype) aoc.Day { const Days = [_]aoc.Day{ create_day(@import("day1.zig")), create_day(@import("day2.zig")), - create_day(@import("day3.zig")) + create_day(@import("day3.zig")), + create_day(@import("day4.zig")), }; pub fn main() !u8 { @@ -100,4 +101,5 @@ test { _ = @import("day1.zig"); _ = @import("day2.zig"); _ = @import("day3.zig"); + _ = @import("day4.zig"); }