const std = @import("std");
const builtin = @import("builtin");
const Point = struct {
x: u32,
y: u32,
};
const Input = struct {
points: []Point,
folds: []Point,
};
pub fn readInput(allocator: std.mem.Allocator, reader: anytype) !Input {
var points = std.ArrayList(Point).init(allocator);
var folds = std.ArrayList(Point).init(allocator);
while (true) {
var line: []u8 = undefined;
if (builtin.os.tag == .windows and !builtin.is_test) {
// NOTE: Read another byte on windows due to two-byte eol.
// NOTE: Check if in testing since tests only add single-byte eol in multiline strings.
line = reader.readUntilDelimiterAlloc(allocator, '\r', 512) catch break;
_ = try reader.readByte();
} else {
line = reader.readUntilDelimiterAlloc(allocator, '\n', 512) catch break;
}
defer allocator.free(line);
if (line.len == 0) {
break;
}
var it = std.mem.split(u8, line, ",");
var x = try std.fmt.parseInt(u32, it.next().?, 10);
var y = try std.fmt.parseInt(u32, it.next().?, 10);
try points.append(Point{
.x = x,
.y = y,
});
}
while (true) {
var line: []u8 = undefined;
if (builtin.os.tag == .windows and !builtin.is_test) {
// NOTE: Read another byte on windows due to two-byte eol.
// NOTE: Check if in testing since tests only add single-byte eol in multiline strings.
line = reader.readUntilDelimiterAlloc(allocator, '\r', 512) catch break;
_ = try reader.readByte();
} else {
line = reader.readUntilDelimiterAlloc(allocator, '\n', 512) catch break;
}
defer allocator.free(line);
var it = std.mem.split(u8, line, "=");
var p1 = it.next().?;
var dir = p1[p1.len - 1];
var value = try std.fmt.parseInt(u32, it.next().?, 10);
var fold = Point{
.x = 0,
.y = 0,
};
if (dir == 'x') {
fold.x = value;
} else {
fold.y = value;
}
try folds.append(fold);
}
return Input{
.points = points.items,
.folds = folds.items,
};
}
fn drawPoints(points: []Point) void {
var result: u32 = 0;
var min = Point{
.x = 99999,
.y = 99999,
};
var max = Point{
.x = 0,
.y = 0,
};
for (points) |point| {
if (point.x < min.x) {
min.x = point.x;
}
if (point.y < min.y) {
min.y = point.y;
}
if (point.x > max.x) {
max.x = point.x;
}
if (point.y > max.y) {
max.y = point.y;
}
}
var line: [2048]u8 = undefined;
std.log.info("\npoints", .{});
var y: u32 = min.y;
while (y <= max.y) : (y += 1) {
var x: u32 = min.x;
while (x <= max.x) : (x += 1) {
var found = false;
for (points) |point| {
if (point.x == x and point.y == y) {
found = true;
result += 1;
break;
}
}
line[x] = if (found) '#' else '.';
}
std.log.info("{s}", .{line[0..x]});
}
}
pub fn countPoints(points: []Point) u32 {
var result: u32 = 0;
var min = Point{
.x = 99999,
.y = 99999,
};
var max = Point{
.x = 0,
.y = 0,
};
for (points) |point| {
if (point.x < min.x) {
min.x = point.x;
}
if (point.y < min.y) {
min.y = point.y;
}
if (point.x > max.x) {
max.x = point.x;
}
if (point.y > max.y) {
max.y = point.y;
}
}
var y: u32 = min.y;
while (y <= max.y) : (y += 1) {
var x: u32 = min.x;
while (x <= max.x) : (x += 1) {
for (points) |point| {
if (point.x == x and point.y == y) {
result += 1;
break;
}
}
}
}
return result;
}
pub fn foldPoints(fold: Point, points: []Point) void {
for (points) |point, i| {
if (fold.x > fold.y) {
if (point.x > fold.x) {
points[i].x = fold.x - (point.x - fold.x);
}
} else {
if (point.y > fold.y) {
points[i].y = fold.y - (point.y - fold.y);
}
}
}
}
pub fn task1(allocator: std.mem.Allocator, input: Input) !u32 {
var result: u32 = 0;
var points = try allocator.dupe(Point, input.points);
defer allocator.free(points);
var folds = try allocator.dupe(Point, input.folds);
defer allocator.free(folds);
foldPoints(folds[0], points);
// drawPoints(points);
result = countPoints(points);
return result;
}
pub fn task2(allocator: std.mem.Allocator, input: Input) !u32 {
var result: u32 = 0;
var points = try allocator.dupe(Point, input.points);
defer allocator.free(points);
var folds = try allocator.dupe(Point, input.folds);
defer allocator.free(folds);
for (folds) |fold| {
foldPoints(fold, points);
}
drawPoints(points);
return result;
}
pub fn main() !void {
var buffer: [2000000]u8 = undefined;
var fixed_buffer = std.heap.FixedBufferAllocator.init(&buffer);
const allocator = fixed_buffer.allocator();
const file = try std.fs.cwd().openFile("input.txt", .{ .read = true });
defer file.close();
const input = try readInput(allocator, file.reader());
const task_1_result = try task1(allocator, input);
std.log.info("Task 1 result: {}", .{task_1_result});
const task_2_result = try task2(allocator, input);
std.log.info("Task 2 result: {}", .{task_2_result});
}