Solutions of most (39 out of 50 so far) puzzles in Zig (system language, alternative for C). My first experience with it.
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

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});
}