solve day 22
This commit is contained in:
parent
ca8e8cf3e9
commit
13a6c53fa6
365
src/day22.zig
Normal file
365
src/day22.zig
Normal file
@ -0,0 +1,365 @@
|
||||
const aoc = @import("./aoc.zig");
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const Vec3 = struct {
|
||||
x: i32,
|
||||
y: i32,
|
||||
z: i32
|
||||
};
|
||||
|
||||
const Brick = struct {
|
||||
v1: Vec3,
|
||||
v2: Vec3,
|
||||
|
||||
fn cubeIterator(self: Brick) CubeIterator {
|
||||
const dx = self.v2.x - self.v1.x;
|
||||
const dy = self.v2.y - self.v1.y;
|
||||
const dz = self.v2.z - self.v1.z;
|
||||
|
||||
return CubeIterator{
|
||||
.current = self.v1,
|
||||
.count = @abs(dx) + @abs(dy) + @abs(dz) + 1,
|
||||
.direction = Vec3{
|
||||
.x = @min(@max(dx, -1), 1),
|
||||
.y = @min(@max(dy, -1), 1),
|
||||
.z = @min(@max(dz, -1), 1)
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const CubeIterator = struct {
|
||||
current: Vec3,
|
||||
direction: Vec3,
|
||||
count: u32,
|
||||
|
||||
fn next(self: *CubeIterator) ?Vec3 {
|
||||
if (self.count == 0) return null;
|
||||
const result = self.current;
|
||||
|
||||
self.count -= 1;
|
||||
self.current.x += self.direction.x;
|
||||
self.current.y += self.direction.y;
|
||||
self.current.z += self.direction.z;
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
const Bricks = std.ArrayList(Brick);
|
||||
|
||||
const BrickId = u11;
|
||||
const BrickVolume = struct {
|
||||
allocator: Allocator,
|
||||
bricks: []Brick,
|
||||
|
||||
cube_lookup: []?BrickId,
|
||||
x_size: u32,
|
||||
y_size: u32,
|
||||
z_size: u32,
|
||||
|
||||
fn init(allocator: Allocator, bricks: []const Brick) !BrickVolume {
|
||||
const local_bricks = try allocator.dupe(Brick, bricks);
|
||||
errdefer allocator.free(local_bricks);
|
||||
|
||||
const upper_bounds = getUpperBounds(bricks);
|
||||
const x_size: u32 = @intCast(upper_bounds.x + 1);
|
||||
const y_size: u32 = @intCast(upper_bounds.y + 1);
|
||||
const z_size: u32 = @intCast(upper_bounds.z + 1);
|
||||
const cube_lookup = try allocator.alloc(?BrickId, x_size * y_size * z_size);
|
||||
errdefer allocator.free(cube_lookup);
|
||||
@memset(cube_lookup, null);
|
||||
|
||||
const self = BrickVolume{
|
||||
.allocator = allocator,
|
||||
.bricks = local_bricks,
|
||||
.cube_lookup = cube_lookup,
|
||||
.x_size = x_size,
|
||||
.y_size = y_size,
|
||||
.z_size = z_size,
|
||||
};
|
||||
|
||||
for (0.., bricks) |brick_id, brick| {
|
||||
self.addToLookup(@intCast(brick_id), brick);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
fn getIndex(self: BrickVolume, x: u32, y: u32, z: u32) u32 {
|
||||
assert(x < self.x_size);
|
||||
assert(y < self.y_size);
|
||||
assert(z < self.z_size);
|
||||
return z * self.x_size * self.y_size + y * self.x_size + x;
|
||||
}
|
||||
|
||||
fn getIndexVec3(self: BrickVolume, vec3: Vec3) u32 {
|
||||
return self.getIndex(
|
||||
@intCast(vec3.x),
|
||||
@intCast(vec3.y),
|
||||
@intCast(vec3.z),
|
||||
);
|
||||
}
|
||||
|
||||
fn deinit(self: BrickVolume) void {
|
||||
self.allocator.free(self.bricks);
|
||||
self.allocator.free(self.cube_lookup);
|
||||
}
|
||||
|
||||
fn addToLookup(self: BrickVolume, brick_id: BrickId, brick: Brick) void {
|
||||
var cube_iter = brick.cubeIterator();
|
||||
while (cube_iter.next()) |cube| {
|
||||
const cube_idx = self.getIndexVec3(cube);
|
||||
assert(self.cube_lookup[cube_idx] == null);
|
||||
self.cube_lookup[cube_idx] = brick_id;
|
||||
}
|
||||
}
|
||||
|
||||
fn removeFromLookup(self: BrickVolume, brick: Brick) void {
|
||||
var cube_iter = brick.cubeIterator();
|
||||
while (cube_iter.next()) |cube| {
|
||||
const cube_idx = self.getIndexVec3(cube);
|
||||
assert(self.cube_lookup[cube_idx] != null);
|
||||
self.cube_lookup[cube_idx] = null;
|
||||
}
|
||||
}
|
||||
|
||||
fn moveBrickDown(self: BrickVolume, brick_id: BrickId, dz: u32) void {
|
||||
var brick = &self.bricks[brick_id];
|
||||
|
||||
self.removeFromLookup(brick.*);
|
||||
|
||||
brick.v1.z -= @intCast(dz);
|
||||
brick.v2.z -= @intCast(dz);
|
||||
assert(brick.v1.z > 0);
|
||||
assert(brick.v2.z > 0);
|
||||
|
||||
self.addToLookup(brick_id, brick.*);
|
||||
}
|
||||
|
||||
fn canBrickMoveDown(self: BrickVolume, brick_id: BrickId, dz: u32) bool {
|
||||
var brick = self.bricks[brick_id];
|
||||
|
||||
var cube_iter = brick.cubeIterator();
|
||||
cube_iter.current.z -= @intCast(dz);
|
||||
while (cube_iter.next()) |cube| {
|
||||
if (cube.z <= 0) return false;
|
||||
|
||||
const cube_idx = self.getIndexVec3(cube);
|
||||
if (self.cube_lookup[cube_idx] != null and self.cube_lookup[cube_idx] != brick_id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
fn parseVec3(str: []const u8) !Vec3 {
|
||||
var components: [3]i32 = undefined;
|
||||
var count: u32 = 0;
|
||||
|
||||
var iter = std.mem.splitScalar(u8, str, ',');
|
||||
while (iter.next()) |part| {
|
||||
if (count == 3) return error.TooManyComponents;
|
||||
components[count] = try std.fmt.parseInt(i32, part, 10);
|
||||
count += 1;
|
||||
}
|
||||
|
||||
if (count < 3) {
|
||||
return error.MissingComponents;
|
||||
}
|
||||
|
||||
return Vec3{
|
||||
.x = components[0],
|
||||
.y = components[1],
|
||||
.z = components[2],
|
||||
};
|
||||
}
|
||||
|
||||
fn parseBrick(line: []const u8) !Brick {
|
||||
const separator = std.mem.indexOfScalar(u8, line, '~') orelse return error.MissingSeparator;
|
||||
return Brick{
|
||||
.v1 = try parseVec3(line[0..separator]),
|
||||
.v2 = try parseVec3(line[(separator+1)..]),
|
||||
};
|
||||
}
|
||||
|
||||
fn parseInput(allocator: Allocator, lines: []const []const u8) !Bricks {
|
||||
var bricks = Bricks.init(allocator);
|
||||
errdefer bricks.deinit();
|
||||
|
||||
for (lines) |line| {
|
||||
try bricks.append(try parseBrick(line));
|
||||
}
|
||||
|
||||
return bricks;
|
||||
}
|
||||
|
||||
fn getUpperBounds(bricks: []const Brick) Vec3 {
|
||||
var upper = bricks[0].v1;
|
||||
|
||||
for (bricks) |brick| {
|
||||
inline for (.{ brick.v1, brick.v2 }) |p| {
|
||||
upper.x = @max(upper.x, p.x);
|
||||
upper.y = @max(upper.y, p.y);
|
||||
upper.z = @max(upper.z, p.z);
|
||||
}
|
||||
}
|
||||
|
||||
return upper;
|
||||
}
|
||||
|
||||
fn dropBricksUntilStable(brick_volume: BrickVolume) void {
|
||||
var count: u32 = 0;
|
||||
var brick_moved = true;
|
||||
while (brick_moved) {
|
||||
brick_moved = false;
|
||||
count += 1;
|
||||
|
||||
for (0..brick_volume.bricks.len) |i| {
|
||||
const brick_id: BrickId = @intCast(i);
|
||||
if (brick_volume.canBrickMoveDown(brick_id, 1)) {
|
||||
brick_volume.moveBrickDown(brick_id, 1);
|
||||
brick_moved = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn part1(input: *aoc.Input) !aoc.Result {
|
||||
const allocator = input.allocator;
|
||||
const bricks = try parseInput(allocator, input.lines);
|
||||
defer bricks.deinit();
|
||||
|
||||
var brick_volume = try BrickVolume.init(allocator, bricks.items);
|
||||
defer brick_volume.deinit();
|
||||
|
||||
dropBricksUntilStable(brick_volume);
|
||||
|
||||
var answer: u32 = 0;
|
||||
for (0.., brick_volume.bricks) |i, brick| {
|
||||
const brick_id: BrickId = @intCast(i);
|
||||
|
||||
var can_be_removed = true;
|
||||
brick_volume.removeFromLookup(brick);
|
||||
for (0..brick_volume.bricks.len) |j| {
|
||||
if (i == j) continue;
|
||||
|
||||
if (brick_volume.canBrickMoveDown(@intCast(j), 1)) {
|
||||
can_be_removed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
brick_volume.addToLookup(brick_id, brick);
|
||||
|
||||
if (can_be_removed) {
|
||||
answer += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return .{ .uint = answer };
|
||||
}
|
||||
|
||||
pub fn part2(input: *aoc.Input) !aoc.Result {
|
||||
const allocator = input.allocator;
|
||||
const bricks = try parseInput(allocator, input.lines);
|
||||
defer bricks.deinit();
|
||||
|
||||
var brick_volume = try BrickVolume.init(allocator, bricks.items);
|
||||
defer brick_volume.deinit();
|
||||
|
||||
dropBricksUntilStable(brick_volume);
|
||||
|
||||
var bricks_deps = std.ArrayList(std.ArrayList(BrickId)).init(allocator);
|
||||
for (0..brick_volume.bricks.len) |_| {
|
||||
try bricks_deps.append(std.ArrayList(BrickId).init(allocator));
|
||||
}
|
||||
defer {
|
||||
for (bricks_deps.items) |brick_deps| {
|
||||
brick_deps.deinit();
|
||||
}
|
||||
bricks_deps.deinit();
|
||||
}
|
||||
|
||||
for (0.., brick_volume.bricks) |i, brick| {
|
||||
const brick_id: BrickId = @intCast(i);
|
||||
var brick_deps: *std.ArrayList(BrickId) = &bricks_deps.items[brick_id];
|
||||
|
||||
var cube_iter = brick.cubeIterator();
|
||||
cube_iter.current.z -= 1;
|
||||
while (cube_iter.next()) |cube| {
|
||||
if (cube.z >= brick_volume.z_size) continue;
|
||||
const cube_idx = brick_volume.getIndexVec3(cube);
|
||||
const brick_at_cube = brick_volume.cube_lookup[cube_idx];
|
||||
|
||||
if (brick_at_cube != null and brick_at_cube != brick_id) {
|
||||
if (std.mem.indexOfScalar(BrickId, brick_deps.items, brick_at_cube.?) == null) {
|
||||
try brick_deps.append(brick_at_cube.?);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var answer: u64 = 0;
|
||||
var removed_bricks = try allocator.alloc(bool, bricks.items.len);
|
||||
defer allocator.free(removed_bricks);
|
||||
|
||||
for (0..bricks.items.len) |i| {
|
||||
@memset(removed_bricks, false);
|
||||
removed_bricks[i] = true;
|
||||
|
||||
var unstable = true;
|
||||
while (unstable) {
|
||||
unstable = false;
|
||||
|
||||
for (0.., bricks_deps.items) |j, brick_deps| {
|
||||
const brick = brick_volume.bricks[j];
|
||||
|
||||
// Any brick on the ground will always be stable
|
||||
if (brick.v1.z == 1 or brick.v2.z == 1) continue;
|
||||
|
||||
// Do not check removed bricks
|
||||
if (removed_bricks[j]) continue;
|
||||
|
||||
var is_brick_supported = false;
|
||||
for (brick_deps.items) |brick_dep| {
|
||||
if (!removed_bricks[brick_dep]) {
|
||||
is_brick_supported = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_brick_supported) {
|
||||
unstable = true;
|
||||
removed_bricks[j] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const removed_count = std.mem.count(bool, removed_bricks, &[_]bool{ true });
|
||||
answer += (removed_count - 1);
|
||||
}
|
||||
|
||||
return .{ .uint = answer };
|
||||
}
|
||||
|
||||
const example_input = [_][]const u8{
|
||||
"1,0,1~1,2,1",
|
||||
"0,0,2~2,0,2",
|
||||
"0,2,3~2,2,3",
|
||||
"0,0,4~0,2,4",
|
||||
"2,0,5~2,2,5",
|
||||
"0,1,6~2,1,6",
|
||||
"1,1,8~1,1,9",
|
||||
};
|
||||
|
||||
test "part 1 example" {
|
||||
try aoc.expectAnswerUInt(part1, 5, &example_input);
|
||||
}
|
||||
|
||||
test "part 2 example" {
|
||||
try aoc.expectAnswerUInt(part2, 7, &example_input);
|
||||
}
|
@ -42,6 +42,7 @@ const Days = [_]aoc.Day{
|
||||
create_day(@import("./day19.zig")),
|
||||
create_day(@import("./day20.zig")),
|
||||
create_day(@import("./day21.zig")),
|
||||
create_day(@import("./day22.zig")),
|
||||
};
|
||||
|
||||
fn kilobytes(count: u32) u32 {
|
||||
@ -206,4 +207,5 @@ test {
|
||||
_ = @import("./day19.zig");
|
||||
_ = @import("./day20.zig");
|
||||
_ = @import("./day21.zig");
|
||||
_ = @import("./day22.zig");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user