aoc-2023/src/day9.zig

146 lines
3.8 KiB
Zig

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;
const 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;
const 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);
}