finish day 10

This commit is contained in:
Kienan Stewart 2020-12-12 11:12:13 -05:00
parent ebbc0931b7
commit 2bd4568cd8
3 changed files with 420 additions and 0 deletions

27
day10/build.zig Normal file
View File

@ -0,0 +1,27 @@
const Builder = @import("std").build.Builder;
pub fn build(b: *Builder) void {
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
// for restricting supported target set are available.
const target = b.standardTargetOptions(.{});
// Standard release options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const mode = b.standardReleaseOptions();
const exe = b.addExecutable("day10", "src/main.zig");
exe.setTarget(target);
exe.setBuildMode(mode);
exe.install();
const run_cmd = exe.run();
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_cmd.addArgs(args);
}
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
}

94
day10/input Normal file
View File

@ -0,0 +1,94 @@
138
3
108
64
92
112
44
53
27
20
23
77
119
62
121
11
2
37
148
34
83
24
10
79
96
98
127
7
115
19
16
78
133
61
82
91
145
39
33
13
97
55
141
1
134
40
71
54
103
101
26
47
90
72
126
124
110
131
58
12
142
105
63
75
50
95
69
25
68
144
86
132
89
128
135
65
125
76
116
32
18
6
38
109
111
30
70
143
104
102
120
31
41
17

299
day10/src/main.zig Normal file
View File

@ -0,0 +1,299 @@
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);
}