solve day 9
This commit is contained in:
parent
769fb00274
commit
6c5e1ea117
28
src/aoc.zig
28
src/aoc.zig
@ -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
145
src/day9.zig
Normal 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);
|
||||
}
|
@ -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");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user