solve day 9

This commit is contained in:
Rokas Puzonas 2023-12-20 20:56:27 +02:00
parent 769fb00274
commit 6c5e1ea117
3 changed files with 172 additions and 4 deletions

View File

@ -9,6 +9,18 @@ pub const Input = struct {
// For now only .uint is needed
pub const Result = union(enum) {
uint: u64,
int: i64,
fn value(self: Result, comptime T: type) ?T {
inline for (@typeInfo(Result).Union.fields, 0..) |field, i| {
if (field.type != T) continue;
if (@intFromEnum(self) == i) {
return @field(self, field.name);
}
}
return null;
}
};
pub const Solver = *const fn(input: *Input) anyerror!Result;
@ -17,9 +29,17 @@ pub const Day = struct {
part2: ?Solver
};
pub fn expectAnswerUInt(solver: Solver, answer: u64, lines: []const []const u8) !void {
fn expectAnswer(comptime T: type, solver: Solver, expected: T, lines: []const []const u8) !void {
var input = Input{ .lines = lines, .allocator = std.testing.allocator };
var actual = try solver(&input);
var expected: u64 = answer;
try std.testing.expectEqual(expected, actual.uint);
var result: Result = try solver(&input);
var actual = result.value(T) orelse return error.ResultTypeMismatch;
try std.testing.expectEqual(expected, actual);
}
pub fn expectAnswerUInt(solver: Solver, answer: u64, lines: []const []const u8) !void {
try expectAnswer(u64, solver, answer, lines);
}
pub fn expectAnswerInt(solver: Solver, answer: i64, lines: []const []const u8) !void {
try expectAnswer(i64, solver, answer, lines);
}

145
src/day9.zig Normal file
View File

@ -0,0 +1,145 @@
const aoc = @import("./aoc.zig");
const std = @import("std");
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const History = std.ArrayList(i32);
const HistoryChanges = std.ArrayList([]i32);
const Input = struct {
histories: std.ArrayList(History),
fn deinit(self: *Input) void {
for (self.histories.items) |history| {
history.deinit();
}
self.histories.deinit();
}
};
fn parse_history(allocator: Allocator, line: []const u8) !History {
var history = History.init(allocator);
var tokens = std.mem.tokenizeScalar(u8, line, ' ');
while (tokens.next()) |token| {
const number = try std.fmt.parseInt(i32, token, 10);
try history.append(number);
}
return history;
}
fn parse_input(allocator: Allocator, lines: []const []const u8) !Input {
var histories = std.ArrayList(History).init(allocator);
errdefer histories.deinit();
for (lines) |line| {
const history = try parse_history(allocator, line);
try histories.append(history);
}
return Input{ .histories = histories };
}
fn is_all_zeros(numbers: []i32) bool {
for (numbers) |num| {
if (num != 0) {
return false;
}
}
return true;
}
fn calc_changes(result: []i32, numbers: []i32) void {
assert(result.len >= numbers.len-1);
for (0..(numbers.len-1)) |i| {
result[i] = numbers[i+1] - numbers[i];
}
}
fn all_changes_of_history(allocator: Allocator, history: []i32) !HistoryChanges {
const max_changes_len: usize = history.len-1;
assert(max_changes_len >= 1);
var changes = HistoryChanges.init(allocator);
errdefer free_history_changes(changes);
try changes.append(try allocator.alloc(i32, history.len-1));
calc_changes(changes.items[0], history);
for (1..max_changes_len) |i| {
if (is_all_zeros(changes.items[i-1])) break;
try changes.append(try allocator.alloc(i32, history.len-i-1));
calc_changes(changes.items[i], changes.items[i-1]);
}
return changes;
}
fn free_history_changes(changes: HistoryChanges) void {
for (changes.items) |row| {
changes.allocator.free(row);
}
changes.deinit();
}
pub fn part1(input: *aoc.Input) !aoc.Result {
const allocator = input.allocator;
var parsed = try parse_input(allocator, input.lines);
defer parsed.deinit();
var result: i32 = 0;
for (parsed.histories.items) |history| {
const numbers = history.items;
var changes = try all_changes_of_history(allocator, numbers);
defer free_history_changes(changes);
var prediction: i32 = numbers[numbers.len-1];
for (changes.items) |change_row| {
prediction += change_row[change_row.len-1];
}
result += prediction;
}
return .{ .int = result };
}
pub fn part2(input: *aoc.Input) !aoc.Result {
const allocator = input.allocator;
var parsed = try parse_input(allocator, input.lines);
defer parsed.deinit();
var result: i32 = 0;
for (parsed.histories.items) |history| {
const numbers = history.items;
var changes = try all_changes_of_history(allocator, numbers);
defer free_history_changes(changes);
var prediction: i32 = 0;
for (0..changes.items.len) |i| {
const change_row = changes.items[changes.items.len-i-1];
prediction = change_row[0] - prediction;
}
prediction = numbers[0] - prediction;
result += prediction;
}
return .{ .int = result };
}
const example_input = [_][]const u8{
"0 3 6 9 12 15",
"1 3 6 10 15 21",
"10 13 16 21 30 45",
};
test "part 1 example" {
try aoc.expectAnswerInt(part1, 114, &example_input);
}
test "part 2 example" {
try aoc.expectAnswerInt(part2, 2, &example_input);
}

View File

@ -29,6 +29,7 @@ const Days = [_]aoc.Day{
create_day(@import("day6.zig")),
create_day(@import("day7.zig")),
create_day(@import("day8.zig")),
create_day(@import("day9.zig")),
};
fn kilobytes(count: u32) u32 {
@ -88,6 +89,7 @@ fn run(allocator: Allocator, args: *cli) !u8 {
switch (result) {
.uint => std.debug.print("{}\n", .{result.uint}),
.int => std.debug.print("{}\n", .{result.int}),
}
const duration = end_time - start_time;
@ -180,4 +182,5 @@ test {
_ = @import("day6.zig");
_ = @import("day7.zig");
_ = @import("day8.zig");
_ = @import("day9.zig");
}