Solutions to 11
This commit is contained in:
parent
0ace5ca159
commit
c80041ba08
1
inputs/11/real.txt
Normal file
1
inputs/11/real.txt
Normal file
@ -0,0 +1 @@
|
||||
510613 358 84 40702 4373582 2 0 1584
|
1
inputs/11/test.txt
Normal file
1
inputs/11/test.txt
Normal file
@ -0,0 +1 @@
|
||||
125 17
|
161
solutions/11.zig
Normal file
161
solutions/11.zig
Normal file
@ -0,0 +1,161 @@
|
||||
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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user