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.

221 lines
6.9 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 has(self: *const Self, needle: T) bool {
for (0..self.length) |i| {
if (self.mem[i] == needle) {
return true;
}
}
return false;
}
fn getMutableSlice(self: *Self) []T {
//var mem_full_slice = &self.mem;
//var mem_slice: []T = mem_full_slice[0..self.length];
//return mem_slice;
return (&self.mem)[0..self.length];
}
fn getSlice(self: *const Self) []const T {
return self.mem[0..self.length];
}
fn getLoopedValue(self: *const Self, index: usize) T {
return self.mem[index % self.length];
}
fn reset(self: *Self) void {
self.length = 0;
}
fn init() Self {
return Self{
.mem = undefined,
.length = 0,
};
}
};
}
const MAX_DIRECTIONS = 270;
const SIXTEEN_BITS = 65535;
const FIVE_BITS = 31;
const END_LINE_MASK = 512 | 256 | 128;
const Direction = enum(u8) { Left, Right };
const Directions = StackList(Direction, usize, MAX_DIRECTIONS);
fn parseDirections(line: []const u8) Directions {
var result = Directions.init();
for (line) |char| {
result.add(switch (char) {
'L' => .Left,
'R' => .Right,
else => unreachable,
});
}
return result;
}
const Node = struct {
line_index: u16,
current_label: u16,
left_next_label: u16,
right_next_label: u16,
};
const Nodes = [32 * 32 * 32]Node;
fn parseNodeLabel(line: []const u8) u16 {
var result: u16 = 0;
for (line) |char| {
result = (result << 5) + (char - 'A');
}
return result;
}
fn parseNodeLine(nodes: *Nodes, line: []const u8, line_index: u16) void {
const current_node = Node{
.line_index = if (line[2] == 'Z') (line_index | END_LINE_MASK) else line_index,
.current_label = parseNodeLabel(line[0..3]),
.left_next_label = parseNodeLabel(line[7..10]),
.right_next_label = parseNodeLabel(line[12..15]),
};
nodes[current_node.current_label] = current_node;
}
const TransitionMap = [MAX_DIRECTIONS * 1024]u32;
fn createTransitions(nodes: *const Nodes, directions: *const Directions) TransitionMap {
var result = std.mem.zeroes(TransitionMap);
for (nodes.*) |node| {
if (node.current_label == 0) {
continue;
}
//std.debug.print("Computing transitions for {d}\n", .{node.line_index});
for (directions.getSlice(), 0..) |direction, direction_index| {
const key = (direction_index << 10) | node.line_index;
const next_direction_index = (direction_index + 1) % directions.length;
const next = (@as(u32, @intCast(next_direction_index)) << 10) | nodes[
switch (direction) {
.Left => node.left_next_label,
.Right => node.right_next_label,
}
].line_index;
result[key] = next;
//std.debug.print("Saved transition from {d} to {d}\n", .{ key, next });
}
}
return result;
}
const CurrentNodes = [6]u32;
fn getStartingNodes(nodes: *const Nodes) CurrentNodes {
var starting_nodes = std.mem.zeroes(CurrentNodes);
var current_index: usize = 0;
for (nodes.*) |node| {
if (node.line_index != 0 and node.current_label & FIVE_BITS == 0) {
if (current_index == 0) {
for (&starting_nodes) |*starting_node| {
starting_node.* = node.line_index;
}
} else {
starting_nodes[current_index] = node.line_index;
}
current_index += 1;
}
}
return starting_nodes;
}
fn solve(nodes: *const Nodes, directions: *const Directions) u64 {
std.debug.print("Inside solve\n", .{});
const transitions = createTransitions(nodes, directions);
const starting = getStartingNodes(nodes);
//var current: CurrentNodes = .{ 203941, 204125, 203913, 204388, 204337, 203941 };
std.debug.print("Starting points: {any}\n", .{starting});
const DEBUG_MASK = (1 << 27) - 1;
var current0 = starting[0];
var current1 = starting[1];
var current2 = starting[2];
var current3 = starting[3];
var current4 = starting[4];
var current5 = starting[5];
var i: usize = 0;
while (true) : (i += 1) {
// Main loop, with hundreds of billions of iterations, so it is performance-critical.
if (i & DEBUG_MASK == 0) {
std.debug.print(
"Points at step {d}: {d} {d} {d} {d} {d} {d} (raw: {any})\n",
.{ i, current0 & 1023, current1 & 1023, current2 & 1023, current3 & 1023, current4 & 1023, current5 & 1023, .{ current0, current1, current2, current3, current4, current5 } },
);
}
if (current0 & current1 & current2 & current3 & current4 & current5 & @as(u32, END_LINE_MASK) == @as(u32, END_LINE_MASK)) {
std.debug.print(
"Points at step {d}: {d} {d} {d} {d} {d} {d} (raw: {any})\n",
.{ i, current0 & 1023, current1 & 1023, current2 & 1023, current3 & 1023, current4 & 1023, current5 & 1023, .{ current0, current1, current2, current3, current4, current5 } },
);
break;
}
current0 = transitions[@as(usize, current0)];
current1 = transitions[@as(usize, current1)];
current2 = transitions[@as(usize, current2)];
current3 = transitions[@as(usize, current3)];
current4 = transitions[@as(usize, current4)];
current5 = transitions[@as(usize, current5)];
}
return i;
}
pub fn main() !void {
std.debug.print("First line of main\n", .{});
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();
var line_buffer: [1000]u8 = undefined;
const first_line = (try reader.readUntilDelimiterOrEof(&line_buffer, '\n')).?;
var directions = parseDirections(first_line);
_ = try reader.readUntilDelimiterOrEof(&line_buffer, '\n');
std.debug.print("Creating nodes\n", .{});
var nodes = std.mem.zeroes(Nodes);
std.debug.print("Created nodes\n", .{});
var line_index: u16 = 3;
while (try reader.readUntilDelimiterOrEof(&line_buffer, '\n')) |line| {
parseNodeLine(&nodes, line, line_index);
line_index += 1;
}
std.debug.print("Calling solve\n", .{});
const result = solve(&nodes, &directions);
try stdout.print("{d}\n", .{result});
}