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.
241 lines
7.3 KiB
241 lines
7.3 KiB
10 months ago
|
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});
|
||
|
}
|