const std = @import("std");
const ArrayList = std.ArrayList;
const SymmetryResult = struct {
part1: u32,
part2: u32,
};
pub fn solve_part1(allocator: std.mem.Allocator, file: []const u8) !void {
var inputs = try std.fs.cwd().openFile(file, .{});
var contents = try inputs.readToEndAlloc(allocator, 100 * 1024);
var section_splitter = std.mem.splitScalar(u8, contents[0 .. contents.len - 1], '\n');
var sections = ArrayList(ArrayList([]const u8)).init(allocator);
try sections.append(ArrayList([]const u8).init(allocator));
{
var section = §ions.items[0];
while (section_splitter.next()) |line| {
if (line.len == 0) {
try sections.append(ArrayList([]const u8).init(allocator));
section = §ions.items[sections.items.len - 1];
continue;
}
try section.append(line);
}
}
var timer = try std.time.Timer.start();
var grid: [][50]u8 = try allocator.alloc([50]u8, 50);
defer allocator.free(grid);
var rows: []u32 = try allocator.alloc(u32, 50);
defer allocator.free(rows);
var num_rows: u32 = 0;
var result_1: u64 = 0;
var result_2: u64 = 0;
for (sections.items) |section| {
num_rows = 0;
for (section.items, 0..) |line, i| {
@memcpy(grid[i][0..line.len], line);
std.mem.replaceScalar(u8, grid[i][0..line.len], '.', '0');
std.mem.replaceScalar(u8, grid[i][0..line.len], '#', '1');
rows[num_rows] = try std.fmt.parseInt(u32, grid[i][0..line.len], 2);
num_rows += 1;
}
var result = find_symmetries(rows[0..num_rows]);
result_1 += result.part1 * 100;
result_2 += result.part2 * 100;
transpose(grid, @intCast(section.items.len), @intCast(section.items[0].len));
num_rows = 0;
for (0..section.items[0].len) |i| {
rows[num_rows] = try std.fmt.parseInt(u32, grid[i][0..section.items.len], 2);
num_rows += 1;
}
result = find_symmetries(rows[0..num_rows]);
result_1 += result.part1;
result_2 += result.part2;
}
var time = timer.read();
std.log.info("Part 1: {d}", .{result_1});
std.log.info("Part 2: {d}", .{result_2});
std.log.info("Time: {d}", .{time});
}
fn transpose(grid: [][50]u8, rows: u32, columns: u32) void {
var max = @max(rows, columns);
for (0..max) |row| {
for (row..max) |column| {
var t = grid[row][column];
grid[row][column] = grid[column][row];
grid[column][row] = t;
}
}
}
fn is_pow_2(val: u64) bool {
return (val & (val - 1)) == 0 and val != 0;
}
fn find_symmetries(rows: []u32) SymmetryResult {
var errors: u32 = 0;
var error_val: u32 = 0;
var result = SymmetryResult{
.part1 = 0,
.part2 = 0,
};
for (0..rows.len - 1) |row| {
errors = 0;
var j: u32 = 0;
while (errors < 2 and row >= j and j + row + 1 < rows.len) : (j += 1) {
if (rows[row - j] != rows[row + j + 1]) {
errors += 1;
error_val = rows[row - j] ^ rows[row + j + 1];
}
}
if (errors == 1 and is_pow_2(error_val)) {
result.part2 += @intCast(row + 1);
}
if (errors == 0) {
result.part1 += @intCast(row + 1);
}
}
return result;
}