114 lines
4.0 KiB
Zig
114 lines
4.0 KiB
Zig
const std = @import("std");
|
|
const print = std.debug.print;
|
|
const util = @import("util.zig");
|
|
const Point = util.Point;
|
|
const log = util.log;
|
|
const expect = std.testing.expect;
|
|
|
|
pub fn main() !void {
|
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
defer _ = gpa.deinit();
|
|
const allocator = gpa.allocator();
|
|
|
|
const response = try partOne(false, false, allocator);
|
|
print("{}\n", .{response});
|
|
}
|
|
|
|
// Sketch of intended logic:
|
|
// * Find shortest non-cheating time start-to-finish
|
|
// * Find fastest time (don't need to save path) from start _to_ each location
|
|
// * Find faster time (ditto) from end _to_ each location
|
|
// * Find all cheats
|
|
// * For each cheat:
|
|
// * Time saved is basic time - (time-from-start-to-start-of-cheat - time-from-end-of-cheat-to-end)
|
|
//
|
|
// Implementation is not yet complete! So far I've only implemented the first bullet, because building a generic
|
|
// implementation of Dijkstra's was an _ARSE_ - the rest can happen tomorrow!
|
|
fn partOne(is_test_case: bool, debug: bool, allocator: std.mem.Allocator) !u32 {
|
|
const input_file = try util.getInputFile("20", is_test_case);
|
|
const data = try util.readAllInputWithAllocator(input_file, allocator);
|
|
defer allocator.free(data);
|
|
|
|
const map = buildMap(data, allocator);
|
|
defer allocator.free(map);
|
|
defer {
|
|
for (map) |line| {
|
|
allocator.free(line);
|
|
}
|
|
}
|
|
|
|
// Technically slightly inefficient to do it this way, as we could have done it during `buildMap`, but I prefer my
|
|
// functions to do one-and-only-one thing.
|
|
const start_point = findPoint(map, 'S');
|
|
const end_point = findPoint(map, 'E');
|
|
log("Start point is {s} and end point is {s}\n", .{ start_point, end_point }, debug);
|
|
|
|
const neighboursFunc = &struct {
|
|
pub fn func(d: *const [][]u8, point: *Point, alloc: std.mem.Allocator) []Point {
|
|
var response = std.ArrayList(Point).init(alloc);
|
|
const ns = point.neighbours(d.*[0].len, d.len, alloc);
|
|
for (ns) |n| {
|
|
if (d.*[n.y][n.x] != '#') {
|
|
response.append(n) catch unreachable;
|
|
}
|
|
}
|
|
alloc.free(ns);
|
|
return response.toOwnedSlice() catch unreachable;
|
|
}
|
|
}.func;
|
|
|
|
const shortestPathLength = util.dijkstra([][]u8, Point, &map, neighboursFunc, start_point, end_point, debug, allocator) catch unreachable;
|
|
return shortestPathLength;
|
|
}
|
|
|
|
fn buildNeighboursFunction(map: *const [][]u8) *const fn (p: *Point, alloc: std.mem.Allocator) []Point {
|
|
return struct {
|
|
pub fn call(p: *Point, alloc: std.mem.Allocator) []Point {
|
|
var responseList = std.ArrayList(Point).init(alloc);
|
|
const neighbours = p.neighbours(map[0].len, map.len, alloc);
|
|
for (neighbours) |n| {
|
|
if (map[n.y][n.x] == '.') {
|
|
responseList.append(n) catch unreachable;
|
|
}
|
|
}
|
|
alloc.free(neighbours);
|
|
|
|
return responseList.toOwnedSlice();
|
|
}
|
|
}.call;
|
|
}
|
|
|
|
fn buildMap(data: []const u8, allocator: std.mem.Allocator) [][]u8 {
|
|
var map_list = std.ArrayList([]u8).init(allocator);
|
|
var data_iterator = std.mem.splitScalar(u8, data, '\n');
|
|
while (data_iterator.next()) |data_line| {
|
|
var line = std.ArrayList(u8).init(allocator);
|
|
for (data_line) |c| {
|
|
line.append(c) catch unreachable;
|
|
}
|
|
map_list.append(line.toOwnedSlice() catch unreachable) catch unreachable;
|
|
}
|
|
return map_list.toOwnedSlice() catch unreachable;
|
|
}
|
|
|
|
fn findPoint(data: [][]u8, char: u8) Point {
|
|
for (data, 0..) |line, y| {
|
|
for (line, 0..) |c, x| {
|
|
if (c == char) {
|
|
return Point{ .x = x, .y = y };
|
|
}
|
|
}
|
|
}
|
|
unreachable;
|
|
}
|
|
|
|
test "partOne" {
|
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
defer _ = gpa.deinit();
|
|
const allocator = gpa.allocator();
|
|
|
|
const response = try partOne(true, true, allocator);
|
|
print("{}\n", .{response});
|
|
try expect(response == 84);
|
|
}
|