const std = @import("std"); const print = std.debug.print; const assert = std.debug.assert; const ArrayList = std.ArrayList; const mem = std.mem; const fin = mem.trim(u8, @embedFile("./input.txt"), &std.ascii.whitespace); var gpa = std.heap.GeneralPurposeAllocator(.{}){}; const allocator = gpa.allocator(); const Color = enum { red, green, blue, }; const Ball = struct { color: Color, num: u64, }; const Set = struct { red: u64, green: u64, blue: u64, }; const Game = struct { var curGameID: u64 = 1; id: u64, sets: ArrayList(Set), fn init(self: *@This()) !void { self.id = curGameID; self.sets = ArrayList(Set).init(allocator); curGameID += 1; } fn deinit(self: *@This()) void { self.sets.deinit(); } }; pub fn parseBall(text: []const u8) Ball { const trimmed = mem.trim(u8, text, &std.ascii.whitespace); var splitBalls = mem.splitScalar(u8, trimmed, ' '); const nballs = std.fmt.parseInt(u64, splitBalls.next().?, 10) catch unreachable; var color: Color = undefined; color = blk: { const s = splitBalls.next().?; if (mem.eql(u8, s, "red")) { break :blk .red; } if (mem.eql(u8, s, "green")) { break :blk .green; } if (mem.eql(u8, s, "blue")) { break :blk .blue; } unreachable; }; return Ball{ .color = color, .num = nballs, }; } pub fn parseSet(text: []const u8) Set { const trimmed = mem.trim(u8, text, &std.ascii.whitespace); var splitBalls = mem.splitScalar(u8, trimmed, ','); var r: u64 = 0; var g: u64 = 0; var b: u64 = 0; while (splitBalls.next()) |ball| { const parsed_ball = parseBall(ball); switch (parsed_ball.color) { .red => r += parsed_ball.num, .green => g += parsed_ball.num, .blue => b += parsed_ball.num, } } return Set{ .red = r, .green = g, .blue = b, }; } pub fn parseGame(text: []const u8) !Game { const trimmed = mem.trim(u8, text, &std.ascii.whitespace); var splitSets = mem.splitScalar(u8, trimmed, ';'); var game: Game = undefined; try game.init(); while (splitSets.next()) |set| { try game.sets.append(parseSet(set)); } return game; } pub fn part1(input: ArrayList(Game)) void { var ans: u64 = 0; outer: for (input.items) |game| { for (game.sets.items) |set| { if (set.red > 12 or set.green > 13 or set.blue > 14) { continue :outer; } } ans += game.id; } print("{d}\n", .{ans}); } pub fn part2(input: ArrayList(Game)) void { var ans: u64 = 0; for (input.items) |game| { var max = Set{ .red = 0, .blue = 0, .green = 0, }; for (game.sets.items) |set| { max.red = @max(max.red, set.red); max.green = @max(max.green, set.green); max.blue = @max(max.blue, set.blue); } ans += max.red * max.green * max.blue; } print("{d}\n", .{ans}); } pub fn main() !void { var splitLines = mem.splitScalar(u8, fin, '\n'); var games = ArrayList(Game).init(allocator); defer games.deinit(); while (splitLines.next()) |line| { var splitGame = mem.splitScalar(u8, line, ':'); _ = splitGame.next(); const game = try parseGame(splitGame.next().?); try games.append(game); } part1(games); part2(games); }