Solution to 16-2
This commit is contained in:
parent
6e9d08011f
commit
4202f34395
232
solutions/16.zig
232
solutions/16.zig
@ -4,7 +4,7 @@ const util = @import("util.zig");
|
||||
const expect = std.testing.expect;
|
||||
|
||||
pub fn main() !void {
|
||||
const response = try part_one(false);
|
||||
const response = try part_two(false);
|
||||
print("{}\n", .{response});
|
||||
}
|
||||
|
||||
@ -42,36 +42,10 @@ fn part_one(is_test_case: bool) !u32 {
|
||||
defer _ = gpa.deinit();
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
const input_file = try util.getInputFile("16", is_test_case);
|
||||
const data = try util.readAllInputWithAllocator(input_file, allocator);
|
||||
defer allocator.free(data);
|
||||
|
||||
var map_list = std.ArrayList([]u8).init(allocator);
|
||||
|
||||
var it = std.mem.splitScalar(u8, data, '\n');
|
||||
var start_position: Position = undefined;
|
||||
var target_point: Point = undefined;
|
||||
var line_counter: usize = 0;
|
||||
|
||||
while (it.next()) |line| : (line_counter += 1) {
|
||||
var line_list = std.ArrayList(u8).init(allocator);
|
||||
for (line) |c| {
|
||||
try line_list.append(c);
|
||||
}
|
||||
try map_list.append(try line_list.toOwnedSlice());
|
||||
|
||||
const index_of_s = std.mem.indexOf(u8, line, "S");
|
||||
if (index_of_s != null) {
|
||||
start_position = Position{ .point = Point{ .x = index_of_s.?, .y = line_counter }, .heading = Heading.east };
|
||||
}
|
||||
|
||||
const index_of_e = std.mem.indexOf(u8, line, "E");
|
||||
if (index_of_e != null) {
|
||||
target_point = Point{ .x = index_of_e.?, .y = line_counter };
|
||||
}
|
||||
}
|
||||
|
||||
const map = try map_list.toOwnedSlice();
|
||||
const built = try build_map_and_start_and_end(is_test_case, allocator);
|
||||
const map = built.map;
|
||||
const start_position = built.start_position;
|
||||
const target_point = built.target_point;
|
||||
defer allocator.free(map);
|
||||
defer {
|
||||
for (map) |line| {
|
||||
@ -159,6 +133,196 @@ fn part_one(is_test_case: bool) !u32 {
|
||||
}
|
||||
}
|
||||
|
||||
// Existence of such a value in the map indicates that _all_ of the Positions listed in .predecessors can reach this
|
||||
// Position with a total path-length of length
|
||||
const PredecessorsAndLength = struct {
|
||||
predecessors: std.ArrayList(Position),
|
||||
length: u32,
|
||||
|
||||
pub fn create(predecessor: Position, length: u32, allocator: std.mem.Allocator) !PredecessorsAndLength {
|
||||
var list = std.ArrayList(Position).init(allocator);
|
||||
try list.append(predecessor);
|
||||
return PredecessorsAndLength{ .predecessors = list, .length = length };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *PredecessorsAndLength) void {
|
||||
self.predecessors.deinit();
|
||||
}
|
||||
};
|
||||
|
||||
fn part_two(is_test_case: bool) !u32 {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
const built = try build_map_and_start_and_end(is_test_case, allocator);
|
||||
const map = built.map;
|
||||
const start_position = built.start_position;
|
||||
const target_point = built.target_point;
|
||||
defer allocator.free(map);
|
||||
defer {
|
||||
for (map) |line| {
|
||||
allocator.free(line);
|
||||
}
|
||||
}
|
||||
|
||||
var distances = std.AutoHashMap(Position, PredecessorsAndLength).init(allocator);
|
||||
defer distances.deinit();
|
||||
defer {
|
||||
var preds_it = distances.valueIterator();
|
||||
while (preds_it.next()) |val| {
|
||||
val.deinit();
|
||||
}
|
||||
}
|
||||
var visited_positions = std.AutoHashMap(Position, bool).init(allocator);
|
||||
defer visited_positions.deinit();
|
||||
|
||||
try distances.put(start_position, PredecessorsAndLength{ .predecessors = std.ArrayList(Position).init(allocator), .length = 0 });
|
||||
var current_position = start_position;
|
||||
var current_distance: u32 = 0;
|
||||
var found_lowest_distance_to_target: ?u32 = null;
|
||||
while (true) {
|
||||
if (std.meta.eql(current_position.point, target_point) and found_lowest_distance_to_target == null) {
|
||||
found_lowest_distance_to_target = current_distance;
|
||||
}
|
||||
const moves = try find_valid_moves(map, current_position, allocator);
|
||||
|
||||
for (moves) |move| {
|
||||
if (visited_positions.contains(move.position)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const distance_from_here = current_distance + move.cost;
|
||||
if (distances.contains(move.position)) {
|
||||
const current_lowest_distance = distances.get(move.position).?.length;
|
||||
if (distance_from_here < current_lowest_distance) {
|
||||
// print("DEBUG - found a new lowest distance for {}/{} ({}) - moving from {} to {}\n", .{ move.position.point.x, move.position.point.y, move.position.heading, current_lowest_distance, distance_from_here });
|
||||
var stale_value = distances.get(move.position).?;
|
||||
stale_value.deinit();
|
||||
try distances.put(move.position, try PredecessorsAndLength.create(current_position, distance_from_here, allocator));
|
||||
}
|
||||
if (distance_from_here == current_lowest_distance) {
|
||||
try distances.getPtr(move.position).?.predecessors.append(current_position);
|
||||
}
|
||||
} else {
|
||||
// print("DEBUG - found fresh lowest distance for {}/{} ({}) - {}\n", .{ move.position.point.x, move.position.point.y, move.position.heading, distance_from_here });
|
||||
try distances.put(move.position, try PredecessorsAndLength.create(current_position, distance_from_here, allocator));
|
||||
}
|
||||
}
|
||||
allocator.free(moves);
|
||||
|
||||
try visited_positions.put(current_position, true);
|
||||
|
||||
// Find the next candidate by iterating over all unvisited nodes with non-infinite distance, and picking the one
|
||||
// with lowest distance.
|
||||
// There would almost-certainly be a way to optimize this with a min-queue if we cared.
|
||||
var next_position: Position = undefined;
|
||||
// var lowest_distance_found: u32 = std.math.inf(u32);
|
||||
// above gives `error: reached unreachable code`
|
||||
var lowest_distance_found: u32 = 999999999;
|
||||
var dist_it = distances.iterator();
|
||||
while (dist_it.next()) |entry| {
|
||||
// print("DEBUG - checking whether {}/{}({}) is valid as next current_position - ", .{ entry.key_ptr.point.x, entry.key_ptr.point.y, entry.key_ptr.heading });
|
||||
if (visited_positions.contains(entry.key_ptr.*)) {
|
||||
// print("no, because it's been visited already\n", .{});
|
||||
continue;
|
||||
}
|
||||
if (entry.value_ptr.*.length > lowest_distance_found) {
|
||||
// print("no, because its distance ({}) is higher than the lowest found so far ({})\n", .{ entry.value_ptr.*, lowest_distance_found });
|
||||
continue;
|
||||
}
|
||||
// print("it is!\n", .{});
|
||||
// print("{}/{}({}) is a valid next-position\n", .{ entry.key_ptr.point.x, entry.key_ptr.point.y, entry.key_ptr.heading });
|
||||
next_position = entry.key_ptr.*;
|
||||
lowest_distance_found = entry.value_ptr.*.length;
|
||||
}
|
||||
current_position = next_position;
|
||||
current_distance = lowest_distance_found;
|
||||
// If we are dealing with distances larger than _a_ found-distance-to-target, then (because all edge-lengths are
|
||||
// positive) no further paths to be found can be shorter - therefore we've found all possible shortest paths.
|
||||
if (found_lowest_distance_to_target != null and current_distance > found_lowest_distance_to_target.?) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
print("DEBUG - finished finding all paths to target_point\n", .{});
|
||||
|
||||
// Iterate back over the predecessors of paths that end at the target_point - all of those are on shortest-paths
|
||||
var points_on_shortest_paths = std.AutoHashMap(Point, bool).init(allocator);
|
||||
defer points_on_shortest_paths.deinit();
|
||||
var positions_to_be_processed = std.AutoHashMap(Position, bool).init(allocator);
|
||||
defer positions_to_be_processed.deinit();
|
||||
|
||||
for (0..4) |i| {
|
||||
const target = Position{ .point = target_point, .heading = @enumFromInt(i) };
|
||||
if (distances.contains(target)) {
|
||||
try positions_to_be_processed.put(target, true);
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
var position_it = positions_to_be_processed.keyIterator();
|
||||
if (position_it.next()) |pos| {
|
||||
const actual_pos = pos.*;
|
||||
// print("DEBUG - found a position to be processed - it is {}/{}\n", .{ pos.point.x, pos.point.y });
|
||||
if (distances.contains(actual_pos)) {
|
||||
try points_on_shortest_paths.put(actual_pos.point, true);
|
||||
// print("DEBUG - it's on a shortest path, has been added\n", .{});
|
||||
for (distances.get(actual_pos).?.predecessors.items) |pred| {
|
||||
// print("DEBUG - adding {}/{} to the positions_to_be_processed\n", .{ pred.point.x, pred.point.y });
|
||||
try positions_to_be_processed.put(pred, true);
|
||||
}
|
||||
}
|
||||
_ = positions_to_be_processed.remove(actual_pos);
|
||||
} else {
|
||||
// `positions_to_be_processed` is empty - stop looping
|
||||
// print("DEBUG - there are no more positions_to_be_processed - stopping processing\n", .{});
|
||||
break;
|
||||
}
|
||||
}
|
||||
print("DEBUG - points_on_shortest_paths are: ", .{});
|
||||
var count: u32 = 0;
|
||||
var key_it = points_on_shortest_paths.keyIterator();
|
||||
while (key_it.next()) |point| {
|
||||
count += 1;
|
||||
print("{}/{}, ", .{ point.x, point.y });
|
||||
}
|
||||
print("\n", .{});
|
||||
return count;
|
||||
}
|
||||
|
||||
fn build_map_and_start_and_end(is_test_case: bool, allocator: std.mem.Allocator) !struct { map: [][]u8, start_position: Position, target_point: Point } {
|
||||
const input_file = try util.getInputFile("16", is_test_case);
|
||||
const data = try util.readAllInputWithAllocator(input_file, allocator);
|
||||
defer allocator.free(data);
|
||||
|
||||
var map_list = std.ArrayList([]u8).init(allocator);
|
||||
|
||||
var it = std.mem.splitScalar(u8, data, '\n');
|
||||
var start_position: Position = undefined;
|
||||
var target_point: Point = undefined;
|
||||
var line_counter: usize = 0;
|
||||
|
||||
while (it.next()) |line| : (line_counter += 1) {
|
||||
var line_list = std.ArrayList(u8).init(allocator);
|
||||
for (line) |c| {
|
||||
try line_list.append(c);
|
||||
}
|
||||
try map_list.append(try line_list.toOwnedSlice());
|
||||
|
||||
const index_of_s = std.mem.indexOf(u8, line, "S");
|
||||
if (index_of_s != null) {
|
||||
start_position = Position{ .point = Point{ .x = index_of_s.?, .y = line_counter }, .heading = Heading.east };
|
||||
}
|
||||
|
||||
const index_of_e = std.mem.indexOf(u8, line, "E");
|
||||
if (index_of_e != null) {
|
||||
target_point = Point{ .x = index_of_e.?, .y = line_counter };
|
||||
}
|
||||
}
|
||||
|
||||
return .{ .map = try map_list.toOwnedSlice(), .start_position = start_position, .target_point = target_point };
|
||||
}
|
||||
|
||||
const Move = struct { position: Position, cost: u32 };
|
||||
|
||||
fn find_valid_moves(map: [][]u8, current_position: Position, allocator: std.mem.Allocator) ![]Move {
|
||||
@ -208,3 +372,9 @@ test "part_one" {
|
||||
print("DEBUG - part_one_response is {}\n", .{part_one_response});
|
||||
try expect(part_one_response == 7036);
|
||||
}
|
||||
|
||||
test "part_two" {
|
||||
const part_two_response = try part_two(true);
|
||||
print("DEBUG - part_two_response is {}\n", .{part_two_response});
|
||||
try expect(part_two_response == 45);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user