You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
288 lines
8.4 KiB
288 lines
8.4 KiB
const std = @import("std");
|
|
|
|
fn StackList(comptime T: type, comptime capacity_type: type, comptime capacity: capacity_type) type {
|
|
return struct {
|
|
const Self = @This();
|
|
mem: [capacity]T,
|
|
length: capacity_type,
|
|
|
|
fn add(self: *Self, value: T) void {
|
|
self.mem[self.length] = value;
|
|
self.length += 1;
|
|
}
|
|
|
|
fn addIfNotNull(self: *Self, nullable_value: ?T) void {
|
|
if (nullable_value) |value| {
|
|
self.add(value);
|
|
}
|
|
}
|
|
|
|
fn has(self: *Self, needle: T) bool {
|
|
for (0..self.length) |i| {
|
|
if (self.mem[i] == needle) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
fn getMutableSlice(self: *Self) []T {
|
|
return (&self.mem)[0..self.length];
|
|
}
|
|
|
|
fn getSlice(self: *const Self) []const T {
|
|
return self.mem[0..self.length];
|
|
}
|
|
|
|
fn init() Self {
|
|
return Self{
|
|
.mem = undefined,
|
|
.length = 0,
|
|
};
|
|
}
|
|
};
|
|
}
|
|
|
|
fn RingQueueWrapper(comptime T: type) type {
|
|
return struct {
|
|
const Self = @This();
|
|
mem: *[]T,
|
|
first: usize,
|
|
next: usize,
|
|
|
|
fn add(self: *Self, value: T) void {
|
|
std.debug.assert(self.next - self.first < self.mem.*.len);
|
|
self.mem.*[self.next % self.mem.*.len] = value;
|
|
self.next += 1;
|
|
}
|
|
|
|
fn take(self: *Self) T {
|
|
const result = self.mem.*[self.first % self.mem.*.len];
|
|
self.first += 1;
|
|
return result;
|
|
}
|
|
|
|
fn isEmpty(self: *const Self) bool {
|
|
return self.next == self.first;
|
|
}
|
|
|
|
fn init(mem: *[]T) Self {
|
|
return Self{
|
|
.mem = mem,
|
|
.first = 0,
|
|
.next = 0,
|
|
};
|
|
}
|
|
};
|
|
}
|
|
|
|
const Direction = enum(u8) {
|
|
Horizontal,
|
|
Vertical,
|
|
};
|
|
|
|
const ResultsByDirection = std.EnumArray(Direction, u32);
|
|
const Results = [150][150]ResultsByDirection;
|
|
|
|
const Task = packed struct(u48) {
|
|
current_heat: u16,
|
|
x: u8,
|
|
y: u8,
|
|
entry_direction: Direction,
|
|
_: u8 = 0,
|
|
};
|
|
|
|
const NextTasks = StackList(Task, usize, 14);
|
|
|
|
fn getNextTasks(board: []const []const u8, task: Task) NextTasks {
|
|
var result = NextTasks.init();
|
|
|
|
const previous_direction: Direction = switch (task.entry_direction) {
|
|
.Vertical => .Horizontal,
|
|
.Horizontal => .Vertical,
|
|
};
|
|
_ = previous_direction;
|
|
|
|
switch (task.entry_direction) {
|
|
.Vertical => {
|
|
{
|
|
var current_heat = task.current_heat;
|
|
inline for (1..11) |delta| {
|
|
const new_x = task.x + @as(u8, delta);
|
|
if (new_x >= board.len) {
|
|
break;
|
|
}
|
|
current_heat += board[new_x][task.y];
|
|
|
|
if (delta >= 4) {
|
|
result.add(.{
|
|
.current_heat = current_heat,
|
|
.x = new_x,
|
|
.y = task.y,
|
|
.entry_direction = .Horizontal,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
var current_heat = task.current_heat;
|
|
inline for (1..11) |delta| {
|
|
if (delta > task.x) {
|
|
break;
|
|
}
|
|
const new_x = task.x - @as(u8, delta);
|
|
current_heat += board[new_x][task.y];
|
|
|
|
if (delta >= 4) {
|
|
result.add(.{
|
|
.current_heat = current_heat,
|
|
.x = new_x,
|
|
.y = task.y,
|
|
.entry_direction = .Horizontal,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
},
|
|
.Horizontal => {
|
|
{
|
|
var current_heat = task.current_heat;
|
|
inline for (1..11) |delta| {
|
|
const new_y = task.y + @as(u8, delta);
|
|
if (new_y >= board[task.x].len) {
|
|
break;
|
|
}
|
|
current_heat += board[task.x][new_y];
|
|
|
|
if (delta >= 4) {
|
|
result.add(.{
|
|
.current_heat = current_heat,
|
|
.x = task.x,
|
|
.y = new_y,
|
|
.entry_direction = .Vertical,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
var current_heat = task.current_heat;
|
|
inline for (1..11) |delta| {
|
|
if (delta > task.y) {
|
|
break;
|
|
}
|
|
const new_y = task.y - @as(u8, delta);
|
|
current_heat += board[task.x][new_y];
|
|
|
|
if (delta >= 4) {
|
|
result.add(.{
|
|
.current_heat = current_heat,
|
|
.x = task.x,
|
|
.y = new_y,
|
|
.entry_direction = .Vertical,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
},
|
|
}
|
|
|
|
//std.debug.print("Next tasks for {any}: {any}\n", .{ task, result.getSlice() });
|
|
|
|
return result;
|
|
}
|
|
|
|
fn solveLines(lines: [][]u8) !usize {
|
|
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
|
defer arena.deinit();
|
|
const allocator = arena.allocator();
|
|
|
|
for (lines) |line| {
|
|
for (line) |*char| {
|
|
char.* = char.* - '0';
|
|
}
|
|
}
|
|
|
|
const x_max = @as(u8, @intCast(lines.len - 1));
|
|
const y_max = @as(u8, @intCast(lines[x_max].len - 1));
|
|
const max_value: u16 = @as(u16, 18) * @max(x_max, y_max) + lines[x_max][y_max];
|
|
|
|
var results: Results = [_][150]ResultsByDirection{[_]ResultsByDirection{ResultsByDirection.initFill(max_value)} ** 150} ** 150;
|
|
var queue_mem = try allocator.alloc(Task, 16 * 1024 * 1024);
|
|
var tasks = RingQueueWrapper(Task).init(&queue_mem);
|
|
tasks.add(.{
|
|
.current_heat = lines[x_max][y_max],
|
|
.x = x_max,
|
|
.y = y_max,
|
|
.entry_direction = .Horizontal,
|
|
});
|
|
tasks.add(.{
|
|
.current_heat = lines[x_max][y_max],
|
|
.x = x_max,
|
|
.y = y_max,
|
|
.entry_direction = .Vertical,
|
|
});
|
|
|
|
var i: usize = 0;
|
|
while (!tasks.isEmpty()) : (i += 1) {
|
|
//std.debug.print("Handling task {d}\n", .{i});
|
|
var current_task = tasks.take();
|
|
if (results[current_task.x][current_task.y].get(current_task.entry_direction) > current_task.current_heat) {
|
|
results[current_task.x][current_task.y].set(current_task.entry_direction, current_task.current_heat);
|
|
for (getNextTasks(lines, current_task).getSlice()) |next_task| {
|
|
tasks.add(next_task);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (false) {
|
|
std.debug.print("Total tasks done: {d}\n", .{i});
|
|
for (0..lines.len) |x| {
|
|
for (0..lines[x].len) |y| {
|
|
std.debug.print("{d}/{d} ", .{ results[x][y].get(.Horizontal), results[x][y].get(.Vertical) });
|
|
}
|
|
std.debug.print("\n", .{});
|
|
}
|
|
}
|
|
|
|
return @min(results[0][0].get(.Vertical), results[0][0].get(.Horizontal)) - lines[0][0];
|
|
}
|
|
|
|
pub fn solveAll(reader: anytype) !usize {
|
|
var result: usize = 0;
|
|
while (true) {
|
|
var allocator_buffer: [20000]u8 = undefined;
|
|
var fba = std.heap.FixedBufferAllocator.init(&allocator_buffer);
|
|
var allocator = fba.allocator();
|
|
|
|
var lines = StackList([]u8, usize, 150).init();
|
|
var empty_line_reached = false;
|
|
|
|
var line_buffer: [1000]u8 = undefined;
|
|
while (try reader.readUntilDelimiterOrEof(&line_buffer, '\n')) |line| {
|
|
if (line.len == 0) {
|
|
empty_line_reached = true;
|
|
break;
|
|
}
|
|
lines.add(try allocator.dupe(u8, line));
|
|
}
|
|
|
|
result += try solveLines(lines.getMutableSlice());
|
|
|
|
if (!empty_line_reached) {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn main() !void {
|
|
const stdout = std.io.getStdOut().writer();
|
|
|
|
const raw_in = std.io.getStdIn();
|
|
var buffered_reader = std.io.bufferedReader(raw_in.reader());
|
|
var reader = buffered_reader.reader();
|
|
const result = try solveAll(&reader);
|
|
try stdout.print("{d}\n", .{result});
|
|
}
|
|
|