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.

240 lines
7.3 KiB

const std = @import("std");
const Offset = enum(u2) {
Before = 0,
Same = 1,
After = 2,
fn applyTo(self: *const Offset, index: usize) usize {
return (index + @intFromEnum(self.*)) - 1;
}
};
const Char = enum(u4) {
Zero = 0,
One = 1,
Two = 2,
Three = 3,
Four = 4,
Five = 5,
Six = 6,
Seven = 7,
Eight = 8,
Nine = 9,
Dot = 12,
MaybeGear = 13,
OtherSymbol = 14,
fn isDigit(self: *const Char) bool {
return @intFromEnum(self.*) < 10;
}
fn getDigit(self: *const Char) u4 {
return @intFromEnum(self.*);
}
};
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 init() Self {
return Self{
.mem = undefined,
.length = 0,
};
}
};
}
const CharInfo = struct {
adjacents: [8]Char,
this: Char,
fn isAdjacentToSymbol(self: *const CharInfo) bool {
for (0..8) |i| {
if (self.adjacents[i] == .MaybeGear or self.adjacents[i] == .OtherSymbol) {
return true;
}
}
return false;
}
fn getGearNumbersLocations(self: *const CharInfo) ?[2][2]Offset {
if (self.this != .MaybeGear) {
return null;
}
var locations = StackList(comptime [2]Offset, comptime u8, comptime 6).init();
// previous line
if (self.adjacents[0].isDigit()) {
locations.add(.{ .Before, .Before });
if (!self.adjacents[1].isDigit() and self.adjacents[2].isDigit()) {
locations.add(.{ .Before, .After });
}
} else if (self.adjacents[1].isDigit()) {
locations.add(.{ .Before, .Same });
} else if (self.adjacents[2].isDigit()) {
locations.add(.{ .Before, .After });
}
// this line
if (self.adjacents[3].isDigit()) {
locations.add(.{ .Same, .Before });
}
if (self.adjacents[4].isDigit()) {
locations.add(.{ .Same, .After });
}
// next line
if (self.adjacents[5].isDigit()) {
locations.add(.{ .After, .Before });
if (!self.adjacents[6].isDigit() and self.adjacents[7].isDigit()) {
locations.add(.{ .After, .After });
}
} else if (self.adjacents[6].isDigit()) {
locations.add(.{ .After, .Same });
} else if (self.adjacents[7].isDigit()) {
locations.add(.{ .After, .After });
}
if (locations.length == 2) {
return .{ locations.mem[0], locations.mem[1] };
}
return null;
}
};
const State = struct {
number: u32 = 0,
is_adjacent_to_symbol: bool = false,
fn addDigit(self: *State, char_info: CharInfo) void {
self.number = self.number * 10 + char_info.this.getDigit();
self.is_adjacent_to_symbol = self.is_adjacent_to_symbol or char_info.isAdjacentToSymbol();
}
};
fn getNumberAt(engine: [][]const CharInfo, x: usize, y: usize) u32 {
const line = engine[x];
var index: usize = y;
while (index > 0) : (index -= 1) {
if (!line[index].this.isDigit()) {
break;
}
}
if (!line[index].this.isDigit()) {
index += 1;
}
var result: u32 = 0;
while (index < line.len) : (index += 1) {
if (!line[index].this.isDigit()) {
break;
}
result = result * 10 + line[index].this.getDigit();
}
return result;
}
fn solve(engine: [][]const CharInfo) u32 {
var result: u32 = 0;
for (engine, 0..) |line, i| {
//std.debug.print("handling new line\n", .{});
for (line, 0..) |char_info, j| {
//std.debug.print("handling: {s}({s})\n", .{ [_]u8{char_info.this}, char_info.adjacents });
const gearNumberLocations = char_info.getGearNumbersLocations();
if (gearNumberLocations) |*value| {
//std.debug.print("found gear at {d},{d}; relative number positions are {d},{d} and {d},{d}\n", .{ i, j, value[0][0], value[0][1], value[1][0], value[1][1] });
const a = getNumberAt(engine, value[0][0].applyTo(i), value[0][1].applyTo(j));
const b = getNumberAt(engine, value[1][0].applyTo(i), value[1][1].applyTo(j));
result += a * b;
}
}
}
return result;
}
fn charLinesToCharInfoLines(allocator: std.mem.Allocator, char_lines: [][]const Char) ![][]const CharInfo {
var char_info_lines = try allocator.alloc([]CharInfo, char_lines.len);
for (0..char_lines.len) |i| {
const char_line = char_lines[i];
var char_info_line = try allocator.alloc(CharInfo, char_line.len);
for (0..char_line.len) |j| {
char_info_line[j] = CharInfo{ .this = char_line[j], .adjacents = .{
if (i > 0 and j > 0) char_lines[i - 1][j - 1] else .Dot,
if (i > 0) char_lines[i - 1][j] else .Dot,
if (i > 0 and j + 1 < char_line.len) char_lines[i - 1][j + 1] else .Dot,
if (j > 0) char_line[j - 1] else .Dot,
if (j + 1 < char_line.len) char_line[j + 1] else .Dot,
if (i + 1 < char_lines.len and j > 0) char_lines[i + 1][j - 1] else .Dot,
if (i + 1 < char_lines.len) char_lines[i + 1][j] else .Dot,
if (i + 1 < char_lines.len and j + 1 < char_line.len) char_lines[i + 1][j + 1] else .Dot,
} };
}
char_info_lines[i] = char_info_line;
}
return char_info_lines;
}
fn readInput(allocator: std.mem.Allocator, reader: anytype) ![][]const Char {
var lines = std.ArrayList([]Char).init(allocator);
var line_buffer: [1000]u8 = undefined;
while (try reader.readUntilDelimiterOrEof(&line_buffer, '\n')) |line_ref| {
var line = try allocator.alloc(Char, line_ref.len);
for (line_ref, line) |raw_char, *char| {
char.* = switch (raw_char) {
'0'...'9' => @enumFromInt(raw_char - '0'),
'.' => .Dot,
'*' => .MaybeGear,
else => .OtherSymbol,
};
}
try lines.append(line);
}
return lines.items;
}
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();
var allocator_buffer: [1_000_000]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&allocator_buffer);
var allocator = fba.allocator();
var lines_arena = std.heap.ArenaAllocator.init(allocator);
const lines_allocator = lines_arena.allocator();
const char_lines = try readInput(lines_allocator, reader);
var info_lines_arena = std.heap.ArenaAllocator.init(allocator);
const info_lines_allocator = info_lines_arena.allocator();
const info_lines = try charLinesToCharInfoLines(info_lines_allocator, char_lines);
lines_arena.deinit();
const result = solve(info_lines);
info_lines_arena.deinit();
try stdout.print("{d}\n", .{result});
}