aoc-2023/src/day2.zig

120 lines
3.5 KiB
Zig

const std = @import("std");
const aoc = @import("./aoc.zig");
const Allocator = std.mem.Allocator;
const Cube = enum { Red, Green, Blue };
const CubeSet = std.EnumArray(Cube, u32);
const Game = struct {
sets: [10]CubeSet,
set_count: u32,
};
fn parse_cube(cube_name: []const u8) ?Cube {
if (std.mem.eql(u8, cube_name, "blue")) {
return .Blue;
} else if (std.mem.eql(u8, cube_name, "red")) {
return .Red;
} if (std.mem.eql(u8, cube_name, "green")) {
return .Green;
}
return null;
}
fn parse_game(game: *Game, line: []const u8) void {
const colon = std.mem.indexOf(u8, line, ":") orelse @panic("Invalid line, missing ':'");
game.set_count = 0;
var game_sets = std.mem.splitSequence(u8, line[(colon+2)..], "; ");
while (game_sets.next()) |game_set_str| {
var cube_set = &game.sets[game.set_count];
cube_set.* = CubeSet.initFill(0);
game.set_count += 1;
var cubes = std.mem.splitSequence(u8, game_set_str, ", ");
while (cubes.next()) |cube_count| {
const space = std.mem.indexOf(u8, cube_count, " ") orelse @panic("Invalid format, expected space");
const cube = parse_cube(cube_count[(space+1)..]) orelse @panic("Invalid cube name");
const count = std.fmt.parseInt(u32, cube_count[0..space], 10) catch @panic("Invalid format");
cube_set.set(cube, count);
}
}
}
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);
}
return games;
}
pub fn part1(input: *aoc.Input) !aoc.Result {
const lines = input.lines;
const allocator = input.allocator;
const games = try parse_games(allocator, lines);
defer allocator.free(games);
const max_red = 12;
const max_green = 13;
const max_blue = 14;
var sum: u32 = 0;
for (1.., games) |idx, game| {
var possible = true;
for (game.sets[0..game.set_count]) |cube_set| {
if ((cube_set.get(.Red) > max_red) or (cube_set.get(.Blue) > max_blue) or (cube_set.get(.Green) > max_green)) {
possible = false;
break;
}
}
if (possible) {
sum += @intCast(idx);
}
}
return .{ .uint = sum };
}
pub fn part2(input: *aoc.Input) !aoc.Result {
const lines = input.lines;
const allocator = input.allocator;
const games = try parse_games(allocator, lines);
defer allocator.free(games);
var sum: u32 = 0;
for (games) |game| {
var max_red: u32 = 0;
var max_green: u32 = 0;
var max_blue: u32 = 0;
for (game.sets[0..game.set_count]) |cube_set| {
max_red = @max(max_red, cube_set.get(.Red));
max_green = @max(max_green, cube_set.get(.Green));
max_blue = @max(max_blue, cube_set.get(.Blue));
}
sum += max_red*max_green*max_blue;
}
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" {
try aoc.expectAnswerUInt(part1, 8, &example_input);
}
test "part 2 example" {
try aoc.expectAnswerUInt(part2, 2286, &example_input);
}