You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
226 lines
5.7 KiB
226 lines
5.7 KiB
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) void {
|
|
if (this.is_settled) {
|
|
return;
|
|
}
|
|
|
|
var min_z: u16 = 1;
|
|
for (bricks) |*other| {
|
|
if (this.is_above(other.*)) {
|
|
settleBrick(bricks, other);
|
|
min_z = @max(min_z, other.z.last + 1);
|
|
}
|
|
}
|
|
|
|
this.z.last -= (this.z.first - min_z);
|
|
this.z.first = min_z;
|
|
this.is_settled = true;
|
|
}
|
|
|
|
fn isDisintegratable(bricks: []const Brick, this: Brick) bool {
|
|
for (bricks) |other| {
|
|
if (other.is_on(this)) {
|
|
var supports_number: usize = 0;
|
|
for (bricks) |support| {
|
|
if (other.is_on(support)) {
|
|
supports_number += 1;
|
|
}
|
|
}
|
|
|
|
if (supports_number == 1) {
|
|
//std.debug.print("{any} is not disintegratable because {any} is on it\n", .{ this, other });
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
fn solveForBricks(bricks: []Brick) usize {
|
|
for (bricks) |*this| {
|
|
settleBrick(bricks, this);
|
|
}
|
|
|
|
//std.debug.print("Settled {d} bricks\n", .{bricks.len});
|
|
//for (bricks) |brick| {
|
|
//std.debug.print("{any}\n", .{brick});
|
|
//}
|
|
var result: usize = 0;
|
|
for (bricks) |this| {
|
|
if (isDisintegratable(bricks, this)) {
|
|
//std.debug.print("Can be disintegrated: {any}\n", .{this});
|
|
result += 1;
|
|
}
|
|
}
|
|
|
|
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});
|
|
}
|
|
|