day 22, part 2

main
Inga 🏳‍🌈 5 months ago
parent e9f11fcecc
commit 6fa6021217
  1. 70
      day22-hard/build.zig
  2. 1229
      day22-hard/hard.in
  3. 7
      day22-hard/sample.in
  4. 229
      day22-hard/src/main.zig

@ -0,0 +1,70 @@
const std = @import("std");
// Although this function looks imperative, note that its job is to
// declaratively construct a build graph that will be executed by an external
// runner.
pub fn build(b: *std.Build) void {
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
// for restricting supported target set are available.
const target = b.standardTargetOptions(.{});
// Standard optimization options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
// set a preferred release mode, allowing the user to decide how to optimize.
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "day22-hard",
// In this case the main source file is merely a path, however, in more
// complicated build scripts, this could be a generated file.
.root_source_file = .{ .path = "src/main.zig" },
.target = target,
.optimize = optimize,
});
// This declares intent for the executable to be installed into the
// standard location when the user invokes the "install" step (the default
// step when running `zig build`).
b.installArtifact(exe);
// This *creates* a Run step in the build graph, to be executed when another
// step is evaluated that depends on it. The next line below will establish
// such a dependency.
const run_cmd = b.addRunArtifact(exe);
// By making the run step depend on the install step, it will be run from the
// installation directory rather than directly from within the cache directory.
// This is not necessary, however, if the application depends on other installed
// files, this ensures they will be present and in the expected location.
run_cmd.step.dependOn(b.getInstallStep());
// This allows the user to pass arguments to the application in the build
// command itself, like this: `zig build run -- arg1 arg2 etc`
if (b.args) |args| {
run_cmd.addArgs(args);
}
// This creates a build step. It will be visible in the `zig build --help` menu,
// and can be selected like this: `zig build run`
// This will evaluate the `run` step rather than the default, which is "install".
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
// Creates a step for unit testing. This only builds the test executable
// but does not run it.
const unit_tests = b.addTest(.{
.root_source_file = .{ .path = "src/main.zig" },
.target = target,
.optimize = optimize,
});
const run_unit_tests = b.addRunArtifact(unit_tests);
// Similar to creating the run step earlier, this exposes a `test` step to
// the `zig build --help` menu, providing a way for the user to request
// running the unit tests.
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_unit_tests.step);
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,7 @@
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

@ -0,0 +1,229 @@
const std = @import("std");
fn StackList(comptime T: type, comptime capacity_type: type, comptime capacity: capacity_type) type {
return struct {
const Self = @This();
mem: [capacity]T,
length: capacity_type,
fn add(self: *Self, value: T) void {
self.mem[self.length] = value;
self.length += 1;
}
fn has(self: *Self, needle: T) bool {
for (0..self.length) |i| {
if (self.mem[i] == needle) {
return true;
}
}
return false;
}
fn getMutableSlice(self: *Self) []T {
return (&self.mem)[0..self.length];
}
fn getSlice(self: *const Self) []const T {
return self.mem[0..self.length];
}
fn init() Self {
return Self{
.mem = undefined,
.length = 0,
};
}
};
}
fn readNumber(comptime T: type, line: []const u8, index: *usize) T {
var result: T = 0;
while (index.* < line.len) : (index.* += 1) {
const char = line[index.*];
switch (char) {
'0'...'9' => {
result = result * 10 + (char - '0');
},
else => {
break;
},
}
}
return result;
}
const Segment = packed struct(u32) {
first: u16,
last: u16,
fn init(a: u16, b: u16) Segment {
if (a < b) {
return .{
.first = a,
.last = b,
};
} else {
return .{
.first = b,
.last = a,
};
}
}
fn intersects(a: Segment, b: Segment) bool {
return a.first <= b.last and a.last >= b.first;
}
};
const Brick = struct {
x: Segment,
y: Segment,
z: Segment,
is_settled: bool,
fn is_above(self: *const Brick, other: Brick) bool {
return Segment.intersects(self.x, other.x) and Segment.intersects(self.y, other.y) and self.z.first > other.z.first;
}
fn is_on(self: *const Brick, other: Brick) bool {
return Segment.intersects(self.x, other.x) and Segment.intersects(self.y, other.y) and self.z.first == other.z.last + 1;
}
pub fn format(value: Brick, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
return writer.print("{d},{d},{d}~{d},{d},{d}", .{ value.x.first, value.y.first, value.z.first, value.x.last, value.y.last, value.z.last });
}
};
fn settleBrick(bricks: []Brick, this: *Brick) usize {
if (this.is_settled) {
return 0;
}
var moved_bricks: usize = 0;
var min_z: u16 = 1;
for (bricks) |*other| {
if (this.is_above(other.*)) {
moved_bricks += settleBrick(bricks, other);
min_z = @max(min_z, other.z.last + 1);
}
}
if (this.z.first != min_z) {
moved_bricks += 1;
}
this.z.last -= (this.z.first - min_z);
this.z.first = min_z;
this.is_settled = true;
return moved_bricks;
}
fn settleBricks(bricks: []Brick) usize {
var result: usize = 0;
for (bricks) |*this| {
result += settleBrick(bricks, this);
}
return result;
}
fn simulateDisintegration(bricks: []const Brick, brick_number: usize) usize {
var simulated_bricks_array: [1500]Brick = undefined;
std.mem.copy(Brick, &simulated_bricks_array, bricks);
var simulated_bricks = simulated_bricks_array[0..bricks.len];
for (simulated_bricks) |*brick| {
brick.*.is_settled = false;
}
simulated_bricks[brick_number].z = Segment.init(0, 0);
simulated_bricks[brick_number].is_settled = true;
return settleBricks(simulated_bricks);
}
fn solveForBricks(bricks: []Brick) usize {
_ = settleBricks(bricks);
var result: usize = 0;
for (0..bricks.len) |brick_number| {
//std.debug.print("Simulating disintegration of brick {d}\n", .{brick_number});
result += simulateDisintegration(bricks, brick_number);
}
return result;
}
fn solveLines(lines: []const []const u8) usize {
var bricks_list = StackList(Brick, usize, 1500).init();
for (lines) |line| {
var i: usize = 0;
var x1 = readNumber(u16, line, &i);
i += 1;
var y1 = readNumber(u16, line, &i);
i += 1;
var z1 = readNumber(u16, line, &i);
i += 1;
var x2 = readNumber(u16, line, &i);
i += 1;
var y2 = readNumber(u16, line, &i);
i += 1;
var z2 = readNumber(u16, line, &i);
bricks_list.add(.{
.x = Segment.init(x1, x2),
.y = Segment.init(y1, y2),
.z = Segment.init(z1, z2),
.is_settled = false,
});
}
return solveForBricks(bricks_list.getMutableSlice());
}
pub fn solveAll(reader: anytype) !usize {
var result: usize = 0;
while (true) {
var allocator_buffer: [50000]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&allocator_buffer);
var allocator = fba.allocator();
var lines = StackList([]u8, usize, 1500).init();
var empty_line_reached = false;
var line_buffer: [1000]u8 = undefined;
while (try reader.readUntilDelimiterOrEof(&line_buffer, '\n')) |line| {
if (line.len == 0) {
empty_line_reached = true;
break;
}
lines.add(try allocator.dupe(u8, line));
}
result += solveLines(lines.getSlice());
if (!empty_line_reached) {
return result;
}
}
}
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
const raw_in = std.io.getStdIn();
var buffered_reader = std.io.bufferedReader(raw_in.reader());
var reader = buffered_reader.reader();
const result = try solveAll(&reader);
try stdout.print("{d}\n", .{result});
}
Loading…
Cancel
Save