const std = @import("std"); const max = 512; const xOrigin = max / 2; const yOrigin = max / 2; const Error = error{ParseError}; const State = enum(u8) { Empty, Origin, Line, Intersect, }; const Direction = enum(u8) { Up = 'U', Right = 'R', Down = 'D', Left = 'L', pub fn parse(in: u8) anyerror!Direction { return switch (in) { 'U' => .Up, 'R' => .Right, 'D' => .Down, 'L' => .Left, else => Error.ParseError, }; } }; const Grid = [max][max]State; const Step = struct { d: Direction, l: u32, pub fn parse(in: []const u8) anyerror!Step { if (in.len < 2) { return error.ParseError; } return Step{ .d = try Direction.parse(in[0]), .l = try std.fmt.parseInt(u32, in[1..in.len], 10), }; } }; const World = struct { x: u32, y: u32, grid: *Grid, pub fn init(self: *World) void { for (self.grid) |y, y_offset| { for (y) |x, x_offset| { self.grid[y_offset][x_offset] = State.Empty; } } } pub fn display(self: *World) void { for (self.grid) |y, y_offset| { for (y) |x, x_offset| { var chr = switch (x) { State.Empty => " ", State.Line => ".", State.Origin => "o", State.Intersect => "*", }; std.debug.warn("{}", chr); } std.debug.warn("\n"); } } pub fn step(self: *World, in: Step, compare: *Grid) void { std.debug.warn("Step: {}\n", in); switch (in.d) { .Up => { self.lineY(self.y - in.l, self.y - 1, compare); self.y -= in.l; }, .Down => { self.lineY(self.y + 1, self.y + in.l, compare); self.y += in.l; }, .Left => { self.lineX(self.x - in.l, self.x - 1, compare); self.x -= in.l; }, .Right => { self.lineX(self.x + 1, self.x + in.l, compare); self.x += in.l; }, } } fn lineX(self: *World, start: u32, end: u32, compare: *Grid) void { var x: u32 = start; while (x <= end) : (x += 1) { self.fill(x, self.y, compare); } } fn lineY(self: *World, start: u32, end: u32, compare: *Grid) void { var y: u32 = start; while (y <= end) : (y += 1) { self.fill(self.x, y, compare); } } fn fill(self: *World, x: u32, y: u32, compare: *Grid) void { std.debug.warn("\tFilling {},{}\n", x, y); var newVal: State = switch (compare[y][x]) { .Line => .Intersect, else => .Line, }; self.grid[y][x] = newVal; } }; fn getLine(stream: *std.fs.File.InStream.Stream) anyerror![]Step { var buf: [10240]u8 = undefined; var out: [1000]Step = undefined; var i: u32 = 0; var line = (try stream.readUntilDelimiterOrEof(&buf, '\n')) orelse return Error.ParseError; var iter = std.mem.separate(line, ","); while (iter.next()) |value| { std.debug.assert(i < out.len); out[i] = try Step.parse(value); i += 1; } return out[0..i]; } fn manhattan(x: i32, y: i32) u32 { return std.math.absCast(x - xOrigin) + std.math.absCast(y - yOrigin); } pub fn main() anyerror!void { var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); const alloc = &arena.allocator; const file = std.io.getStdIn(); const stream = &file.inStream().stream; var grid = try alloc.create(Grid); var world: World = World{ .x = xOrigin, .y = yOrigin, .grid = grid, }; world.init(); var compare = std.mem.dupe(alloc, Grid, grid); // Line 1 for (try getLine(stream)) |item| { world.step(item, compare); } // reset to origin //std.mem.copy(compare, world.grid); world.x = xOrigin; world.y = yOrigin; // Line 2 for (try getLine(stream)) |item| { world.step(item, compare); } // Ignore the origin world.grid[yOrigin][xOrigin] = State.Origin; // detect collisions var minMH: u32 = undefined; var found: bool = false; for (world.grid) |y, yOff| { for (y) |x, xOff| { if (x == State.Intersect) { const mh = manhattan(@intCast(i32, xOff), @intCast(i32, yOff)); std.debug.warn("Intersect! {},{} : {}\n", xOff, yOff, mh); if (found) { if (minMH > mh) { minMH = mh; } } else { found = true; minMH = mh; } } } } if (found) { std.debug.warn("Smallest manhattan: {}\n", minMH); } // world.display(); }