aoc/2020/10/src/main.zig

300 lines
8.4 KiB
Zig

const std = @import("std");
pub fn main() anyerror!void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
var gpa = &arena.allocator;
var f = try std.fs.cwd().openFile("input", .{});
var contents = try f.readToEndAlloc(gpa, std.math.maxInt(u32));
var it = std.mem.tokenize(contents, "\n");
var joltages = std.ArrayList(u32).init(gpa);
defer joltages.deinit();
while (it.next()) |line| {
try joltages.append(atoi(line));
}
var chain = try JoltageChain.construct_chain(gpa, joltages.items[0..]);
defer chain.deinit();
std.log.info("Product of one_diffs and three_diffs of joltage chain: {}",
.{chain.part_one()});
var permutations = try chain.calculate_permutations();
std.log.info("{} permutations", .{permutations});
}
const Tree = struct {
children : std.ArrayList(*Tree),
allocator: *std.mem.Allocator,
value : u32 = 0,
dirty : bool = true,
perms : u64 = 0,
fn init(allocator: *std.mem.Allocator, value: u32) !*Tree {
var self = try allocator.create(Tree);
errdefer allocator.destroy(self);
self.allocator = allocator;
self.value = value;
self.children = std.ArrayList(*Tree).init(allocator);
return self;
}
fn deinit(self: *Tree) void {
self.children.deinit();
self.allocator.destroy(self);
}
fn add_child(self: *Tree, child: *Tree) !void {
try self.children.append(child);
self.dirty = true;
}
fn find_child_by_value(self: *Tree, value: u32) ?*Tree {
if (self.value == value) {
return self;
}
for (self.children.items) |c| {
var t = c.find_child_by_value(value);
if (t) |_t| {
return _t;
}
}
return null;
}
// caching the calculation of the perm count makes it possible to
// finish reasonbly quickly when revisiting nodes that have already
// been seen
fn perm_count(self: *Tree) u64 {
if (!self.dirty) {
return self.perms;
}
var count : u64 = 0;
if (self.children.items.len == 0) {
count = 0; // No permutation, since have can only go one way
}
else if (self.children.items.len == 1) {
// We only have ourself
count = std.math.max(1, self.children.items[0].perm_count());
}
else {
for(self.children.items) |c| {
count += std.math.max(1, c.perm_count());
}
}
self.perms = count;
self.dirty = false;
return count;
}
fn print_tree(self: *Tree) void {
std.log.warn("{} children:\n", .{self.value});
for(self.children.items) |c| {
c.print_tree();
}
}
};
const JoltageChain = struct {
allocator: *std.mem.Allocator,
joltages : std.ArrayList(u32),
pub fn construct_chain(allocator: *std.mem.Allocator, j: []u32) !*JoltageChain {
var self = try allocator.create(JoltageChain);
errdefer allocator.destroy(self);
self.joltages = std.ArrayList(u32).init(allocator);
self.allocator = allocator;
// This has a side effect of modifying j...
std.sort.insertionSort(u32, j[0..], {}, comptime std.sort.asc(u32));
try self.joltages.append(0); // Always start at zero
var last_joltage : u32 = 0;
for (j) |x| {
if ((x-last_joltage)<4) {
try self.joltages.append(x);
last_joltage = x;
}
else {
break;
}
}
try self.joltages.append(last_joltage + 3);
return self;
}
pub fn deinit(self: *JoltageChain) void {
self.joltages.deinit();
self.allocator.destroy(self);
}
pub fn part_one(self: *JoltageChain) u32 {
var one_diffs : u32 = 0;
var three_diffs : u32 = 0;
for (self.joltages.items) |v, k| {
if (k == 0) {
continue;
}
var delta : u32 = v - self.joltages.items[k-1];
if (delta == 1) {
one_diffs += 1;
}
if (delta == 3) {
three_diffs += 1;
}
}
std.log.debug("Found {} one diffs, and {} three diffs",
.{one_diffs, three_diffs});
return one_diffs * three_diffs;
}
// this is a garbage way of doing it and just crushes CPU/memory
// it does work for small datasets though
pub fn calculate_permutations(self: *JoltageChain) !u32 {
var tree = try Tree.init(self.allocator, self.joltages.items[0]);
var current_node : *Tree = undefined;
var count : u32 = 0;
// Finding in the tree seems v. slow, will use a hash map for lookup
// it is also much easier to try and deinit
var map = std.hash_map.AutoHashMap(u32, *Tree).init(self.allocator);
try map.ensureCapacity(@intCast(u32, self.joltages.items.len));
map.putAssumeCapacityNoClobber(self.joltages.items[0], tree);
for (self.joltages.items) |v, k| {
var _t = map.get(v);
if (_t) |t| {
current_node = t;
}
else {
unreachable;
}
//std.log.warn("Current node: {}", .{current_node});
var n : usize = k+1;
while (n < self.joltages.items.len) : (n += 1) {
//std.log.warn("{}", .{n});
if ((self.joltages.items[n] - v) <= 3) {
var value = self.joltages.items[n];
_t = map.get(value);
if (_t) |t| {
try current_node.add_child(t);
//std.log.warn("Added existing child {} to {}", .{value, v});
}
else {
var t = try Tree.init(current_node.allocator, value);
try current_node.add_child(t);
map.putAssumeCapacityNoClobber(value, t);
//std.log.warn("Added new child {} to {}", .{value, v});
}
}
}
}
std.log.warn("tree count: {}", .{tree.perm_count()});
// Need to do some cleanup
var it = map.iterator();
while (it.next()) |kv| {
kv.value.deinit();
// don't remove from map, since it may modifying the underlying structure? (source?)
// and we're about to deinit it anyway
}
map.deinit();
return count;
}
};
test "joltage_chain" {
var joltages = [_]u32 {
16,
10,
15,
5,
1,
11,
7,
19,
6,
12,
4,
};
var chain = try JoltageChain.construct_chain(std.testing.allocator, joltages[0..]);
var diff = chain.part_one();
std.testing.expectEqual(diff, 7*5);
var perm = chain.calculate_permutations();
chain.deinit();
}
test "joltage_chain" {
var joltages = [_]u32 {
28,
33,
18,
42,
31,
14,
46,
20,
48,
47,
24,
23,
49,
45,
19,
38,
39,
11,
1,
32,
25,
35,
8,
17,
7,
9,
4,
2,
34,
10,
3,
};
var chain = try JoltageChain.construct_chain(std.testing.allocator, joltages[0..]);
var diff = chain.part_one();
std.testing.expectEqual(diff, 220);
var perm = chain.calculate_permutations();
chain.deinit();
}
fn atoi(a: []const u8) u32 {
var i : u32 = 0;
var mul : u32 = 1;
var start : usize = 0;
if (a[0] == '-' or a[0] == '+') {
start = 1;
if (a[0] == '-') {
//mul *= -1;
unreachable;
}
}
for(a[start..]) |v, k| {
if (! std.ascii.isDigit(v)) {
std.log.warn("Byte {x} is not a digit", .{v});
continue;
}
// 48 is '0' in ascii
std.debug.assert(v >= 48 and v < 58);
i += @as(u32, @as(u32, (v - 48)) * std.math.pow(u32, 10, @intCast(u32, a.len - k - 1 - start)));
}
//std.log.debug("0x{x} --> {}", .{a, i});
return i * mul;
}
test "atoi_regular" {
var i = atoi("1234");
std.testing.expectEqual(i, 1234);
}
test "atoi_pos" {
var i = atoi("+1234");
std.testing.expectEqual(i, 1234);
}