146 lines
3.8 KiB
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);
|
|
}
|