Solution to 2-1
This commit is contained in:
parent
aa408ad75b
commit
84223d9b3b
1000
inputs/02/real.txt
Normal file
1000
inputs/02/real.txt
Normal file
File diff suppressed because it is too large
Load Diff
6
inputs/02/test.txt
Normal file
6
inputs/02/test.txt
Normal 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
128
solutions/02.zig
Normal 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);
|
||||
}
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user