122 lines
3.3 KiB
Zig
122 lines
3.3 KiB
Zig
const std = @import("std");
|
|
const aoc = @import("aoc.zig");
|
|
|
|
const Allocator = std.mem.Allocator;
|
|
const List = std.ArrayList;
|
|
const Map = std.AutoHashMap;
|
|
const Set = aoc.AutoHashSet;
|
|
const PriorityQueue = std.PriorityQueue;
|
|
|
|
pub fn main() !void {
|
|
var gpa: std.heap.GeneralPurposeAllocator(.{}) = .{};
|
|
defer _ = gpa.deinit();
|
|
const allocator = gpa.allocator();
|
|
|
|
const text = try aoc.readAll(allocator, "input/day22.txt");
|
|
defer allocator.free(text);
|
|
|
|
var lines = std.mem.tokenizeScalar(u8, text, '\n');
|
|
|
|
var seeds = List(u64).init(allocator);
|
|
defer seeds.deinit();
|
|
while (lines.next()) |line| {
|
|
const seed = try std.fmt.parseInt(u64, line, 10);
|
|
try seeds.append(seed);
|
|
}
|
|
|
|
var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
|
|
const stdout = bw.writer();
|
|
|
|
try stdout.print("{}\n{}\n", try solve(allocator, seeds.items));
|
|
try bw.flush();
|
|
}
|
|
|
|
fn solve(allocator: Allocator, seeds: []u64) !struct { u64, u64 } {
|
|
var totals = Map([4]i64, u64).init(allocator);
|
|
defer totals.deinit();
|
|
|
|
var sum: u64 = 0;
|
|
for (seeds) |seed| {
|
|
var profit = Map([4]i64, u64).init(allocator);
|
|
defer profit.deinit();
|
|
|
|
sum += try populate(seed, 2000, &profit);
|
|
|
|
var it = profit.iterator();
|
|
while (it.next()) |entry| {
|
|
const changes, const bananas = .{ entry.key_ptr.*, entry.value_ptr.* };
|
|
|
|
const total = try totals.getOrPutValue(changes, 0);
|
|
total.value_ptr.* += bananas;
|
|
}
|
|
}
|
|
|
|
var best: u64 = 0;
|
|
var it = totals.valueIterator();
|
|
while (it.next()) |p| {
|
|
best = @max(best, p.*);
|
|
}
|
|
return .{ sum, best };
|
|
}
|
|
|
|
fn populate(seed: u64, steps: usize, profit: *Map([4]i64, u64)) !u64 {
|
|
var n: u64 = seed;
|
|
var prices = [_]u64{0} ** 4;
|
|
|
|
// Pre-fill the first few updates, since those don't have enough changes yet.
|
|
for (0..3) |i| {
|
|
const price = n % 10;
|
|
prices[i] = price;
|
|
|
|
n = ((n * 64) ^ n) % 16777216;
|
|
n = ((n / 32) ^ n) % 16777216;
|
|
n = ((n * 2048) ^ n) % 16777216;
|
|
}
|
|
|
|
var changes = [_]i64{0} ** 4;
|
|
for (1..4) |i| {
|
|
const next: i64 = @intCast(prices[i]);
|
|
const prev: i64 = @intCast(prices[i - 1]);
|
|
changes[i] = next - prev;
|
|
}
|
|
|
|
// This is the first time we have a usable change sequence.
|
|
_ = try profit.getOrPutValue(changes, prices[3]);
|
|
|
|
for (3..steps) |_| {
|
|
n = ((n * 64) ^ n) % 16777216;
|
|
n = ((n / 32) ^ n) % 16777216;
|
|
n = ((n * 2048) ^ n) % 16777216;
|
|
|
|
const price = n % 10;
|
|
|
|
std.mem.copyForwards(u64, &prices, prices[1..]);
|
|
prices[3] = price;
|
|
|
|
std.mem.copyForwards(i64, &changes, changes[1..]);
|
|
{
|
|
const a: i64 = @intCast(prices[3]);
|
|
const b: i64 = @intCast(prices[2]);
|
|
changes[3] = a - b;
|
|
}
|
|
_ = try profit.getOrPutValue(changes, prices[3]);
|
|
}
|
|
|
|
// Everything but the update, because we *can* still sell here.
|
|
{
|
|
const price = n % 10;
|
|
|
|
std.mem.copyForwards(u64, &prices, prices[1..]);
|
|
prices[3] = price;
|
|
|
|
std.mem.copyForwards(i64, &changes, changes[1..]);
|
|
{
|
|
const a: i64 = @intCast(prices[3]);
|
|
const b: i64 = @intCast(prices[2]);
|
|
changes[3] = a - b;
|
|
}
|
|
_ = try profit.getOrPutValue(changes, prices[3]);
|
|
}
|
|
|
|
return n;
|
|
}
|