const std = @import("std"); const Pos = @Vector(2, usize); const Dir = enum { right, up, left, down, fn rotate(self: @This()) @This() { return switch (self) { .right => .down, .up => .right, .left => .up, .down => .left, }; } }; const Input = struct { map: std.AutoHashMap(Pos, void), size: Pos, start_pos: Pos, start_dir: Dir, const Self = @This(); fn step_forwards(pos: Pos, dir: Dir) ?Pos { return switch (dir) { .right => .{ std.math.add(usize, pos[0], 1) catch return null, pos[1] }, .up => .{ pos[0], std.math.sub(usize, pos[1], 1) catch return null }, .left => .{ std.math.sub(usize, pos[0], 1) catch return null, pos[1] }, .down => .{ pos[0], std.math.add(usize, pos[1], 1) catch return null }, }; } fn step(self: Self, pos: Pos, dir: Dir) ?struct { Pos, Dir } { var new_pos = Self.step_forwards(pos, dir) orelse return null; var new_dir = dir; while (self.map.contains(new_pos)) { new_dir = new_dir.rotate(); new_pos = Self.step_forwards(pos, new_dir) orelse return null; } return .{ new_pos, new_dir }; } }; pub fn parse(allocator: std.mem.Allocator, data: []const u8) !Input { var lines = std.mem.split(u8, data, "\n"); var input = Input{ .map = std.AutoHashMap(Pos, void).init(allocator), .size = undefined, .start_pos = undefined, .start_dir = undefined }; var y: usize = 0; while (lines.next()) |line| : (y += 1) { if (line.len == 0) input.size[1] = y else input.size[0] = line.len; for (0.., line) |x, c| { if (c == '#') { try input.map.put(.{ x, y }, {}); } else if (c == '>') { input.start_pos = .{ x, y }; input.start_dir = .right; } else if (c == '^') { input.start_pos = .{ x, y }; input.start_dir = .up; } else if (c == '<') { input.start_pos = .{ x, y }; input.start_dir = .left; } else if (c == 'v') { input.start_pos = .{ x, y }; input.start_dir = .down; } } } return input; } fn printAndWait(input: Input, traversed: std.AutoHashMap(Pos, void)) !void { var underlying_buffer: [100000]u8 = undefined; var buffer = std.io.fixedBufferStream(&underlying_buffer); var writer = buffer.writer(); for (0..input.size[1]) |y| { for (0..input.size[0]) |x| { try writer.print("{c}", .{ if (traversed.contains(.{ x, y })) 'X' else if (input.map.contains(.{ x, y })) @as(u8, '#') else '.', }); } try writer.print("\n", .{}); } std.debug.print("{s}\n", .{underlying_buffer[0..try buffer.getPos()]}); try std.io.getStdIn().reader().skipUntilDelimiterOrEof('\n'); } const test_input = \\....#..... \\.........# \\.......... \\..#....... \\.......#.. \\.......... \\.#..^..... \\........#. \\#......... \\......#... \\ ; test "part1" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); const output = try part1(arena.allocator(), try parse(arena.allocator(), test_input)); std.debug.print("got {}\n", .{output}); std.debug.assert(output == 41); } pub fn part1(allocator: std.mem.Allocator, input: Input) !u32 { var pos = input.start_pos; var dir = input.start_dir; var traversed = std.AutoHashMap(Pos, void).init(allocator); return while (true) { try traversed.put(pos, {}); const new = input.step(pos, dir); if (new) |n| { pos = n[0]; dir = n[1]; // const try printAndWait(input, traversed); } else { break traversed.count(); } }; } pub fn part2(allocator: std.mem.Allocator, input: Input) !u32 { _ = allocator; _ = input; return 0; }