const std = @import("std"); pub fn main() anyerror!void { var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); const alloc = arena.allocator(); // Read our input var f = try std.fs.cwd().openFile("input", .{}); defer f.close(); var contents = try f.readToEndAlloc(alloc, std.math.maxInt(u32)); defer alloc.free(contents); var it = std.mem.tokenize(u8, contents, "\n"); var map = std.ArrayList(u8).init(alloc); defer map.deinit(); var width: u8 = 0; var height: u8 = 0; while (it.next()) |line| { height += 1; if (@intCast(u8, line.len) > width) { width = @intCast(u8, line.len); } for (line) |_, i| { try map.append(try std.fmt.parseInt(u8, line[i..i+1], 10)); } } var path = try find_path(alloc, map.items, width, height); defer path.deinit(); } const Point = struct { x: u8 = 0, y: u8 = 0, pub fn get_index(p: *Point, width: u8, height: u8) usize { _ = width; return @as(usize, height) * @as(usize, p.y) + @as(usize, p.x); } pub fn from_index(index: usize, width: u8, height: u8) Point { return Point { .x = @intCast(u8, index % width), .y = @intCast(u8, index / height), }; } }; fn find_path(alloc: std.mem.Allocator, map: []u8, width: u8, height: u8) !std.ArrayList(Point) { var path = std.ArrayList(Point).init(alloc); var open_set = std.AutoHashMap(Point, void).init(alloc); defer open_set.deinit(); // Our starting point try open_set.put(.{.x = width - 1, .y = height - 1}, undefined); var came_from = std.AutoHashMap(Point, void).init(alloc); defer came_from.deinit(); while (open_set.count() > 0) { // Find lowest score in openset var lowest: Point = undefined; var score: u8 = std.math.maxInt(u8); var oit = open_set.iterator(); while(oit.next()) |kv| { var p = kv.key_ptr.*; _ = kv.value_ptr.*; // We might have to modify this, since our open_set may // contain points that aren't directly adjacent? if (map[p.get_index(width, height)] < score) { lowest = p; score = map[p.get_index(width, height)]; } } std.log.debug("Went to point {}, {}: score {}", .{lowest.x, lowest.y, score}); try path.append(lowest); try came_from.put(lowest, undefined); if (lowest.x == 0 and lowest.y == 0) { // We have arrived return path; } _ = open_set.remove(lowest); // Let's clear our open_set, but this might be an error open_set.clearRetainingCapacity(); var adjacents = get_adjacent_points(lowest.get_index(width, height), width, height); for (adjacents) |adj| { if (adj) |a| { var p = Point.from_index(a, width, height); if (came_from.contains(p)) { continue; } try open_set.put(p, undefined); } } } // Failure to find path unreachable; } fn get_adjacent_points(i: usize, width: usize, height: usize) [4]?usize { var adjacents = [4] ?usize { null, null, null, null, }; var ai: usize = 0; var bottom = i < ((height - 1) * width); var top = i >= width; var left = (i % width) != 0; var right = (i % width) != (width - 1); if (top) { adjacents[ai] = i - width; ai += 1; } if (left) { adjacents[ai] = i - 1; ai += 1; } if (right) { adjacents[ai] = i + 1; ai += 1; } if (bottom) { adjacents[ai] = i + width; ai += 1; } return adjacents; }