aoc-2020-zig

git clone https://git.sinitax.com/sinitax/aoc-2020-zig
Log | Files | Refs | README | sfeed.txt

commit 0d74166d177df1853679d83d11c45bfd0358a4a0
parent 9755ba3236266ea10d532d2de17f718b206fcaf5
Author: Louis Burda <quent.burda@gmail.com>
Date:   Fri, 11 Dec 2020 16:28:10 +0100

added day 10, fixed scrape and added option to specify other input via env var in aoc lib

Diffstat:
Mlib/aoc.zig | 14+++++++++++++-
Mscripts/scrape | 8+++++---
Msrc/day10/main.zig | 148++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Asrc/day10/out | 32++++++++++++++++++++++++++++++++
Asrc/day10/part2 | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/day10/test-input | 31+++++++++++++++++++++++++++++++
Asrc/day10/test-input2 | 11+++++++++++
7 files changed, 300 insertions(+), 9 deletions(-)

diff --git a/lib/aoc.zig b/lib/aoc.zig @@ -18,8 +18,20 @@ pub fn gen_main(comptime part1: part_type, comptime part2: part_type) fn () anye if (args.len < 2) return; const part = try std.fmt.parseInt(u8, args[1], 10); + var filename: []const u8 = std.mem.spanZ("input"); + for (std.os.environ) |v| { + const kv = std.mem.spanZ(v); + if (std.mem.indexOfScalar(u8, kv, '=')) |sep| { + if (sep < kv.len - 1 and std.mem.eql(u8, kv[0..sep], "AOCINPUT")) { + filename = kv[sep + 1 ..]; + std.debug.print("Using input file: {}\n", .{filename}); + break; + } + } + } + // read all input into mem (files are always small so no problem) - const file = try std.fs.cwd().openFile("input", .{}); + const file = try std.fs.cwd().openFile(filename, .{}); const text = try file.reader().readAllAlloc(heapalloc, std.math.maxInt(u32)); defer heapalloc.free(text); diff --git a/scripts/scrape b/scripts/scrape @@ -68,10 +68,12 @@ def scrape_text(day, part): with open(path, "w+") as of: text = extract_hltext(textobj) - parts = text.split("\n\n") + parts = text.split("\n") for i,p in enumerate(parts): + if p == "": + continue newp = [list(),] - words = p.split() + words = p.split(" ") linelen = 0 for w in words: if linelen + len(w) > 100: @@ -80,7 +82,7 @@ def scrape_text(day, part): newp[-1].append(w) linelen += len(w) + 1 parts[i] = "\n".join([" ".join(words) for words in newp]) - of.write("{}\n".format("\n\n".join(parts))) + of.write("{}\n".format("\n".join(parts))) def main(): if cmd == "input": diff --git a/src/day10/main.zig b/src/day10/main.zig @@ -2,15 +2,153 @@ const std = @import("std"); const aoc = @import("aoc"); fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var answer: u32 = 0; + var target: u32 = 0; - std.debug.print("{}\n", .{answer}); + var intlist = std.ArrayList(u32).init(allocator); + defer intlist.deinit(); + + var lineit = std.mem.tokenize(input, "\n"); + while (lineit.next()) |line| { + var val = try std.fmt.parseInt(u32, line, 10); + if (val > target) { + target = val; + } + try intlist.append(val); + } + + target += 3; + try intlist.append(target); + + if (intlist.items.len == 0) return; + + std.sort.sort(u32, intlist.items, {}, comptime std.sort.asc(u32)); + + var diffs = [_]u32{ 0, 0, 0, 0 }; + var last: u32 = 0; + for (intlist.items) |ov| { + if (ov - last > 3 or ov == last) { + std.debug.print("Unable to find a suitable adapter for {} output jolts (next: {} jolts)\n", .{ last, ov }); + return aoc.Error.InvalidInput; + } + diffs[ov - last] += 1; + last = ov; + } + + std.debug.print("{}\n", .{diffs[1] * diffs[3]}); } -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var answer: u32 = 0; +fn countArrangements(arrangement: []u32, adapters: []const u32, adaptermask: []u1, target: u32, fillcount: u32, searchstart: u32, count: *u32) void { + var last: u32 = 0; + if (fillcount > 0) { + last = arrangement[fillcount - 1]; + } + + if (last == target) { + count.* += 1; + return; + } + + for (adapters[searchstart..]) |adap, i| { + if (adaptermask[searchstart + i] == 1) continue; + if (adap > last + 3) break; + + adaptermask[searchstart + i] = 1; + arrangement[fillcount] = adap; + countArrangements(arrangement, adapters, adaptermask, target, fillcount + 1, searchstart + @intCast(u32, i) + 1, count); + adaptermask[searchstart + i] = 0; + } +} + +fn part2Recursive(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { + var target: u32 = 0; + + var intlist = std.ArrayList(u32).init(allocator); + defer intlist.deinit(); + + var lineit = std.mem.tokenize(input, "\n"); + while (lineit.next()) |line| { + var val = try std.fmt.parseInt(u32, line, 10); + if (val > target) { + target = val; + } + try intlist.append(val); + } + + target += 3; + try intlist.append(target); + + if (intlist.items.len == 0) return; + + std.sort.sort(u32, intlist.items, {}, comptime std.sort.asc(u32)); + + var count: u32 = 0; + + var arrangements = try allocator.alloc(u32, intlist.items.len); + defer allocator.free(arrangements); - std.debug.print("{}\n", .{answer}); + var adaptermask = try allocator.alloc(u1, intlist.items.len); + defer allocator.free(adaptermask); + + countArrangements(arrangements, intlist.items, adaptermask, target, 0, 0, &count); + + std.debug.print("{}\n", .{count}); +} + +fn groupPermutations(adapters: []const u32) u64 { + if (adapters.len == 1) return 1; + var count: u64 = 0; + for (adapters) |v, i| { + if (i == 0) continue; + if (adapters[i] <= adapters[0] + 3) { + count += groupPermutations(adapters[i..]); + } + } + return count; +} + +fn part2Iterative(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { + var target: u32 = 0; + + var intlist = std.ArrayList(u32).init(allocator); + defer intlist.deinit(); + + try intlist.append(0); + + var lineit = std.mem.tokenize(input, "\n"); + while (lineit.next()) |line| { + var val = try std.fmt.parseInt(u32, line, 10); + if (val > target) { + target = val; + } + try intlist.append(val); + } + + target += 3; + try intlist.append(target); + + if (intlist.items.len == 0) return aoc.Error.InvalidInput; + + std.sort.sort(u32, intlist.items, {}, comptime std.sort.asc(u32)); + + var gapstart: u32 = 0; + var multiplier: u128 = 1; + + var i: u32 = 0; + while (i < intlist.items.len - 1) : (i += 1) { + // whenever we encounter a 3 jolt gap, we can calc permutations of group before + if (i + 1 < intlist.items.len and intlist.items[i + 1] == intlist.items[i] + 3) { + multiplier *= groupPermutations(intlist.items[gapstart .. i + 1]); + std.debug.print("gap from {} to {} => {}\n", .{ intlist.items[i], intlist.items[i + 1], multiplier }); + std.debug.print("group size: {}\n", .{i + 1 - gapstart}); + gapstart = i + 1; + } + } + + std.debug.print("{}\n", .{multiplier}); +} + +fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { + try part2Iterative(allocator, input, args); } pub const main = aoc.gen_main(part1, part2); diff --git a/src/day10/out b/src/day10/out @@ -0,0 +1,31 @@ +1 +2 +3 +4 +7 +8 +9 +10 +11 +14 +17 +18 +19 +20 +23 +24 +25 +28 +31 +32 +33 +34 +35 +38 +39 +42 +45 +46 +47 +48 +49 +\ No newline at end of file diff --git a/src/day10/part2 b/src/day10/part2 @@ -0,0 +1,65 @@ +--- Part Two --- + +To completely determine whether you have enough adapters, you'll need to figure out how many +different ways they can be arranged. Every arrangement needs to connect the charging outlet to your +device. The previous rules about when adapters can successfully connect still apply. + +The first example above (the one that starts with 16, 10, 15) supports the following arrangements: + +(0), 1, 4, 5, 6, 7, 10, 11, 12, 15, 16, 19, (22) +(0), 1, 4, 5, 6, 7, 10, 12, 15, 16, 19, (22) +(0), 1, 4, 5, 7, 10, 11, 12, 15, 16, 19, (22) +(0), 1, 4, 5, 7, 10, 12, 15, 16, 19, (22) +(0), 1, 4, 6, 7, 10, 11, 12, 15, 16, 19, (22) +(0), 1, 4, 6, 7, 10, 12, 15, 16, 19, (22) +(0), 1, 4, 7, 10, 11, 12, 15, 16, 19, (22) +(0), 1, 4, 7, 10, 12, 15, 16, 19, (22) + +(The charging outlet and your device's built-in adapter are shown in parentheses.) Given the +adapters from the first example, the total number of arrangements that connect the charging outlet +to your device is 8. + +The second example above (the one that starts with 28, 33, 18) has many arrangements. Here are a +few: + +(0), 1, 2, 3, 4, 7, 8, 9, 10, 11, 14, 17, 18, 19, 20, 23, 24, 25, 28, 31, +32, 33, 34, 35, 38, 39, 42, 45, 46, 47, 48, 49, (52) + +(0), 1, 2, 3, 4, 7, 8, 9, 10, 11, 14, 17, 18, 19, 20, 23, 24, 25, 28, 31, +32, 33, 34, 35, 38, 39, 42, 45, 46, 47, 49, (52) + +(0), 1, 2, 3, 4, 7, 8, 9, 10, 11, 14, 17, 18, 19, 20, 23, 24, 25, 28, 31, +32, 33, 34, 35, 38, 39, 42, 45, 46, 48, 49, (52) + +(0), 1, 2, 3, 4, 7, 8, 9, 10, 11, 14, 17, 18, 19, 20, 23, 24, 25, 28, 31, +32, 33, 34, 35, 38, 39, 42, 45, 46, 49, (52) + +(0), 1, 2, 3, 4, 7, 8, 9, 10, 11, 14, 17, 18, 19, 20, 23, 24, 25, 28, 31, +32, 33, 34, 35, 38, 39, 42, 45, 47, 48, 49, (52) + +(0), 3, 4, 7, 10, 11, 14, 17, 20, 23, 25, 28, 31, 34, 35, 38, 39, 42, 45, +46, 48, 49, (52) + +(0), 3, 4, 7, 10, 11, 14, 17, 20, 23, 25, 28, 31, 34, 35, 38, 39, 42, 45, +46, 49, (52) + +(0), 3, 4, 7, 10, 11, 14, 17, 20, 23, 25, 28, 31, 34, 35, 38, 39, 42, 45, +47, 48, 49, (52) + +(0), 3, 4, 7, 10, 11, 14, 17, 20, 23, 25, 28, 31, 34, 35, 38, 39, 42, 45, +47, 49, (52) + +(0), 3, 4, 7, 10, 11, 14, 17, 20, 23, 25, 28, 31, 34, 35, 38, 39, 42, 45, +48, 49, (52) + +In total, this set of adapters can connect the charging outlet to your device in 19208 +distinct arrangements. + +You glance back down at your bag and try to remember why you brought so many adapters; there must be +more than a trillion valid ways to arrange them! Surely, there must be an efficient way +to count the arrangements. + +What is the total number of distinct ways you can arrange the adapters to connect the +charging outlet to your device? + + diff --git a/src/day10/test-input b/src/day10/test-input @@ -0,0 +1,31 @@ +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 diff --git a/src/day10/test-input2 b/src/day10/test-input2 @@ -0,0 +1,11 @@ +16 +10 +15 +5 +1 +11 +7 +19 +6 +12 +4