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