136 lines
3.9 KiB
Zig
136 lines
3.9 KiB
Zig
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;
|
|
}
|