const std = @import("std");
const ArrayList = std.ArrayList;
const Position = struct {
x: i64 = 0,
y: i64 = 0,
};
pub fn solve_part1() !void {
var inputs = try std.fs.cwd().openFile("inputs/day10.txt", .{});
var buffered_reader = std.io.bufferedReader(inputs.reader());
var in_stream = buffered_reader.reader();
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
var allocator = arena.allocator();
var lines = ArrayList([]u8).init(allocator);
while (try in_stream.readUntilDelimiterOrEofAlloc(allocator, '\n', 100 * 1024)) |line| {
try lines.append(line);
}
std.mem.reverse([]u8, lines.items);
var timer = try std.time.Timer.start();
var start_pos = blk: for (lines.items, 0..) |line, y| {
if (std.mem.indexOfScalar(u8, line, 'S')) |x| {
break :blk Position{ .x = @intCast(x), .y = @intCast(y) };
}
} else {
std.log.info("Didn't find any 'S'", .{});
std.os.exit(1);
break :blk Position{ .x = 0, .y = 0 };
};
var steps: u32 = 0;
var dir_1: Position = .{};
var pos_1: Position = start_pos;
var dir_2: Position = .{};
var pos_2: Position = start_pos;
var found_connections: i32 = 0;
var dirs = [_]Position{
.{ .x = 0, .y = 1 },
.{ .x = 0, .y = -1 },
.{ .x = 1, .y = 0 },
.{ .x = -1, .y = 0 },
};
for (dirs) |dir| {
if ((start_pos.y + dir.y >= 0 and start_pos.y + dir.y < lines.items.len) and (start_pos.x + dir.x >= 0 and start_pos.x + dir.x < lines.items[0].len) and is_connected(dir, lines.items[@as(usize, @intCast(start_pos.y + dir.y))][@as(usize, @intCast(start_pos.x + dir.x))])) {
if (found_connections == 0) {
dir_1 = dir;
} else {
dir_2 = dir;
}
found_connections += 1;
}
}
while ((pos_1.x == start_pos.x and pos_1.y == start_pos.y) or (pos_1.x != pos_2.x or pos_1.y != pos_2.y)) : (steps += 1) {
var c = &lines.items[@as(usize, @intCast(pos_1.y))][@as(usize, @intCast(pos_1.x))];
if (c.* == 'L' or c.* == 'J' or c.* == '|') {
c.* = 'x';
} else {
c.* = ',';
}
c = &lines.items[@as(usize, @intCast(pos_2.y))][@as(usize, @intCast(pos_2.x))];
if (c.* == 'L' or c.* == 'J' or c.* == '|') {
c.* = 'x';
} else {
c.* = ',';
}
pos_1.x += dir_1.x;
pos_1.y += dir_1.y;
pos_2.x += dir_2.x;
pos_2.y += dir_2.y;
if (next_dir(dir_1, lines.items[@as(usize, @intCast(pos_1.y))][@as(usize, @intCast(pos_1.x))])) |n_dir| {
dir_1 = n_dir;
} else {
std.log.info("Failed to get next dir on {d}, {d}", .{ pos_1.x, pos_1.y });
std.os.exit(1);
}
if (next_dir(dir_2, lines.items[@as(usize, @intCast(pos_2.y))][@as(usize, @intCast(pos_2.x))])) |n_dir| {
dir_2 = n_dir;
} else {
std.log.info("Failed to get next dir on {d}, {d}", .{ pos_2.x, pos_2.y });
std.os.exit(1);
}
}
var part1_timestamp = timer.read();
// Update the farthest character to x or ,.
var c1 = &lines.items[@as(usize, @intCast(pos_1.y))][@as(usize, @intCast(pos_1.x))];
if (c1.* == 'L' or c1.* == 'J' or c1.* == '|') {
c1.* = 'x';
} else {
c1.* = ',';
}
var inside: u32 = 0;
var outside: u32 = 0;
for (lines.items) |line| {
for (0..line.len) |x| {
if (line[x] == ',' or line[x] == 'x') {
continue;
}
var borders: u32 = 0;
for (x..line.len) |c| {
if (line[c] == 'x') {
borders += 1;
}
}
if (borders % 2 == 1) {
line[x] = 'I';
inside += 1;
} else {
line[x] = 'O';
outside += 1;
}
}
}
var part2_timestamp = timer.read();
std.mem.reverse([]u8, lines.items);
for (lines.items) |line| {
std.log.info("{s}", .{line});
}
std.log.info("Part 1 result {d} microseconds: {d}", .{ part1_timestamp, steps });
std.log.info("Part 2 result {d} microseconds: Inside: {d}, Outside: {d}", .{ part2_timestamp, inside, outside });
}
fn is_connected(dir: Position, c: u8) bool {
if (dir.y == 1) {
return c == '|' or c == 'F' or c == '7';
} else if (dir.y == -1) {
return c == '|' or c == 'J' or c == 'L';
} else if (dir.x == 1) {
return c == '-' or c == 'J' or c == '7';
} else if (dir.x == -1) {
return c == '-' or c == 'F' or c == 'L';
}
return false;
}
fn next_dir(dir: Position, c: u8) ?Position {
if (dir.x == 1) {
switch (c) {
'J' => return .{ .x = 0, .y = 1 },
'7' => return .{ .x = 0, .y = -1 },
'-' => return .{ .x = 1, .y = 0 },
else => return null,
}
} else if (dir.x == -1) {
switch (c) {
'L' => return .{ .x = 0, .y = 1 },
'F' => return .{ .x = 0, .y = -1 },
'-' => return .{ .x = -1, .y = 0 },
else => return null,
}
} else if (dir.y == 1) {
switch (c) {
'F' => return .{ .x = 1, .y = 0 },
'7' => return .{ .x = -1, .y = 0 },
'|' => return .{ .x = 0, .y = 1 },
else => return null,
}
} else if (dir.y == -1) {
switch (c) {
'L' => return .{ .x = 1, .y = 0 },
'J' => return .{ .x = -1, .y = 0 },
'|' => return .{ .x = 0, .y = -1 },
else => return null,
}
}
return null;
}