const Map = struct {
rows: usize,
cols: usize,
buffer: []u8,
pub fn init(allocator: Allocator, buffer: []const u8) !Map {
if (std.mem.indexOf(u8, buffer, "\n")) |line_end| {
const clean = std.mem.trimEnd(u8, buffer, "\n");
return .{
.buffer = try std.mem.replaceOwned(u8, allocator, clean, "\n", ""),
.cols = line_end,
.rows = (clean.len - 1) / line_end,
};
}
@panic("Failed to split input buffer!");
}
pub fn get(self: *const Map, x: usize, y: usize) u8 {
return self.buffer[(y * self.cols) + x];
}
pub fn set(self: *const Map, x: usize, y: usize, v: u8) void {
self.buffer[(y * self.cols) + x] = v;
}
pub fn format(self: Map, writer: *std.Io.Writer) std.Io.Writer.Error!void {
try writer.print("Map {d} x {d}\n", .{ self.rows, self.cols });
for (0..self.rows) |y| {
for (0..self.cols) |x| {
try writer.print("{c}", .{self.get(x, y)});
}
try writer.print("\n", .{});
}
}
pub fn deinit(self: Map, allocator: Allocator) void {
allocator.free(self.buffer);
}
pub fn animate(self: Map) void {
std.debug.print(t.hide_cursor, .{});
for (0..self.cols) |x| {
for (0..self.rows - 1) |y| {
std.debug.print(t.yx, .{ y, x });
const v = self.get(x, y);
switch (v) {
'|' => std.debug.print("{s}{c}{s}", .{ t.yellow, v, t.clear }),
'^' => std.debug.print("{s}{c}{s}", .{ t.red, v, t.clear }),
'.' => std.debug.print("{s}{c}{s}", .{ t.dark_gray, v, t.clear }),
else => std.debug.print("{s}{c}{s}", .{ t.blue, v, t.clear }),
}
}
}
}
};
fn part1(allocator: Allocator) anyerror!void {
const input = @embedFile("puzzle-07");
// std.debug.print("--- INPUT---\n{s}\n------------\n", .{input});
const map: Map = try .init(allocator, input);
var start: @Vector(2, usize) = @splat(0);
var splitters: std.array_list.Managed(@Vector(2, usize)) = .init(allocator);
// std.debug.print("{d} x {d}\n", .{ map.cols, map.rows });
for (0..map.rows) |y| {
for (0..map.cols) |x| {
switch (map.get(x, y)) {
'S' => start = .{ x, y },
'^' => try splitters.append(.{ x, y }),
else => continue,
}
}
}
var beams: std.array_list.Managed(@Vector(2, usize)) = .init(allocator);
try beams.append(start);
var splits: usize = 0;
while (beams.pop()) |b| {
// not necessary, doing this just for achieving a nicer order in the animation
std.mem.sort(@Vector(2, usize), beams.items, {}, comptime struct {
pub fn f(_: void, beam1: @Vector(2, usize), beam2: @Vector(2, usize)) bool {
return beam1[1] > beam2[1];
}
}.f);
map.animate();
const curr: @Vector(2, usize) = .{ b[0], b[1] };
if (curr[0] < 0 or
curr[0] > map.cols - 1 or
curr[1] > map.rows - 1)
{
continue;
}
switch (map.get(curr[0], curr[1])) {
'S' => try beams.append(.{ curr[0], curr[1] + 1 }),
'.' => try beams.append(.{ curr[0], curr[1] + 1 }),
'^' => {
splits += 1;
if (map.get(curr[0] - 1, curr[1]) != '|')
try beams.append(.{ curr[0] - 1, curr[1] });
if (map.get(curr[0] + 1, curr[1]) != '|')
try beams.append(.{ curr[0] + 1, curr[1] });
},
else => continue,
}
map.set(curr[0], curr[1], '|');
}
// std.debug.print("{f}\n", .{map});
std.debug.print("# Splitters: {d}\n", .{splitters.items.len});
std.debug.print("Result: {d}\n", .{splits});
}