162 lines
6.2 KiB
Zig
162 lines
6.2 KiB
Zig
const std = @import("std");
|
|
const print = std.debug.print;
|
|
const util = @import("util.zig");
|
|
|
|
pub fn main() !void {
|
|
const response = try part_two(false, 75);
|
|
print("Response from running with 75 blinks is {}\n", .{response});
|
|
}
|
|
|
|
fn part_one(is_test_case: bool) anyerror!u128 {
|
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
defer _ = gpa.deinit();
|
|
const allocator = gpa.allocator();
|
|
|
|
const input_file = try util.getInputFile("11", is_test_case);
|
|
const data = try util.readAllInputWithAllocator(input_file, allocator);
|
|
defer allocator.free(data);
|
|
|
|
var start_stones = try parseData(data, allocator);
|
|
defer allocator.free(start_stones);
|
|
|
|
for (0..25) |_| {
|
|
start_stones = try blinkStones(start_stones, allocator);
|
|
// print("DEBUG - after iteration {}, stones are {any}\n", .{ i, start_stones });
|
|
}
|
|
return start_stones.len;
|
|
}
|
|
|
|
fn parseData(data: []const u8, alloc: std.mem.Allocator) ![]u128 {
|
|
var accum = std.ArrayList(u128).init(alloc);
|
|
var it = std.mem.splitScalar(u8, data, ' ');
|
|
while (it.next()) |chunk| {
|
|
print("DEBUG - parsing {any} as int\n", .{chunk});
|
|
try accum.append(try std.fmt.parseInt(u128, chunk, 10));
|
|
}
|
|
print("DEBUG - initial values are: ", .{});
|
|
for (accum.items) |stone_value| {
|
|
print("{} ", .{stone_value});
|
|
}
|
|
print("\n", .{});
|
|
return accum.toOwnedSlice();
|
|
}
|
|
|
|
fn blinkStones(stones: []u128, alloc: std.mem.Allocator) ![]u128 {
|
|
var accum = std.ArrayList(u128).init(alloc);
|
|
for (stones) |stone| {
|
|
const blinkedStones = try blinkStone(stone, alloc);
|
|
for (blinkedStones) |blinked_stone| {
|
|
try accum.append(blinked_stone);
|
|
}
|
|
alloc.free(blinkedStones);
|
|
}
|
|
alloc.free(stones);
|
|
return accum.toOwnedSlice();
|
|
}
|
|
|
|
fn blinkStone(stone: u128, alloc: std.mem.Allocator) ![]u128 {
|
|
// I don't like having to create an ArrayList here, but I don't know how to get around it:
|
|
// `return []u128{1}` gives `array literal requires address-of operator (&) to coerce to slice type '[]u128'`
|
|
// `return [_]u128{1}` gives the same error
|
|
// `return [1]u128{1}` gives `expected type '[]u128', found '*const [1]u128'
|
|
// And I can't declare the return type to be `[1]u128` because sometimes it's _not_ a single value
|
|
var al = std.ArrayList(u128).init(alloc);
|
|
if (stone == 0) {
|
|
try al.append(1);
|
|
return al.toOwnedSlice();
|
|
}
|
|
const number_of_digits_in_stone = std.math.log10_int(stone) + 1;
|
|
// print("number of digits in {} is {}, ", .{ stone, number_of_digits_in_stone });
|
|
if (number_of_digits_in_stone % 2 == 0) {
|
|
// print("which is even, so splitting it\n", .{});
|
|
const half_number_of_digits = @divExact(number_of_digits_in_stone, 2);
|
|
const factor = std.math.pow(u128, 10, half_number_of_digits);
|
|
try al.append(stone / factor);
|
|
try al.append(stone % factor);
|
|
return al.toOwnedSlice();
|
|
} else {
|
|
// print("which is odd, so multiplying it by 2024\n", .{});
|
|
try al.append(stone * 2024);
|
|
return al.toOwnedSlice();
|
|
}
|
|
}
|
|
|
|
fn part_two(is_test_case: bool, blinks: u8) anyerror!u128 {
|
|
// Heh, I suspected that this would just be an optimization question :P
|
|
// Memoization ahoy!
|
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
defer _ = gpa.deinit();
|
|
const allocator = gpa.allocator();
|
|
|
|
const input_file = try util.getInputFile("11", is_test_case);
|
|
const data = try util.readAllInputWithAllocator(input_file, allocator);
|
|
defer allocator.free(data);
|
|
|
|
const start_stones = try parseData(data, allocator);
|
|
defer allocator.free(start_stones);
|
|
|
|
var cache_data = std.AutoHashMap(Query, u128).init(allocator);
|
|
defer cache_data.deinit();
|
|
var cache = Cache{ .data = cache_data };
|
|
|
|
var total: u128 = 0;
|
|
for (start_stones) |stone| {
|
|
total += try cache.get(Query{ .value = stone, .blinks = blinks }, allocator);
|
|
}
|
|
return total;
|
|
}
|
|
|
|
const Query = struct { value: u128, blinks: u8 };
|
|
|
|
const Cache = struct {
|
|
data: std.AutoHashMap(Query, u128),
|
|
fn get(self: *Cache, query: Query, alloc: std.mem.Allocator) !u128 {
|
|
// "If you have a single stone with value `value`, how many stones will you have after `blinks` number of blinks?"
|
|
if (query.blinks == 0) {
|
|
return 1;
|
|
}
|
|
// A `getOrPut`-based solution - as outlined below - doesn't work. I'm _guessing_ this is because the
|
|
// pointer is invalid after recursively calling `try self.get(Query{...})`, because _they_ will mutate the
|
|
// HashMap, and so it's been "extended" such that the pointer doesn't point to the right place anymore.
|
|
// ...this memory volatility is getting really annoying!
|
|
// const response = try self.data.getOrPut(query);
|
|
// if (response.found_existing) {
|
|
// return response.value_ptr.*;
|
|
// } else {
|
|
// const blinked_stones = try blinkStone(query.value, alloc);
|
|
// var total: u32 = 0;
|
|
// for (blinked_stones) |blinked_stone| {
|
|
// total += try self.get(Query{ .value = blinked_stone, .blinks = query.blinks - 1 }, alloc);
|
|
// }
|
|
// response.value_ptr.* = total;
|
|
// alloc.free(blinked_stones);
|
|
// return total;
|
|
// }
|
|
//
|
|
if (self.data.contains(query)) {
|
|
return self.data.get(query).?;
|
|
} else {
|
|
const blinked_stones = try blinkStone(query.value, alloc);
|
|
var total: u128 = 0;
|
|
for (blinked_stones) |blinked_stone| {
|
|
total += try self.get(Query{ .value = blinked_stone, .blinks = query.blinks - 1 }, alloc);
|
|
}
|
|
try self.data.put(query, total);
|
|
alloc.free(blinked_stones);
|
|
return total;
|
|
}
|
|
}
|
|
};
|
|
|
|
test "part_one" {
|
|
const part_one_response = try part_one(true);
|
|
print("DEBUG - part_one_response is {}\n", .{part_one_response});
|
|
try std.testing.expect(part_one_response == 55312);
|
|
}
|
|
|
|
test "original_question_with_cache" {
|
|
const with_cache_response = try part_two(true, 25);
|
|
print("DEBUG - with_cache response is {}\n", .{with_cache_response});
|
|
try std.testing.expect(with_cache_response == 55312);
|
|
}
|