Solution to 2-1

This commit is contained in:
Jack Jackson 2024-12-19 15:11:25 -08:00
parent aa408ad75b
commit 84223d9b3b
4 changed files with 1144 additions and 0 deletions

1000
inputs/02/real.txt Normal file

File diff suppressed because it is too large Load Diff

6
inputs/02/test.txt Normal file
View File

@ -0,0 +1,6 @@
7 6 4 2 1
1 2 7 8 9
9 7 6 2 1
1 3 2 4 5
8 6 4 4 1
1 3 6 7 9

128
solutions/02.zig Normal file
View File

@ -0,0 +1,128 @@
const std = @import("std");
const print = std.debug.print;
const util = @import("util.zig");
pub fn main() !void {
const output = try part_one();
print("{}\n", .{output});
}
pub fn part_one() !u32 {
const isTestCase = true;
// Reading the input...
// Making use of example here: https://cookbook.ziglang.cc/01-01-read-file-line-by-line.html
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const path = try util.getInputFile("02", isTestCase);
const file = try std.fs.cwd().openFile(path, .{});
defer file.close();
// Wrap the file reader in a buffered reader.
// Since it's usually faster to read a bunch of bytes at once.
var buf_reader = std.io.bufferedReader(file.reader());
const reader = buf_reader.reader();
var line = std.ArrayList(u8).init(allocator);
defer line.deinit();
const writer = line.writer();
var line_no: usize = 0;
var safe_count: u32 = 0;
while (reader.streamUntilDelimiter(writer, '\n', null)) {
defer line.clearRetainingCapacity();
line_no += 1;
const report = try parseLineToNumbers(line.items, allocator);
if (try is_safe(report)) {
safe_count += 1;
}
report.deinit();
} else |err| switch (err) {
error.EndOfStream => { // end of file
if (line.items.len > 0) {
line_no += 1;
print("{d}--{s}\n", .{ line_no, line.items });
}
},
else => return err, // Propagate error
}
return safe_count;
}
pub const IllegalDataError = error{
TooSmall
};
// The logic given is:
// - "Levels are either all increasing or all decreasing"
// - "Adjacent levels differ by at least one and at most three"
//
// which can be rephrased (so as to avoid having to pass through the state of "is this an all-increasing or
// all-decreasing report?") as:
// - "The first pair of levels is within 1-3"
// - "For all triples in the report, both pairs have the same direction, and the final pair is within 1-3"
fn is_safe(reportAsArray: std.ArrayList(u32)) !bool {
const report = reportAsArray.items;
// Legality-check that the report contains at least two levels
if (report.len < 2) {
return IllegalDataError.TooSmall;
}
// First, check that the first pair of levels is within 1-3
const diff_of_first_pair = util.diffOfNumbers(report[0], report[1]);
if ((diff_of_first_pair < 1) or (diff_of_first_pair > 3)) {
return false;
}
// Early return if the report contains only 2 levels
if (report.len < 3) {
return true;
}
// We know that the report contains at least 3 levels, so we can start a sliding comparison.
var i: u32 = 0;
while (i < (report.len - 2)) {
if (
(report[i] > report[i+1]) and (report[i+1] < report[i+2])
or
(report[i] < report[i+1] and (report[i+1] > report[i+2]))
) {
return false;
}
const diff_of_second_pair = util.diffOfNumbers(report[i+1], report[i+2]);
if ((diff_of_second_pair < 1) or (diff_of_second_pair > 3)) {
return false;
}
i+=1;
}
return true;
}
// TODO - probably extract this to utils?
// (Though note that this differs from the example in 01.zig)
fn parseLineToNumbers(line: []u8, allocator: std.mem.Allocator) !std.ArrayList(u32) {
var numbers = std.ArrayList(u32).init(allocator);
// Feels _weird_ to be explicitly `deinit`-ing memory! This language is so fucked up.
// https://stackoverflow.com/a/79199470/1040915
var it = std.mem.splitScalar(u8, line, ' ');
while (it.next()) |chunk| {
print("Trying to parse {any}\n", .{chunk});
const parsed = try std.fmt.parseInt(u32, chunk, 10);
print("Parsed it to {}\n", .{parsed});
try numbers.append(parsed);
}
return numbers;
}
const expect = std.testing.expect;
test "part one" {
try expect(try part_one() == 2);
}

View File

@ -50,6 +50,16 @@ fn concat(comptime T: type, allocator: std.mem.Allocator, arr1: []const T, arr2:
return combined;
}
// Ugh. There are a _ton_ of problems with this because of overflow nonsense - but it's good enough to use until
// test cases demonstrate that it's not.
pub fn diffOfNumbers(a: u32, b: u32) u32 {
if (a > b) {
return a - b;
} else {
return b - a;
}
}
const expect = @import("std").testing.expect;
test {