const std = @import("std");
const builtin = @import("builtin");
// NOTE: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
const segment_counts = [_]u32{ 6, 2, 5, 5, 4, 5, 6, 3, 7, 6 };
pub const Display = struct {
definitions: []const []const u8,
digits: []const []const u8,
};
fn readInputFile(allocator: std.mem.Allocator, filename: []const u8) ![]Display {
var result = std.ArrayList(Display).init(allocator);
const file = try std.fs.cwd().openFile(filename, .{ .read = true });
defer file.close();
const reader = file.reader();
while (true) {
var line: []u8 = undefined;
if (builtin.os.tag == .windows) {
// NOTE: Read another byte on windows due to two-byte eol.
line = reader.readUntilDelimiterAlloc(allocator, '\r', 512) catch break;
_ = try reader.readByte();
} else {
line = reader.readUntilDelimiterAlloc(allocator, '\n', 512) catch break;
}
defer allocator.free(line);
var display: Display = undefined;
var parts = std.mem.split(u8, line, " | ");
var definitions = std.ArrayList([]u8).init(allocator);
var it = std.mem.split(u8, parts.next().?, " ");
while (it.next()) |slice| {
var def = try allocator.dupe(u8, slice);
try definitions.append(def);
}
display.definitions = definitions.items;
var digits = std.ArrayList([]u8).init(allocator);
it = std.mem.split(u8, parts.next().?, " ");
while (it.next()) |slice| {
var dig = try allocator.dupe(u8, slice);
try digits.append(dig);
}
display.digits = digits.items;
try result.append(display);
}
return result.items;
}
pub fn task1(input: []const Display) u32 {
var result: u32 = 0;
for (input) |display| {
for (display.digits) |digit| {
if (digit.len == segment_counts[1] or
digit.len == segment_counts[4] or
digit.len == segment_counts[7] or
digit.len == segment_counts[8])
{
result += 1;
}
}
}
return result;
}
fn findMatch(segment_count: u32, base_on: usize, match_count: u32, partial: []usize, display: *const Display) usize {
skip: for (display.definitions) |digit, index| {
for (partial) |p| {
if (p == index) {
continue :skip;
}
}
if (digit.len == segment_count) {
var found: u32 = 0;
for (display.definitions[base_on]) |def| {
for (digit) |dig| {
if (def == dig) {
found += 1;
}
}
}
if (found == match_count) {
return index;
}
}
}
unreachable;
}
pub fn task2(allocator: std.mem.Allocator, input: []const Display) ![]u32 {
var result = std.ArrayList(u32).init(allocator);
for (input) |display| {
var solution = [_]usize{99} ** 10;
for (display.definitions) |digit, index| {
if (digit.len == segment_counts[1]) {
solution[1] = index;
std.log.debug("1 {s}", .{digit});
} else if (digit.len == segment_counts[4]) {
solution[4] = index;
std.log.debug("4 {s}", .{digit});
} else if (digit.len == segment_counts[7]) {
solution[7] = index;
std.log.debug("7 {s}", .{digit});
} else if (digit.len == segment_counts[8]) {
solution[8] = index;
std.log.debug("8 {s}", .{digit});
}
}
solution[6] = findMatch(segment_counts[6], solution[1], 1, &solution, &display);
std.log.debug("6 {s}", .{display.definitions[solution[6]]});
solution[0] = findMatch(segment_counts[0], solution[4], 3, &solution, &display);
std.log.debug("0 {s}", .{display.definitions[solution[0]]});
solution[9] = findMatch(segment_counts[9], solution[0], 5, &solution, &display);
std.log.debug("9 {s}", .{display.definitions[solution[9]]});
solution[5] = findMatch(segment_counts[5], solution[6], 5, &solution, &display);
std.log.debug("5 {s}", .{display.definitions[solution[5]]});
solution[3] = findMatch(segment_counts[3], solution[9], 5, &solution, &display);
std.log.debug("3 {s}", .{display.definitions[solution[3]]});
solution[2] = findMatch(segment_counts[2], solution[8], 5, &solution, &display);
std.log.debug("2 {s}", .{display.definitions[solution[2]]});
std.log.debug("solution {any}", .{solution});
var solve_result: u32 = 0;
solve: for (display.digits) |dig| {
std.log.debug("looking for {s}", .{dig});
for (display.definitions) |def, def_index| {
if (dig.len == def.len) {
std.log.debug("potential {s}#{}", .{ def, def.len });
var count: u32 = 0;
for (dig) |dig_char| {
for (def) |def_char| {
if (dig_char == def_char) {
count += 1;
}
}
}
if (count == def.len) {
std.log.debug("found! {s}#{}", .{ def, def.len });
for (solution) |sol, sol_index| {
if (sol == def_index) {
std.log.debug("={}", .{sol_index});
solve_result = (solve_result * 10) + @intCast(u32, sol_index);
}
}
continue :solve;
}
}
}
}
try result.append(solve_result);
}
return result.items;
}
pub fn main() !void {
var buffer: [2000000]u8 = undefined;
var fixed_buffer = std.heap.FixedBufferAllocator.init(&buffer);
const allocator = fixed_buffer.allocator();
const input = try readInputFile(allocator, "input.txt");
const task_1_result = task1(input);
std.log.info("Task 1 result: {}", .{task_1_result});
const task_2_result = try task2(allocator, input);
var count: u32 = 0;
for (task_2_result) |r| {
count += r;
}
std.log.info("Task 2 result: {}", .{count});
}