|
| 1 | +const std = @import("std"); |
| 2 | +const Allocator = std.mem.Allocator; |
| 3 | +const HashMap = std.AutoArrayHashMap; |
| 4 | +const string = []const u8; |
| 5 | + |
| 6 | +/// Task 1 & 2 - Simulate the given amount of blinks and return the amount of |
| 7 | +/// stones after all blinks. Use two sets of stones which are |
| 8 | +/// swapped after each blink to reduce the amount of reallocations |
| 9 | +/// of the used Hashmaps. |
| 10 | +/// |
| 11 | +/// Arguments: |
| 12 | +/// - `contents`: Input file contents. |
| 13 | +/// - `blinks`: Amount of blinks. |
| 14 | +/// |
| 15 | +/// Returns: |
| 16 | +/// - The amount of stones after all blinks. |
| 17 | +pub fn amount_of_stones(contents: string, blinks: usize) !u64 { |
| 18 | + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); |
| 19 | + defer arena.deinit(); |
| 20 | + |
| 21 | + const allocator = arena.allocator(); |
| 22 | + var stones = try parse(contents, allocator); |
| 23 | + var new_stones = HashMap(u64, u64).init(allocator); |
| 24 | + |
| 25 | + for (0..blinks) |_| { |
| 26 | + try blink(stones, &new_stones); |
| 27 | + std.mem.swap(HashMap(u64, u64), &stones, &new_stones); |
| 28 | + } |
| 29 | + |
| 30 | + var stones_count: u64 = 0; |
| 31 | + for (stones.values()) |amount| { |
| 32 | + stones_count += amount; |
| 33 | + } |
| 34 | + |
| 35 | + return stones_count; |
| 36 | +} |
| 37 | + |
| 38 | +// -------------------------------------------------------------------------- \\ |
| 39 | + |
| 40 | +/// Simulate 'one blink'. For each unique stone number apply the rules and |
| 41 | +/// increment the amounts of the new stones. |
| 42 | +/// |
| 43 | +/// Arguments: |
| 44 | +/// - `stones`: Current set of stone numbers and amounts. |
| 45 | +/// - `new_stones`: The new stones after the blink. |
| 46 | +fn blink(stones: HashMap(u64, u64), new_stones: *HashMap(u64, u64)) !void { |
| 47 | + new_stones.clearRetainingCapacity(); |
| 48 | + for (stones.keys()) |stone| { |
| 49 | + const amount = stones.getPtr(stone).?.*; |
| 50 | + if (stone == 0) { |
| 51 | + try increment_amount(new_stones, 1, amount); |
| 52 | + continue; |
| 53 | + } |
| 54 | + const digits = std.math.log10(stone) + 1; |
| 55 | + if (digits % 2 == 0) { |
| 56 | + const power_of_10 = try std.math.powi(u64, 10, digits / 2); |
| 57 | + const right_stone = stone % power_of_10; |
| 58 | + const left_stone = (stone - right_stone) / power_of_10; |
| 59 | + |
| 60 | + try increment_amount(new_stones, right_stone, amount); |
| 61 | + try increment_amount(new_stones, left_stone, amount); |
| 62 | + } else { |
| 63 | + try increment_amount(new_stones, stone * 2024, amount); |
| 64 | + } |
| 65 | + } |
| 66 | +} |
| 67 | + |
| 68 | +/// Parse the file contents into a initial map of stones with their number |
| 69 | +/// mapped to the amount of the same stone. |
| 70 | +/// |
| 71 | +/// Arguments: |
| 72 | +/// - `contents`: Input file contents. |
| 73 | +/// - `allocator`: Allocator for the containers. |
| 74 | +/// |
| 75 | +/// Returns: |
| 76 | +/// - Map of stone numbers to stone amounts. |
| 77 | +fn parse(contents: string, allocator: Allocator) !HashMap(u64, u64) { |
| 78 | + var stones = HashMap(u64, u64).init(allocator); |
| 79 | + |
| 80 | + var numbers = std.mem.tokenize(u8, contents, " "); |
| 81 | + while (numbers.next()) |num| { |
| 82 | + try increment_amount(&stones, try std.fmt.parseInt(u64, num, 10), 1); |
| 83 | + } |
| 84 | + |
| 85 | + return stones; |
| 86 | +} |
| 87 | + |
| 88 | +/// Increment the amount of a given stone in the map by the given amount. Create |
| 89 | +/// a new entry if the stone didn't exist before. |
| 90 | +/// |
| 91 | +/// Arguments: |
| 92 | +/// - `contents`: Input file contents. |
| 93 | +/// - `allocator`: Allocator for the containers. |
| 94 | +/// |
| 95 | +/// Returns: |
| 96 | +/// - Map of stone numbers to stone amounts. |
| 97 | +fn increment_amount(stones: *HashMap(u64, u64), stone: u64, increment: u64) !void { |
| 98 | + const entry = try stones.*.getOrPutValue(stone, increment); |
| 99 | + if (entry.found_existing) { |
| 100 | + entry.value_ptr.* += increment; |
| 101 | + } |
| 102 | +} |
0 commit comments