aoc-2020-zig

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

commit 9282e95e8844afe856ba76ceb6d2c3010df8bb1a
parent 2b5d4232879dc74491dabf54a0ddc958d66ebcec
Author: Louis Burda <quent.burda@gmail.com>
Date:   Sat,  8 Apr 2023 12:40:30 -0400

Restructure repo and update solutions to zig 0.10.1

Diffstat:
D.gitignore | 3---
D.gitmodules | 3---
AREADME.md | 5+++++
Ddata/helper/config | 13-------------
Ddata/helper/init.sh | 12------------
Ddata/helper/scripts/build | 7-------
Ddata/helper/scripts/run | 4----
Ddata/helper/scripts/test | 7-------
Ddata/helper/template/main.zig | 16----------------
Ddocs/README.md | 5-----
Dhelper | 1-
Dlib/aoc.zig | 122-------------------------------------------------------------------------------
Dlib/console8.zig | 91-------------------------------------------------------------------------------
Dlib/input.zig | 1-
Asrc/.gitignore | 3+++
Rsrc/day1/input -> src/01/input | 0
Asrc/01/main.zig | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/day1/part1 -> src/01/part1 | 0
Rsrc/day1/part2 -> src/01/part2 | 0
Rsrc/day2/input -> src/02/input | 0
Asrc/02/main.zig | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/day2/part1 -> src/02/part1 | 0
Rsrc/day2/part2 -> src/02/part2 | 0
Rsrc/day3/input -> src/03/input | 0
Asrc/03/main.zig | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/day3/part1 -> src/03/part1 | 0
Rsrc/day3/part2 -> src/03/part2 | 0
Rsrc/day4/input -> src/04/input | 0
Asrc/04/main.zig | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/day4/part1 -> src/04/part1 | 0
Rsrc/day4/part2 -> src/04/part2 | 0
Rsrc/day5/input -> src/05/input | 0
Asrc/05/main.zig | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/day5/part1 -> src/05/part1 | 0
Rsrc/day5/part2 -> src/05/part2 | 0
Rsrc/day6/input -> src/06/input | 0
Asrc/06/main.zig | 39+++++++++++++++++++++++++++++++++++++++
Rsrc/day6/part1 -> src/06/part1 | 0
Rsrc/day6/part2 -> src/06/part2 | 0
Rsrc/day7/input -> src/07/input | 0
Asrc/07/main.zig | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/day7/part1 -> src/07/part1 | 0
Rsrc/day7/part2 -> src/07/part2 | 0
Rsrc/day8/input -> src/08/input | 0
Asrc/08/main.zig | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/day8/part1 -> src/08/part1 | 0
Rsrc/day8/part2 -> src/08/part2 | 0
Rsrc/day9/input -> src/09/input | 0
Asrc/09/main.zig | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/day9/part1 -> src/09/part1 | 0
Rsrc/day9/part2 -> src/09/part2 | 0
Rsrc/day10/input -> src/10/input | 0
Asrc/10/main.zig | 119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/day10/out -> src/10/out | 0
Rsrc/day10/part1 -> src/10/part1 | 0
Rsrc/day10/part2 -> src/10/part2 | 0
Rsrc/day10/test-input -> src/10/test-input | 0
Rsrc/day10/test-input2 -> src/10/test-input2 | 0
Rsrc/day11/input -> src/11/input | 0
Asrc/11/main.zig | 169+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/day11/part1 -> src/11/part1 | 0
Rsrc/day11/part2 -> src/11/part2 | 0
Rsrc/day12/input -> src/12/input | 0
Rsrc/day12/input-test -> src/12/input-test | 0
Asrc/12/main.zig | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/day12/part1 -> src/12/part1 | 0
Rsrc/day12/part2 -> src/12/part2 | 0
Rsrc/day13/input -> src/13/input | 0
Rsrc/day13/input-test -> src/13/input-test | 0
Asrc/13/main.zig | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/day13/notes -> src/13/notes | 0
Rsrc/day13/part1 -> src/13/part1 | 0
Rsrc/day13/part2 -> src/13/part2 | 0
Rsrc/day14/input -> src/14/input | 0
Rsrc/day14/input-test -> src/14/input-test | 0
Rsrc/day14/input-test2 -> src/14/input-test2 | 0
Rsrc/day14/input-test3 -> src/14/input-test3 | 0
Asrc/14/main.zig | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/day14/part1 -> src/14/part1 | 0
Rsrc/day14/part2 -> src/14/part2 | 0
Rsrc/day15/input -> src/15/input | 0
Asrc/15/main.zig | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/day15/part1 -> src/15/part1 | 0
Rsrc/day15/part2 -> src/15/part2 | 0
Rsrc/day16/input -> src/16/input | 0
Rsrc/day16/input-test -> src/16/input-test | 0
Asrc/16/main.zig | 179+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/day16/part1 -> src/16/part1 | 0
Rsrc/day16/part2 -> src/16/part2 | 0
Rsrc/day17/input -> src/17/input | 0
Rsrc/day17/input-test -> src/17/input-test | 0
Asrc/17/main.zig | 195+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/day17/part1 -> src/17/part1 | 0
Rsrc/day17/part2 -> src/17/part2 | 0
Rsrc/day18/input -> src/18/input | 0
Rsrc/day18/input-test -> src/18/input-test | 0
Rsrc/day18/input-test2 -> src/18/input-test2 | 0
Asrc/18/main.zig | 177+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/day18/part1 -> src/18/part1 | 0
Rsrc/day18/part2 -> src/18/part2 | 0
Rsrc/day19/input -> src/19/input | 0
Rsrc/day19/input-test -> src/19/input-test | 0
Rsrc/day19/input-test2 -> src/19/input-test2 | 0
Rsrc/day19/input-test3 -> src/19/input-test3 | 0
Asrc/19/main.zig | 181+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/day19/part1 -> src/19/part1 | 0
Rsrc/day19/part2 -> src/19/part2 | 0
Rsrc/day19/trace -> src/19/trace | 0
Rsrc/day20/input -> src/20/input | 0
Rsrc/day20/input-test -> src/20/input-test | 0
Rsrc/day20/input-test2 -> src/20/input-test2 | 0
Asrc/20/main.zig | 470+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/day20/part1 -> src/20/part1 | 0
Rsrc/day20/part2 -> src/20/part2 | 0
Rsrc/day21/input -> src/21/input | 0
Rsrc/day21/input-test -> src/21/input-test | 0
Asrc/21/main.zig | 188+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/day21/part1 -> src/21/part1 | 0
Rsrc/day21/part2 -> src/21/part2 | 0
Rsrc/day22/input -> src/22/input | 0
Rsrc/day22/input-test -> src/22/input-test | 0
Rsrc/day22/input-test2 -> src/22/input-test2 | 0
Asrc/22/main.zig | 181+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/day22/part1 -> src/22/part1 | 0
Rsrc/day22/part2 -> src/22/part2 | 0
Rsrc/day23/input -> src/23/input | 0
Rsrc/day23/input-test -> src/23/input-test | 0
Asrc/23/main.zig | 194+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/day23/notes -> src/23/notes | 0
Rsrc/day23/part1 -> src/23/part1 | 0
Rsrc/day23/part2 -> src/23/part2 | 0
Rsrc/day24/input -> src/24/input | 0
Rsrc/day24/input-test -> src/24/input-test | 0
Asrc/24/main.zig | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/day24/part1 -> src/24/part1 | 0
Rsrc/day24/part2 -> src/24/part2 | 0
Rsrc/day25/input -> src/25/input | 0
Rsrc/day25/input-test -> src/25/input-test | 0
Asrc/25/main.zig | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/day25/part1 -> src/25/part1 | 0
Rsrc/day25/part2 -> src/25/part2 | 0
Asrc/Makefile | 21+++++++++++++++++++++
Asrc/common/aoc.zig | 145+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/common/console8.zig | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/day1/main.zig | 67-------------------------------------------------------------------
Dsrc/day10/main.zig | 154-------------------------------------------------------------------------------
Dsrc/day11/main.zig | 161-------------------------------------------------------------------------------
Dsrc/day12/main.zig | 60------------------------------------------------------------
Dsrc/day13/main.zig | 105-------------------------------------------------------------------------------
Dsrc/day14/main.zig | 118-------------------------------------------------------------------------------
Dsrc/day15/main.zig | 55-------------------------------------------------------
Dsrc/day16/main.zig | 176-------------------------------------------------------------------------------
Dsrc/day17/main.zig | 193-------------------------------------------------------------------------------
Dsrc/day18/main.zig | 173-------------------------------------------------------------------------------
Dsrc/day19/main.zig | 177-------------------------------------------------------------------------------
Dsrc/day2/main.zig | 57---------------------------------------------------------
Dsrc/day20/main.zig | 463-------------------------------------------------------------------------------
Dsrc/day21/main.zig | 180-------------------------------------------------------------------------------
Dsrc/day22/main.zig | 180-------------------------------------------------------------------------------
Dsrc/day23/main.zig | 180-------------------------------------------------------------------------------
Dsrc/day24/main.zig | 121-------------------------------------------------------------------------------
Dsrc/day25/main.zig | 56--------------------------------------------------------
Dsrc/day3/main.zig | 73-------------------------------------------------------------------------
Dsrc/day4/main.zig | 100-------------------------------------------------------------------------------
Dsrc/day5/main.zig | 46----------------------------------------------
Dsrc/day6/main.zig | 33---------------------------------
Dsrc/day7/main.zig | 105-------------------------------------------------------------------------------
Dsrc/day8/main.zig | 60------------------------------------------------------------
Dsrc/day9/main.zig | 74--------------------------------------------------------------------------
169 files changed, 3532 insertions(+), 3452 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,3 +0,0 @@ -main -cache -zig-cache diff --git a/.gitmodules b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "helper"] - path = helper - url = git@github.com:Sinitax/aoc-helper.git diff --git a/README.md b/README.md @@ -0,0 +1,5 @@ +## Advent Of Code 2020 + +Solutions to the annual [coding advent calendar](https://adventofcode.com) written in [Zig](https://ziglang.org/). + +The version of zig used to compile the code is: `0.10.1` diff --git a/data/helper/config b/data/helper/config @@ -1,13 +0,0 @@ -#!/bin/bash - -# year you are solving problems for -export AOCYEAR=2020 - -# directory you want day directories to be prepared in (format: src/dayN/..) -export SRCDIR="src" - -# specify what files to copy to your day directory on prepare -export TEMPLATE_DIR="data/helper/template" -export TEMPLATE_FILES=" -main.zig:main.zig -" diff --git a/data/helper/init.sh b/data/helper/init.sh @@ -1,12 +0,0 @@ -#!/bin/bash - -REPOROOT=$(git rev-parse --show-toplevel) - -set -e - -ln -sf "$REPOROOT/data/helper/config" "$REPOROOT/helper/data/config" -for f in "$REPOROOT/data/helper/scripts"/*; do - ln -sf "$f" "$REPOROOT/helper/scripts" -done - -echo "done" diff --git a/data/helper/scripts/build b/data/helper/scripts/build @@ -1,7 +0,0 @@ -#!/bin/bash - -[ ! -e "$SOLUTIONS_ROOT/cache" ] && mkdir "$SOLUTIONS_ROOT/cache" -zig build-exe --cache-dir "$SOLUTIONS_ROOT/cache" \ - --pkg-begin "console8" "$SOLUTIONS_ROOT/lib/console8.zig" --pkg-end \ - --pkg-begin "aoc" "$SOLUTIONS_ROOT/lib/aoc.zig" --pkg-end \ - main.zig diff --git a/data/helper/scripts/run b/data/helper/scripts/run @@ -1,4 +0,0 @@ -#!/bin/bash - -[ ! -e "main" ] && $HELPER_ROOT/scripts/build -$PWD/main $@ diff --git a/data/helper/scripts/test b/data/helper/scripts/test @@ -1,7 +0,0 @@ -#!/bin/bash - -[ ! -e "$SOLUTIONS_ROOT/cache" ] && mkdir "$SOLUTIONS_ROOT/cache" -zig test --cache-dir "$SOLUTIONS_ROOT/cache" \ - --pkg-begin "console8" "$SOLUTIONS_ROOT/lib/console8.zig" --pkg-end \ - --pkg-begin "aoc" "$SOLUTIONS_ROOT/lib/aoc.zig" --pkg-end \ - main.zig diff --git a/data/helper/template/main.zig b/data/helper/template/main.zig @@ -1,16 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var answer: u32 = 0; - - std.debug.print("{}\n", .{answer}); -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var answer: u32 = 0; - - std.debug.print("{}\n", .{answer}); -} - -pub const main = aoc.gen_main(part1, part2); diff --git a/docs/README.md b/docs/README.md @@ -1,5 +0,0 @@ -## Advent Of Code 2020 - -Solutions to the annual [coding advent calendar](https://adventofcode.com) written in [Zig](https://ziglang.org/). - -The version of zig used to compile to code is: `0.7.0+d4c167f3c` diff --git a/helper b/helper @@ -1 +0,0 @@ -Subproject commit 6417aa3043784125bd6630152d34ce48617ea852 diff --git a/lib/aoc.zig b/lib/aoc.zig @@ -1,122 +0,0 @@ -const std = @import("std"); -pub const input = @import("input.zig"); - -pub const Error = error{InvalidInput}; - -pub var debug = false; -pub var debuglvl: u32 = 0; - -const part_type = fn (alloc: *std.mem.Allocator, input: []u8, args: [][]u8) anyerror!void; -pub fn gen_main(comptime part1: part_type, comptime part2: part_type) fn () anyerror!void { - const impl = struct { - fn main() !void { - // create a default allocator - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); - var heapalloc = &gpa.allocator; - - // parse args - const args = try std.process.argsAlloc(heapalloc); - defer heapalloc.free(args); - 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) continue; - if (std.mem.eql(u8, kv[0..sep], "AOCINPUT")) { - filename = kv[sep + 1 ..]; - std.debug.print("Using input file: {}\n", .{filename}); - break; - } else if (std.mem.eql(u8, kv[0..sep], "AOCDEBUG")) { - debug = true; - debuglvl = try std.fmt.parseInt(u32, kv[sep + 1 ..], 10); - } - } - } - - // read all input into mem (files are always small so no problem) - const file = try std.fs.cwd().openFile(filename, .{}); - const text = try file.reader().readAllAlloc(heapalloc, std.math.maxInt(u32)); - defer heapalloc.free(text); - - // exec part - try switch (part) { - 1 => part1(heapalloc, text, args[2..]), - 2 => part2(heapalloc, text, args[2..]), - else => std.debug.print("Invalid part number!\n", .{}), - }; - } - }; - return impl.main; -} - -pub const Pos = struct { - x: i64, - y: i64, - const Self = @This(); - - pub fn add(self: Self, other: Self) Self { - return Self{ .x = self.x + other.x, .y = self.y + other.y }; - } - - pub fn mult(self: Self, val: i64) Self { - return Self{ .x = self.x * val, .y = self.y * val }; - } -}; - -pub const Dir = struct { - pub const East = Pos{ .x = 1, .y = 0 }; - pub const West = Pos{ .x = -1, .y = 0 }; - pub const South = Pos{ .x = 0, .y = -1 }; - pub const North = Pos{ .x = 0, .y = 1 }; - - pub const Name = enum { - NORTH = 0, EAST = 1, SOUTH = 2, WEST = 3 - }; - pub const dirs = [_]Pos{ North, East, South, West }; - - pub fn get(name: Name) Pos { - return dirs[@enumToInt(name)]; - } - - pub fn nextCW(dir: usize, offset: usize) usize { - return (dir + @intCast(u32, offset)) % @intCast(u32, dirs.len); - } - - pub fn nextCCW(dir: usize, offset: usize) usize { - const constrained = offset % dirs.len; - if (dir >= constrained) { - return dir - constrained; - } else { - return dirs.len - (constrained - dir); - } - } - - const cos90vs = [_]i32{ 1, 0, -1, 0 }; - const sin90vs = [_]i32{ 0, 1, 0, -1 }; - - pub fn rotCW(pos: Pos, offset: usize) Pos { - const constrained = (4 - offset % 4) % 4; - return Pos{ - .x = cos90vs[constrained] * pos.x - sin90vs[constrained] * pos.y, - .y = sin90vs[constrained] * pos.x + cos90vs[constrained] * pos.y, - }; - } - - pub fn rotCCW(pos: Pos, offset: usize) Pos { - const constrained = offset % 4; - std.debug.print("{}\n", .{constrained}); - return Pos{ - .x = cos90vs[constrained] * pos.x - sin90vs[constrained] * pos.y, - .y = sin90vs[constrained] * pos.x + cos90vs[constrained] * pos.y, - }; - } -}; - -pub fn assertV(v: anytype) !@TypeOf(v.?) { - if (v == null) return Error.InvalidInput; - return v.?; -} diff --git a/lib/console8.zig b/lib/console8.zig @@ -1,91 +0,0 @@ -const std = @import("std"); - -pub const OpError = error{ InstructionPointerOOB, InvalidFormat, InstructionUnknown }; -pub const OpFuncSig = fn (ctx: *Console, arg: i64) OpError!void; -pub const Instruction = struct { - opcode: []const u8, opfunc: OpFuncSig, argval: i64 -}; - -pub const Console = struct { - accumulator: i64 = 0, - instructptr: u64 = 0, - - jumpAddr: i65 = 0, - - code: []const u8, - instlist: [][]const u8, - allocator: *std.mem.Allocator, - const Self = @This(); - - pub fn init(code: []const u8, allocator: *std.mem.Allocator) !Self { - var instvec = std.ArrayList([]const u8).init(allocator); - errdefer instvec.deinit(); - - var instit = std.mem.tokenize(code, "\n"); - while (instit.next()) |inst| { - try instvec.append(inst); - } - return Console{ - .code = code, - .instlist = instvec.toOwnedSlice(), - .allocator = allocator, - }; - } - - pub fn deinit(self: *Self) void { - self.allocator.free(self.instlist); - } - - pub fn reset(self: *Self) void { - self.accumulator = 0; - self.instructptr = 0; - } - - const instructionMap = std.ComptimeStringMap(OpFuncSig, .{ - .{ "jmp", jumpInstruction }, - .{ "acc", accInstruction }, - .{ "nop", nopInstruction }, - }); - - pub fn jumpInstruction(self: *Self, arg: i64) OpError!void { - self.jumpAddr = @intCast(i65, self.instructptr) + @intCast(i65, arg); - if (self.jumpAddr < 0 or self.jumpAddr >= self.instlist.len) - return error.InstructionPointerOOB; - self.instructptr = @intCast(u64, self.jumpAddr); - } - - pub fn accInstruction(self: *Self, arg: i64) OpError!void { - self.accumulator += arg; - self.instructptr += 1; - } - - pub fn nopInstruction(self: *Self, arg: i64) OpError!void { - self.instructptr += 1; - } - - pub fn parseNext(self: *Self) !?Instruction { - if (self.instructptr >= self.instlist.len) - return null; - - const inststr = self.instlist[self.instructptr]; - const sep = std.mem.indexOfScalar(u8, inststr, ' '); - if (sep == null) return OpError.InvalidFormat; - - const opcode = inststr[0..sep.?]; - if (instructionMap.get(opcode)) |opfunc| { - const arg = inststr[sep.? + 1 ..]; - const val = std.fmt.parseInt(i64, arg, 10) catch |err| { - std.debug.print("Failed to parse arg value: {}\n", .{arg}); - return OpError.InvalidFormat; - }; - return Instruction{ .opcode = opcode, .opfunc = opfunc, .argval = val }; - } else { - std.debug.print("Unknown instruction: {}\n", .{inststr}); - return OpError.InstructionUnknown; - } - } - - pub fn exec(self: *Self, inst: *Instruction) !void { - try inst.opfunc(self, inst.argval); - } -}; diff --git a/lib/input.zig b/lib/input.zig @@ -1 +0,0 @@ -const std = @import("std"); diff --git a/src/.gitignore b/src/.gitignore @@ -0,0 +1,3 @@ +main +main.o +.cache diff --git a/src/day1/input b/src/01/input diff --git a/src/01/main.zig b/src/01/main.zig @@ -0,0 +1,72 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const Parts = struct { a: u32, b: u32 }; + +fn findparts(intlist: *const std.ArrayList(u32), sum: u32) ?Parts { + var start: usize = 0; + const items = intlist.items; + var end: usize = items.len - 1; + while (start != end) { + const csum = items[start] + items[end]; + if (csum == sum) { + return Parts{ .a = items[start], .b = items[end] }; + } else if (csum > sum) { + end -= 1; + } else { + start += 1; + } + } + return null; +} + +fn parse(allocator: std.mem.Allocator, input: []u8) !std.ArrayList(u32) { + var intlist = std.ArrayList(u32).init(allocator); + errdefer intlist.deinit(); + + var it = std.mem.tokenize(u8, input, "\n"); + while (it.next()) |line| { + try intlist.append(try std.fmt.parseInt(u32, line, 10)); + } + + return intlist; +} + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + const intlist = try parse(allocator, input); + defer intlist.deinit(); + + std.sort.sort(u32, intlist.items, {}, comptime std.sort.asc(u32)); + + if (findparts(&intlist, 2020)) |parts| { + return try std.fmt.allocPrint(allocator, "{d}", .{parts.a * parts.b}); + } + + return null; +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + const intlist = try parse(allocator, input); + defer intlist.deinit(); + + std.sort.sort(u32, intlist.items, {}, comptime std.sort.asc(u32)); + + const target: u16 = 2020; + var third: u32 = 0; + while (third < intlist.items.len) : (third += 1) { + const tmp = intlist.items[third]; + intlist.items[third] = target + 1; + if (findparts(&intlist, target - tmp)) |parts| { + return try std.fmt.allocPrint(allocator, "{d}", .{parts.a * parts.b * tmp}); + } + intlist.items[third] = tmp; + } + + return null; +} + +pub const main = aoc.main(part1, part2, .{ "658899", "155806250" }); diff --git a/src/day1/part1 b/src/01/part1 diff --git a/src/day1/part2 b/src/01/part2 diff --git a/src/day2/input b/src/02/input diff --git a/src/02/main.zig b/src/02/main.zig @@ -0,0 +1,61 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const Entry = struct { a: u16, b: u16, char: u8, pass: []u8 }; + +fn freeEntries(allocator: std.mem.Allocator, entries: *const std.ArrayList(Entry)) void { + for (entries.items) |item| + allocator.free(item.pass); + entries.deinit(); +} + +fn parse(allocator: std.mem.Allocator, input: []u8) !std.ArrayList(Entry) { + var entries = std.ArrayList(Entry).init(allocator); + errdefer freeEntries(allocator, &entries); + + var lineit = std.mem.tokenize(u8, input, "\n"); + while (lineit.next()) |line| { + var entry: Entry = undefined; + var it = std.mem.tokenize(u8, line, " -"); + entry.a = try std.fmt.parseInt(u16, it.next().?, 10); + entry.b = try std.fmt.parseInt(u16, it.next().?, 10); + entry.char = it.next().?[0]; + entry.pass = try allocator.dupe(u8, it.next().?); + try entries.append(entry); + } + + return entries; +} + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + const entries = try parse(allocator, input); + defer freeEntries(allocator, &entries); + + var valid: u32 = 0; + for (entries.items) |entry| { + var count: u16 = 0; + for (entry.pass) |c| count += @boolToInt(c == entry.char); + valid += @boolToInt(count >= entry.a and count <= entry.b); + } + + return try std.fmt.allocPrint(allocator, "{d}", .{valid}); +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + const entries = try parse(allocator, input); + defer freeEntries(allocator, &entries); + + var valid: u32 = 0; + for (entries.items) |entry| { + valid += @boolToInt((entry.pass[entry.a - 1] == entry.char) != + (entry.pass[entry.b - 1] == entry.char)); + } + + return try std.fmt.allocPrint(allocator, "{d}", .{valid}); +} + +pub const main = aoc.main(part1, part2, .{ "603", "404" }); diff --git a/src/day2/part1 b/src/02/part1 diff --git a/src/day2/part2 b/src/02/part2 diff --git a/src/day3/input b/src/03/input diff --git a/src/03/main.zig b/src/03/main.zig @@ -0,0 +1,77 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const Entry = []u1; +const Vector = struct { x: u16, y: u16 }; + +fn freeEntries(allocator: std.mem.Allocator, entries: *const std.ArrayList(Entry)) void { + for (entries.items) |entry| { + allocator.free(entry); + } + entries.deinit(); +} + +fn parse(allocator: std.mem.Allocator, input: []u8) !std.ArrayList(Entry) { + var entries = std.ArrayList(Entry).init(allocator); + errdefer freeEntries(allocator, &entries); + + var lineit = std.mem.tokenize(u8, input, "\n"); + while (lineit.next()) |line| { + var linemap: []u1 = try allocator.alloc(u1, line.len); + errdefer allocator.free(linemap); + + for (line) |c, i| { + linemap[i] = @boolToInt(c == '#'); + } + try entries.append(linemap); + } + + return entries; +} + +fn treesHit(map: std.ArrayList(Entry), slope: Vector) u16 { + var count: u16 = 0; + var pos = Vector{ .x = 0, .y = 0 }; + while (pos.y < map.items.len - 1) { + pos.x += slope.x; + pos.y += slope.y; + + count += map.items[pos.y][pos.x % map.items[0].len]; + } + return count; +} + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + const map = try parse(allocator, input); + defer freeEntries(allocator, &map); + + const answer = treesHit(map, Vector{ .x = 3, .y = 1 }); + + return try std.fmt.allocPrint(allocator, "{d}", .{answer}); +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + const map = try parse(allocator, input); + defer freeEntries(allocator, &map); + + const slopes = [_]Vector{ + Vector{ .x = 1, .y = 1 }, + Vector{ .x = 3, .y = 1 }, + Vector{ .x = 5, .y = 1 }, + Vector{ .x = 7, .y = 1 }, + Vector{ .x = 1, .y = 2 }, + }; + + var answer: u32 = 1; + for (slopes) |slope| { + answer *= treesHit(map, slope); + } + + return try std.fmt.allocPrint(allocator, "{d}", .{answer}); +} + +pub const main = aoc.main(part1, part2, .{ "282", "958815792" }); diff --git a/src/day3/part1 b/src/03/part1 diff --git a/src/day3/part2 b/src/03/part2 diff --git a/src/day4/input b/src/04/input diff --git a/src/04/main.zig b/src/04/main.zig @@ -0,0 +1,108 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +fn intChecker( + comptime min: u32, + comptime max: u32, + comptime len: ?u32, + comptime suffix: ?[]const u8, +) fn ([]const u8) bool { + const impl = struct { + fn check(input: []const u8) bool { + var parsed = input; + if (suffix) |suf| { + if (!std.mem.eql(u8, suf, input[(input.len - suf.len)..])) + return false; + parsed = input[0..(input.len - suf.len)]; + } + if (len != null and parsed.len != len.?) + return false; + const val = std.fmt.parseInt(u32, parsed, 10) catch { + return false; + }; + return (val >= min and val <= max); + } + }; + return impl.check; +} + +fn combineOr( + comptime f1: fn ([]const u8) bool, + comptime f2: fn ([]const u8) bool, +) fn ([]const u8) bool { + const impl = struct { + fn check(input: []const u8) bool { + return f1(input) or f2(input); + } + }; + return impl.check; +} + +fn isColorStr(input: []const u8) bool { + if (input.len != 7) return false; + _ = std.fmt.parseInt(u32, input[1..], 16) catch { + return false; + }; + return input[0] == '#'; +} + +fn isEyeColor(input: []const u8) bool { + const valids = "amb blu brn gry grn hzl oth"; + return std.mem.indexOf(u8, valids[0..], input) != null; +} + +fn countValids(input: []u8, validate: bool) !u16 { + var entryit = std.mem.split(u8, input, "\n\n"); + const required_keys = [_][]const u8{ "byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid", "cid" }; + const validation_funcs = [required_keys.len]?*const fn ([]const u8) bool{ + intChecker(1920, 2002, 4, null), + intChecker(2010, 2020, 4, null), + intChecker(2020, 2030, 4, null), + combineOr(intChecker(150, 193, 3, "cm"), intChecker(59, 76, 2, "in")), + isColorStr, + isEyeColor, + intChecker(0, 999999999, 9, null), + null, + }; + var count: u16 = 0; + entryloop: while (entryit.next()) |entry| { + const key_mask = [required_keys.len]u8{ 1, 1, 1, 1, 1, 1, 1, 'X' }; + var present = [required_keys.len]u1{ 0, 0, 0, 0, 0, 0, 0, 0 }; + var partit = std.mem.tokenize(u8, entry, ": \n"); + while (partit.next()) |key| { + const value = partit.next().?; + for (required_keys) |ckey, i| { + if (std.mem.eql(u8, key, ckey)) { + if (validate and validation_funcs[i] != null) { + if (!validation_funcs[i].?(value)) continue :entryloop; + } + present[i] = 1; + } + } + } + for (key_mask) |k, i| { + if (k != 'X' and present[i] != k) + continue :entryloop; + } + count += 1; + } + return count; +} + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + const answer = try countValids(input, false); + + return try std.fmt.allocPrint(allocator, "{d}", .{answer}); +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + const answer = try countValids(input, true); + + return try std.fmt.allocPrint(allocator, "{d}", .{answer}); +} + +pub const main = aoc.main(part1, part2, .{ "226", "160" }); diff --git a/src/day4/part1 b/src/04/part1 diff --git a/src/day4/part2 b/src/04/part2 diff --git a/src/day5/input b/src/05/input diff --git a/src/05/main.zig b/src/05/main.zig @@ -0,0 +1,52 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const row_count = 128; +const col_count = 8; + +// Input is coded in a way that we can do an implicit binary search by replacing +// identifiers for the lower half with "0" and for the upper half with "1" +// See commit <f1b717029cf3262c1fa2760124af258924d668da> for actual binary search + +fn code2Id(input: []const u8) !u32 { + var value: u32 = 0; + for (input) |c| + value = value * 2 + @boolToInt(c == 'B' or c == 'R'); + return value; +} + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var lineit = std.mem.tokenize(u8, input, "\n"); + var answer: u32 = 0; + while (lineit.next()) |line| { + const id = try code2Id(line[0..10]); + answer = std.math.max(answer, id); + } + + return try std.fmt.allocPrint(allocator, "{d}", .{answer}); +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var lineit = std.mem.tokenize(u8, input, "\n"); + var seats = [_]u1{0} ** (row_count * col_count); + var min: u32 = std.math.inf_u32; + while (lineit.next()) |line| { + const id = try code2Id(line); + min = std.math.min(min, id); + seats[id] = 1; + } + + for (seats[min..]) |_, i| { + if (seats[min + i] == 0) { + return try std.fmt.allocPrint(allocator, "{d}", .{min + i}); + } + } + + return null; +} + +pub const main = aoc.main(part1, part2, .{ "850", "599" }); diff --git a/src/day5/part1 b/src/05/part1 diff --git a/src/day5/part2 b/src/05/part2 diff --git a/src/day6/input b/src/06/input diff --git a/src/06/main.zig b/src/06/main.zig @@ -0,0 +1,39 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var entryit = std.mem.split(u8, input, "\n\n"); + var answer: u32 = 0; + while (entryit.next()) |group| { + var seen = [_]u1{0} ** 256; + for (group) |c| { + if (c == ' ' or c == '\n') continue; + answer += @boolToInt(seen[c] == 0); + seen[c] = 1; + } + } + + return try std.fmt.allocPrint(allocator, "{d}", .{answer}); +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var entryit = std.mem.split(u8, input, "\n\n"); + var answer: u32 = 0; + while (entryit.next()) |group| { + var count = [_]u16{0} ** 256; + var members: u16 = 0; + var memberit = std.mem.tokenize(u8, group, "\n "); + while (memberit.next()) |member| : (members += 1) { + for (member) |c| count[c] += 1; + } + for (count) |v| answer += @boolToInt(v == members); + } + + return try std.fmt.allocPrint(allocator, "{d}", .{answer}); +} + +pub const main = aoc.main(part1, part2, .{ "6748", "3445" }); diff --git a/src/day6/part1 b/src/06/part1 diff --git a/src/day6/part2 b/src/06/part2 diff --git a/src/day7/input b/src/07/input diff --git a/src/07/main.zig b/src/07/main.zig @@ -0,0 +1,108 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const BagCount = struct { id: u64, count: u32 }; + +const BagRule = struct { + outer: u64, + innerit: std.mem.SplitIterator(u8), + + fn from(line: []const u8) BagRule { + var partit = std.mem.split(u8, line, " bags contain "); + const outer = std.hash.CityHash64.hash(partit.next().?); + var innerit = std.mem.split(u8, partit.next().?, ", "); + return BagRule{ .outer = outer, .innerit = innerit }; + } + + fn nextInner(self: *BagRule) !?BagCount { + if (self.innerit.next()) |bagstr| { + if (std.mem.eql(u8, bagstr[0..2], "no")) return null; + const lastsep = std.mem.lastIndexOfScalar(u8, bagstr, ' ').?; + const firstsep = std.mem.indexOfScalar(u8, bagstr, ' ').?; + const count = try std.fmt.parseInt(u32, bagstr[0..firstsep], 10); + const id = std.hash.CityHash64.hash(bagstr[firstsep + 1 .. lastsep]); + return BagCount{ .id = id, .count = count }; + } else { + return null; + } + } +}; + +fn countBagsTotal(map: *std.AutoHashMap(u64, std.ArrayList(BagCount)), outer: u64) u64 { + var sum: u64 = 0; + if (map.get(outer)) |inner| { + for (inner.items) |bag| + sum += bag.count * (1 + countBagsTotal(map, bag.id)); + } + return sum; +} + +fn countParentTypes(map: *std.AutoHashMap(u64, std.ArrayList(u64)), inner: u64, seen: *std.ArrayList(u64)) anyerror!u64 { + var sum: u64 = 0; + if (map.get(inner)) |outer| { + for (outer.items) |bagid| { + if (std.mem.indexOfScalar(u64, seen.items, bagid) == null) { + try seen.append(bagid); + sum += 1 + try countParentTypes(map, bagid, seen); + } + } + } + return sum; +} + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var lineit = std.mem.tokenize(u8, input, "\n"); + var map = std.AutoHashMap(u64, std.ArrayList(u64)).init(allocator); + defer { + var mapit = map.iterator(); + while (mapit.next()) |kv| kv.value_ptr.deinit(); + map.deinit(); + } + + while (lineit.next()) |line| { + var rule = BagRule.from(line); + while (try rule.nextInner()) |inner| { + if (map.get(inner.id) == null) + try map.put(inner.id, std.ArrayList(u64).init(allocator)); + try map.getEntry(inner.id).?.value_ptr.append(rule.outer); + } + } + + const target = std.hash.CityHash64.hash("shiny gold"); + var seen = std.ArrayList(u64).init(allocator); + defer seen.deinit(); + + const answer = try countParentTypes(&map, target, &seen); + + return try std.fmt.allocPrint(allocator, "{d}", .{answer}); +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var lineit = std.mem.tokenize(u8, input, "\n"); + var map = std.AutoHashMap(u64, std.ArrayList(BagCount)).init(allocator); + defer { + var mapit = map.iterator(); + while (mapit.next()) |kv| kv.value_ptr.deinit(); + map.deinit(); + } + + const target = std.hash.CityHash64.hash("shiny gold"); + while (lineit.next()) |line| { + var rule = BagRule.from(line); + if (map.get(rule.outer) == null) + try map.put(rule.outer, std.ArrayList(BagCount).init(allocator)); + while (try rule.nextInner()) |bag| { + try map.getEntry(rule.outer).?.value_ptr.append(bag); + } + } + + const answer = countBagsTotal(&map, target); + + return try std.fmt.allocPrint(allocator, "{d}", .{answer}); +} + +pub const main = aoc.main(part1, part2, .{ "161", "30899" }); diff --git a/src/day7/part1 b/src/07/part1 diff --git a/src/day7/part2 b/src/07/part2 diff --git a/src/day8/input b/src/08/input diff --git a/src/08/main.zig b/src/08/main.zig @@ -0,0 +1,67 @@ +const std = @import("std"); +const aoc = @import("aoc"); +const Console = @import("console8").Console; + +fn runTillRepeat(console: *Console, instlist: *std.ArrayList(u64), swapon: ?u64) !void { + while (try console.parseNext()) |_inst| { + var inst = _inst; + aoc.debugfmt("{:<5}: {s} {d}\n", .{ console.instructptr, inst.opcode, inst.argval }); + if (std.mem.indexOfScalar(u64, instlist.items, console.instructptr) != null) { + return; + } + if (swapon != null and swapon.? == console.instructptr) { + inst.opfunc = switch (inst.opfunc) { + Console.jumpInstruction => Console.nopInstruction, + Console.nopInstruction => Console.jumpInstruction, + else => unreachable, + }; + } + try instlist.append(console.instructptr); + try console.exec(&inst); + } +} + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var console = try Console.init(input, allocator); + defer console.deinit(); + + var instlist = std.ArrayList(u64).init(allocator); + defer instlist.deinit(); + + try runTillRepeat(&console, &instlist, null); + + return try std.fmt.allocPrint(allocator, "{d}", .{console.accumulator}); +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var console = try Console.init(input, allocator); + defer console.deinit(); + + var instlist = std.ArrayList(u64).init(allocator); + defer instlist.deinit(); + + try runTillRepeat(&console, &instlist, null); + + for (instlist.items) |inst| { + if (std.mem.eql(u8, "acc"[0..3], console.instlist[inst][0..3])) + continue; + + var sim_seen = std.ArrayList(u64).init(allocator); + defer sim_seen.deinit(); + + console.reset(); + //std.debug.print("Swapping instruction: {:<5}: {}\n", .{ inst, console.instlist[inst] }); + runTillRepeat(&console, &sim_seen, inst) catch {}; + if (console.jumpAddr == console.instlist.len or console.instructptr == console.instlist.len) { + return try std.fmt.allocPrint(allocator, "{d}", .{console.accumulator}); + } + } + + return null; +} + +pub const main = aoc.main(part1, part2, .{ "1766", "1639" }); diff --git a/src/day8/part1 b/src/08/part1 diff --git a/src/day8/part2 b/src/08/part2 diff --git a/src/day9/input b/src/09/input diff --git a/src/09/main.zig b/src/09/main.zig @@ -0,0 +1,83 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const preamble_size = 25; + +fn preambleHasSum(preamble: []u64, target: u64) bool { + for (preamble) |v1| { + for (preamble) |v2| { + if (v1 != v2 and v1 + v2 == target) + return true; + } + } + return false; +} + +fn getInvalid(allocator: std.mem.Allocator, input: []u8) !u64 { + var preamble = std.ArrayList(u64).init(allocator); + defer preamble.deinit(); + + var i: u64 = 0; + var numit = std.mem.tokenize(u8, input, "\n"); + while (numit.next()) |numstr| : (i += 1) { + const num = try std.fmt.parseInt(u64, numstr, 10); + if (i > preamble_size) { + if (!preambleHasSum(preamble.items, num)) { + return num; + } + preamble.items[(i - 1) % preamble_size] = num; + } else { + try preamble.append(num); + } + } + + return aoc.Error.InvalidInput; +} + +fn listSum(items: []u64) u64 { + var sum: u64 = 0; + for (items) |i| + sum += i; + return sum; +} + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + const answer = try getInvalid(allocator, input); + + return try std.fmt.allocPrint(allocator, "{d}", .{answer}); +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + const invalid = try getInvalid(allocator, input); + + var numset = std.ArrayList(u64).init(allocator); + defer numset.deinit(); + + var numit = std.mem.tokenize(u8, input, "\n"); + while (true) { + const sum = listSum(numset.items); + if (sum < invalid) { + const numstr = numit.next(); + if (numstr == null) break; + const num = try std.fmt.parseInt(u64, numstr.?, 10); + try numset.append(num); + } else if (sum > invalid) { + _ = numset.orderedRemove(0); + } else { + break; + } + } + + if (numset.items.len > 0) { + const answer = std.mem.min(u64, numset.items) + std.mem.max(u64, numset.items); + return try std.fmt.allocPrint(allocator, "{d}", .{answer}); + } + + return null; +} + +pub const main = aoc.main(part1, part2, .{ "466456641", "55732936" }); diff --git a/src/day9/part1 b/src/09/part1 diff --git a/src/day9/part2 b/src/09/part2 diff --git a/src/day10/input b/src/10/input diff --git a/src/10/main.zig b/src/10/main.zig @@ -0,0 +1,119 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var target: u32 = 0; + + var intlist = std.ArrayList(u32).init(allocator); + defer intlist.deinit(); + + var lineit = std.mem.tokenize(u8, 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 null; + + 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) { + aoc.debugfmt("Unable to find a suitable adapter for {} output jolts (next: {} jolts)\n", .{ last, ov }); + return aoc.Error.InvalidInput; + } + diffs[ov - last] += 1; + last = ov; + } + + return try std.fmt.allocPrint(allocator, "{d}", .{diffs[1] * diffs[3]}); +} + +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 groupPermutations(adapters: []const u32) u64 { + if (adapters.len == 1) return 1; + var count: u64 = 0; + for (adapters) |_, i| { + if (i == 0) continue; + if (adapters[i] <= adapters[0] + 3) { + count += groupPermutations(adapters[i..]); + } + } + return count; +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var target: u32 = 0; + + var intlist = std.ArrayList(u32).init(allocator); + defer intlist.deinit(); + + try intlist.append(0); + + var lineit = std.mem.tokenize(u8, 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]); + aoc.debugfmt("gap from {} to {} => {}\n", .{ intlist.items[i], intlist.items[i + 1], multiplier }); + aoc.debugfmt("group size: {}\n", .{i + 1 - gapstart}); + gapstart = i + 1; + } + } + + return try std.fmt.allocPrint(allocator, "{d}", .{multiplier}); +} + +pub const main = aoc.main(part1, part2, .{ "1998", "347250213298688" }); diff --git a/src/day10/out b/src/10/out diff --git a/src/day10/part1 b/src/10/part1 diff --git a/src/day10/part2 b/src/10/part2 diff --git a/src/day10/test-input b/src/10/test-input diff --git a/src/day10/test-input2 b/src/10/test-input2 diff --git a/src/day11/input b/src/11/input diff --git a/src/11/main.zig b/src/11/main.zig @@ -0,0 +1,169 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const SeatState = enum { EMPTY, FLOOR, TAKEN }; +const MapDims = struct { width: usize, height: usize }; +const Dir = struct { x: i8, y: i8 }; +const Pos = struct { x: i128, y: i128 }; + +const adjacent = [_]Dir{ + Dir{ .x = -1, .y = -1 }, + Dir{ .x = -0, .y = -1 }, + Dir{ .x = 1, .y = -1 }, + Dir{ .x = -1, .y = 0 }, + Dir{ .x = 1, .y = 0 }, + Dir{ .x = -1, .y = 1 }, + Dir{ .x = 0, .y = 1 }, + Dir{ .x = 1, .y = 1 }, +}; + +fn parseMap(mapitems: *[]SeatState, dims: *MapDims, input: []const u8, allocator: std.mem.Allocator) !void { + var lineit = std.mem.tokenize(u8, input, "\n"); + var map = std.ArrayList(SeatState).init(allocator); + errdefer map.deinit(); + while (lineit.next()) |line| { + if (dims.width == 0) { + dims.width = line.len; + } else if (dims.width != line.len) { + return aoc.Error.InvalidInput; + } + for (line) |c| { + try map.append(switch (c) { + '#' => SeatState.TAKEN, + 'L' => SeatState.EMPTY, + '.' => SeatState.FLOOR, + else => { + return aoc.Error.InvalidInput; + }, + }); + } + dims.height += 1; + } + mapitems.* = map.toOwnedSlice(); +} + +fn printMap(mapitems: []SeatState, dims: MapDims) void { + for (mapitems) |state, i| { + const c: u8 = switch (state) { + SeatState.EMPTY => 'L', + SeatState.TAKEN => '#', + SeatState.FLOOR => '.', + }; + if (i % dims.width == 0) { + aoc.debugfmt("\n", .{}); + } + aoc.debugfmt("{c}", .{c}); + } +} + +fn simulateStrategyOne(before: []SeatState, after: []SeatState, dims: MapDims) u32 { + var seat_changes: u32 = 0; + for (before) |state, i| { + const p = Pos{ .x = i % dims.width, .y = i / dims.width }; + var count: u32 = 0; + for (adjacent) |ap| { + if (p.x + ap.x >= dims.width or p.x + ap.x < 0 or p.y + ap.y >= dims.height or p.y + ap.y < 0) continue; + const ni = @intCast(u64, @intCast(i64, i) + ap.x + ap.y * @intCast(i64, dims.width)); + count += @boolToInt(before[ni] == SeatState.TAKEN); + } + if (state == SeatState.EMPTY and count == 0) { + after[i] = SeatState.TAKEN; + seat_changes += 1; + } else if (state == SeatState.TAKEN and count >= 4) { + after[i] = SeatState.EMPTY; + seat_changes += 1; + } else { + after[i] = before[i]; + } + } + return seat_changes; +} + +fn simulateStrategyTwo(before: []SeatState, after: []SeatState, dims: MapDims) u32 { + var seat_changes: u32 = 0; + for (before) |state, i| { + const p = Pos{ .x = i % dims.width, .y = i / dims.width }; + var count: u32 = 0; + for (adjacent) |ap| { + var iterp = Pos{ .x = p.x + ap.x, .y = p.y + ap.y }; + while (iterp.x < dims.width and iterp.x >= 0 and iterp.y < dims.height and iterp.y >= 0) { + const ni = @intCast(u64, iterp.x + iterp.y * @intCast(i64, dims.width)); + if (before[ni] == SeatState.TAKEN) { + count += 1; + break; + } else if (before[ni] == SeatState.EMPTY) { + break; + } else { + iterp.x += ap.x; + iterp.y += ap.y; + } + } + } + if (state == SeatState.EMPTY and count == 0) { + after[i] = SeatState.TAKEN; + seat_changes += 1; + } else if (state == SeatState.TAKEN and count >= 5) { + after[i] = SeatState.EMPTY; + seat_changes += 1; + } else { + after[i] = before[i]; + } + } + return seat_changes; +} + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var mapdims = MapDims{ .width = 0, .height = 0 }; + var mapitems: []SeatState = undefined; + try parseMap(&mapitems, &mapdims, input, allocator); + defer allocator.free(mapitems); + + var mapresult = try allocator.alloc(SeatState, mapitems.len); + defer allocator.free(mapresult); + + var round: u32 = 0; + while (simulateStrategyOne(mapitems, mapresult, mapdims) > 0) : (round += 1) { + aoc.debugfmt("\rRound: {}", .{round}); + std.mem.copy(SeatState, mapitems, mapresult); + } + + var taken_count: u64 = 0; + for (mapresult) |state| { + taken_count += @boolToInt(state == SeatState.TAKEN); + } + + aoc.debugfmt("\nSeats Occupied: {}\n", .{taken_count}); + + return try std.fmt.allocPrint(allocator, "{d}", .{taken_count}); +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var mapdims = MapDims{ .width = 0, .height = 0 }; + var mapitems: []SeatState = undefined; + try parseMap(&mapitems, &mapdims, input, allocator); + defer allocator.free(mapitems); + + var mapresult = try allocator.alloc(SeatState, mapitems.len); + defer allocator.free(mapresult); + + var round: u32 = 0; + while (simulateStrategyTwo(mapitems, mapresult, mapdims) > 0) : (round += 1) { + aoc.debugfmt("\rRound: {}", .{round}); + std.mem.copy(SeatState, mapitems, mapresult); + } + + var taken_count: u64 = 0; + for (mapresult) |state| { + taken_count += @boolToInt(state == SeatState.TAKEN); + } + + aoc.debugfmt("\nSeats Occupied: {}\n", .{taken_count}); + + return try std.fmt.allocPrint(allocator, "{d}", .{taken_count}); +} + +pub const main = aoc.main(part1, part2, .{ "2126", "1914" }); diff --git a/src/day11/part1 b/src/11/part1 diff --git a/src/day11/part2 b/src/11/part2 diff --git a/src/day12/input b/src/12/input diff --git a/src/day12/input-test b/src/12/input-test diff --git a/src/12/main.zig b/src/12/main.zig @@ -0,0 +1,67 @@ +const std = @import("std"); +const aoc = @import("aoc"); +const Pos = aoc.Pos; +const Dir = aoc.Dir; + +const Ship = struct { pos: Pos, waypoint: Pos, dir: usize }; + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var ship = Ship{ .pos = Pos{ .x = 0, .y = 0 }, .waypoint = Pos{ .x = 0, .y = 0 }, .dir = 0 }; + var lineit = std.mem.tokenize(u8, input, "\n"); + while (lineit.next()) |line| { + const val = try std.fmt.parseInt(u32, line[1..], 10); + switch (line[0]) { + 'N' => ship.pos = ship.pos.add(Dir.North.mult(val)), + 'S' => ship.pos = ship.pos.add(Dir.South.mult(val)), + 'E' => ship.pos = ship.pos.add(Dir.East.mult(val)), + 'W' => ship.pos = ship.pos.add(Dir.West.mult(val)), + 'L' => ship.dir = Dir.nextCCW(ship.dir, @divExact(val, 90)), + 'R' => ship.dir = Dir.nextCW(ship.dir, @divExact(val, 90)), + 'F' => ship.pos = ship.pos.add(Dir.dirs[ship.dir].mult(val)), + else => { + return aoc.Error.InvalidInput; + }, + } + } + + const answer = std.math.absCast(ship.pos.x) + std.math.absCast(ship.pos.y); + + return try std.fmt.allocPrint(allocator, "{d}", .{answer}); +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var ship = Ship{ .pos = Pos{ .x = 0, .y = 0 }, .waypoint = Pos{ .x = 10, .y = 1 }, .dir = 0 }; + var lineit = std.mem.tokenize(u8, input, "\n"); + while (lineit.next()) |line| { + const val = try std.fmt.parseInt(u32, line[1..], 10); + switch (line[0]) { + 'N' => ship.waypoint = ship.waypoint.add(Dir.North.mult(val)), + 'S' => ship.waypoint = ship.waypoint.add(Dir.South.mult(val)), + 'E' => ship.waypoint = ship.waypoint.add(Dir.East.mult(val)), + 'W' => ship.waypoint = ship.waypoint.add(Dir.West.mult(val)), + 'L' => { + var new = Dir.rotCCW(ship.waypoint, @divExact(val, 90)); + ship.waypoint = new; + }, + 'R' => { + var new = Dir.rotCW(ship.waypoint, @divExact(val, 90)); + ship.waypoint = new; + }, + 'F' => ship.pos = ship.pos.add(ship.waypoint.mult(val)), + else => { + return aoc.Error.InvalidInput; + }, + } + aoc.debugfmt("{s} {} {} {} {}\n", .{ line, ship.waypoint.x, ship.waypoint.y, ship.pos.x, ship.pos.y }); + } + + const answer = std.math.absCast(ship.pos.x) + std.math.absCast(ship.pos.y); + + return try std.fmt.allocPrint(allocator, "{d}", .{answer}); +} + +pub const main = aoc.main(part1, part2, .{ "1191", "61053" }); diff --git a/src/day12/part1 b/src/12/part1 diff --git a/src/day12/part2 b/src/12/part2 diff --git a/src/day13/input b/src/13/input diff --git a/src/day13/input-test b/src/13/input-test diff --git a/src/13/main.zig b/src/13/main.zig @@ -0,0 +1,112 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +fn gcd(a: u64, b: u64) u64 { + var ac = a; + var bc = b; + while (true) { + if (ac > bc) { + ac = ac % bc; + if (ac == 0) return bc; + } else { + bc = bc % ac; + if (bc == 0) return ac; + } + } +} + +fn lcm(a: u64, b: u64) u64 { + return @divExact(a * b, gcd(a, b)); +} + +fn waittime(start: u32, busid: u32) u32 { + return (busid - (start % busid)) % busid; +} + +fn printRegion(buses: []u32, start: u64, end: u64) void { + aoc.debugfmt(" ", .{}); + for (buses) |bus| { + aoc.debugfmt(" {:^5}", .{bus}); + } + aoc.debugfmt("\n", .{}); + + var i = start; + while (i < end) : (i += 1) { + aoc.debugfmt("{:<10}", .{i}); + for (buses) |bus| { + var c: u8 = '.'; + if (bus > 0 and i % bus == 0) c = 'D'; + aoc.debugfmt(" {c} ", .{c}); + } + aoc.debugfmt("\n", .{}); + } + aoc.debugfmt("\n", .{}); +} + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var lineit = std.mem.tokenize(u8, input, "\n"); + var start = try std.fmt.parseInt(u32, lineit.next().?, 10); + + var bestbus: ?u32 = null; + var busit = std.mem.tokenize(u8, lineit.next().?, ","); + while (busit.next()) |bus| { + if (bus[0] == 'x') continue; + const val = try std.fmt.parseInt(u32, bus, 10); + if (bestbus == null or waittime(start, val) < waittime(start, bestbus.?)) { + bestbus = val; + } + } + + const answer = bestbus.? * waittime(start, bestbus.?); + + return try std.fmt.allocPrint(allocator, "{d}", .{answer}); +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var lineit = std.mem.tokenize(u8, input, "\n"); + _ = try std.fmt.parseInt(u32, lineit.next().?, 10); + + var busses = std.ArrayList(u32).init(allocator); + defer busses.deinit(); + + var busit = std.mem.tokenize(u8, lineit.next().?, ","); + + const first = try std.fmt.parseInt(u32, busit.next().?, 10); + try busses.append(first); + + var offset: u64 = 0; + var cycle: u64 = first; + var delay: u32 = 1; + while (busit.next()) |bus| : (delay += 1) { + if (bus[0] == 'x') { + try busses.append(0); + continue; + } + const val = try std.fmt.parseInt(u32, bus, 10); + try busses.append(val); + + var mult: u64 = @divFloor(offset + delay, val); + if ((offset + delay) % val != 0) mult += 1; + while ((mult * val - offset - delay) % cycle != 0) { + mult += std.math.max(1, @divFloor(cycle - (mult * val - offset - delay) % cycle, val)); + } + offset = mult * val - delay; + + cycle = lcm(cycle, val); + if (aoc.debug) { + printRegion(busses.items, offset, offset + delay + 1); + printRegion(busses.items, offset + cycle, offset + cycle + delay + 1); + aoc.debugfmt("{}\n", .{offset}); + } + } + + printRegion(busses.items, offset, offset + busses.items.len); + + return try std.fmt.allocPrint(allocator, "{}", .{offset}); +} + +pub const main = aoc.main(part1, part2, .{ "6568", "554865447501099" }); diff --git a/src/day13/notes b/src/13/notes diff --git a/src/day13/part1 b/src/13/part1 diff --git a/src/day13/part2 b/src/13/part2 diff --git a/src/day14/input b/src/14/input diff --git a/src/day14/input-test b/src/14/input-test diff --git a/src/day14/input-test2 b/src/14/input-test2 diff --git a/src/day14/input-test3 b/src/14/input-test3 diff --git a/src/14/main.zig b/src/14/main.zig @@ -0,0 +1,122 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +fn getBit(v: u36, i: u6) bool { + return ((v >> i) & 1) > 0; +} + +fn clearBit(v: u36, i: u6) u36 { + return v & ~(@as(u36, 1) << i); +} + +fn setBit(v: u36, i: u6) u36 { + return v | (@as(u36, 1) << i); +} + +fn getValStr(line: []const u8) ![]const u8 { + const sep = std.mem.indexOf(u8, line, " = "); + if (sep == null) return aoc.Error.InvalidInput; + return line[sep.? + 3 ..]; +} + +fn getAddrStr(line: []const u8) ![]const u8 { + const sep = std.mem.indexOfScalar(u8, line, ']'); + if (sep == null) return aoc.Error.InvalidInput; + return line[4..sep.?]; +} + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var answer: u64 = 0; + + var memmap = std.AutoHashMap(u36, u64).init(allocator); + defer memmap.deinit(); + + var ormask: u36 = 0; + var andmask: u36 = ~@as(u36, 0); + + var lineit = std.mem.tokenize(u8, input, "\n"); + while (lineit.next()) |line| { + if (std.mem.eql(u8, line[0..4], "mask")) { + andmask = ~@as(u36, 0); + ormask = 0; + for (try getValStr(line)) |c, i| { + if (c == '0') { + andmask = clearBit(andmask, @intCast(u6, 35 - i)); + } else if (c == '1') { + ormask = setBit(ormask, @intCast(u6, 35 - i)); + } + } + } else { + const val = try std.fmt.parseInt(u36, try getValStr(line), 10); + const addr = try std.fmt.parseInt(u36, try getAddrStr(line), 10); + try memmap.put(addr, (val & andmask) | ormask); + } + } + + var mapit = memmap.iterator(); + while (mapit.next()) |kv| { + answer += kv.value_ptr.*; + } + + return try std.fmt.allocPrint(allocator, "{}", .{answer}); +} + +fn bitRecurse(map: *std.AutoHashMap(u36, u64), addr: *u36, val: u64, mask: u36, index: u6) anyerror!void { + if (index == 36) { + aoc.debugfmt("SET: {b} {}\n", .{ addr.*, val }); + try map.put(addr.*, val); + } else { + if (getBit(mask, index)) { + addr.* = setBit(addr.*, index); + try bitRecurse(map, addr, val, mask, index + 1); + addr.* = clearBit(addr.*, index); + try bitRecurse(map, addr, val, mask, index + 1); + } else { + try bitRecurse(map, addr, val, mask, index + 1); + } + } +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var answer: u64 = 0; + + var memmap = std.AutoHashMap(u36, u64).init(allocator); + defer memmap.deinit(); + + var ormask: u36 = 0; + var flipmask: u36 = 0; + + var lineit = std.mem.tokenize(u8, input, "\n"); + while (lineit.next()) |line| { + if (std.mem.eql(u8, line[0..4], "mask")) { + ormask = 0; + flipmask = 0; + for (try getValStr(line)) |c, i| { + if (c == '1') { + ormask = setBit(ormask, @intCast(u6, 35 - i)); + } else if (c == 'X') { + flipmask = setBit(flipmask, @intCast(u6, 35 - i)); + } + } + } else { + var addr = try std.fmt.parseInt(u36, try getAddrStr(line), 10); + const val = try std.fmt.parseInt(u36, try getValStr(line), 10); + addr = addr | ormask; + aoc.debugfmt("{b} {b} {} {}\n", .{ flipmask, ormask, addr, val }); + try bitRecurse(&memmap, &addr, val, flipmask, 0); + } + } + + var mapit = memmap.iterator(); + while (mapit.next()) |kv| { + answer += kv.value_ptr.*; + } + + return try std.fmt.allocPrint(allocator, "{}", .{answer}); +} + +pub const main = aoc.main(part1, part2, .{ "4886706177792", "3348493585827" }); diff --git a/src/day14/part1 b/src/14/part1 diff --git a/src/day14/part2 b/src/14/part2 diff --git a/src/day15/input b/src/15/input diff --git a/src/15/main.zig b/src/15/main.zig @@ -0,0 +1,63 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const Occurance = struct { last: u32, beforelast: u32 }; + +fn getNthSpoken(allocator: std.mem.Allocator, input: []u8, spoken: u32) !u32 { + var occurances = std.AutoHashMap(u32, Occurance).init(allocator); + defer occurances.deinit(); + + var intlist = std.ArrayList(u32).init(allocator); + defer intlist.deinit(); + + var numit = std.mem.tokenize(u8, input, "\n,"); + var i: u32 = 0; + while (numit.next()) |numstr| : (i += 1) { + const num = try std.fmt.parseInt(u32, numstr, 10); + try intlist.append(num); + try occurances.put(num, Occurance{ .last = i, .beforelast = i }); + } + + while (i < spoken) : (i += 1) { + if (aoc.debug and i % 100000 == 0) + aoc.debugfmt("\r{}", .{i}); + + const num = intlist.items[i - 1]; + var entry = occurances.getEntry(num); + var diff: u32 = 0; + if (entry) |occ_entry| { + diff = occ_entry.value_ptr.last - occ_entry.value_ptr.beforelast; + } + entry = occurances.getEntry(diff); + if (entry) |occ_entry| { + occ_entry.value_ptr.beforelast = occ_entry.value_ptr.last; + occ_entry.value_ptr.last = i; + } else { + try occurances.put(diff, Occurance{ .last = i, .beforelast = i }); + } + try intlist.append(diff); + } + + if (aoc.debug) + aoc.debugfmt("\r \r", .{}); + + return intlist.items[spoken - 1]; +} + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + const answer = try getNthSpoken(allocator, input, 2020); + + return try std.fmt.allocPrint(allocator, "{}", .{answer}); +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + const answer = try getNthSpoken(allocator, input, 30000000); + + return try std.fmt.allocPrint(allocator, "{}", .{answer}); +} + +pub const main = aoc.main(part1, part2, .{ "1259", "689" }); diff --git a/src/day15/part1 b/src/15/part1 diff --git a/src/day15/part2 b/src/15/part2 diff --git a/src/day16/input b/src/16/input diff --git a/src/day16/input-test b/src/16/input-test diff --git a/src/16/main.zig b/src/16/main.zig @@ -0,0 +1,179 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const Range = struct { min: u32, max: u32 }; +const FieldRule = struct { name: []const u8, r1: Range, r2: Range }; + +fn assert(comptime T: type, part: ?T) !T { + if (part == null) return aoc.Error.InvalidInput; + return part.?; +} + +fn parseRange(str: []const u8) !Range { + const sep = try assert(usize, std.mem.indexOfScalar(u8, str, '-')); + if (sep == str.len - 1) return aoc.Error.InvalidInput; + return Range{ + .min = try std.fmt.parseInt(u32, str[0..sep], 10), + .max = try std.fmt.parseInt(u32, str[sep + 1 ..], 10), + }; +} + +fn parseRules(rules: *std.ArrayList(FieldRule), str: []const u8) !void { + var lineit = std.mem.tokenize(u8, str, "\n"); + while (lineit.next()) |line| { + const sep = try assert(usize, std.mem.indexOf(u8, line, ": ")); + if (sep == line.len - 1) return aoc.Error.InvalidInput; + const name = line[0..sep]; + var rangepart = line[sep + 2 ..]; + var spaceit = std.mem.tokenize(u8, rangepart, " "); + const r1 = try parseRange(try assert([]const u8, spaceit.next())); + _ = spaceit.next(); + const r2 = try parseRange(try assert([]const u8, spaceit.next())); + try rules.append(FieldRule{ .name = name, .r1 = r1, .r2 = r2 }); + } +} + +fn parseVals(vals: *std.ArrayList(u32), str: []const u8) !void { + var lineit = std.mem.tokenize(u8, str, "\n"); + while (lineit.next()) |line| { + var partit = std.mem.tokenize(u8, line, ","); + while (partit.next()) |part| { + try vals.append(try std.fmt.parseInt(u32, part, 10)); + } + } +} + +fn matchesRule(rule: FieldRule, v: u32) bool { + return (v >= rule.r1.min and v <= rule.r1.max or v >= rule.r2.min and v <= rule.r2.max); +} + +fn hasRule(rules: []FieldRule, v: u32) bool { + for (rules) |r| { + if (matchesRule(r, v)) return true; + } + return false; +} + +fn printCandidates(candidates: []u1, rulecount: usize, remove: usize) void { + var i: u32 = 0; + while (i < rulecount) : (i += 1) { + for (candidates[i * rulecount .. (i + 1) * rulecount]) |c| { + aoc.debugfmt("{} ", .{c}); + } + if (i == remove) aoc.debugfmt(" <<<", .{}); + aoc.debugfmt("\n", .{}); + } +} + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var answer: u32 = 0; + + var rules = std.ArrayList(FieldRule).init(allocator); + defer rules.deinit(); + + var othervals = std.ArrayList(u32).init(allocator); + defer othervals.deinit(); + + var secit = std.mem.split(u8, input, "\n\n"); + try parseRules(&rules, try assert([]const u8, secit.next())); + _ = secit.next(); + try parseVals(&othervals, (try assert([]const u8, secit.next()))[16..]); + + for (othervals.items) |v| { + if (!hasRule(rules.items, v)) answer += v; + } + + return try std.fmt.allocPrint(allocator, "{}", .{answer}); +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var rules = std.ArrayList(FieldRule).init(allocator); + defer rules.deinit(); + + var ownvals = std.ArrayList(u32).init(allocator); + defer ownvals.deinit(); + + var othervals = std.ArrayList(u32).init(allocator); + defer othervals.deinit(); + + var secit = std.mem.split(u8, input, "\n\n"); + try parseRules(&rules, try assert([]const u8, secit.next())); + try parseVals(&ownvals, (try assert([]const u8, secit.next()))[13..]); + try parseVals(&othervals, (try assert([]const u8, secit.next()))[16..]); + + const rulecount = rules.items.len; + + { + var index: u32 = 0; + while (index < othervals.items.len) { + var i: u32 = 0; + while (i < rulecount) : (i += 1) { + if (!hasRule(rules.items, othervals.items[index + i])) + break; + } + + if (i == rulecount) { + index += @intCast(u32, rulecount); + } else { + if (aoc.debug) + aoc.debugfmt("REMOVE: ", .{}); + i = 0; + while (i < rulecount) : (i += 1) { + if (aoc.debug) + aoc.debugfmt("{} ", .{othervals.items[index]}); + _ = othervals.orderedRemove(index); + } + if (aoc.debug) + aoc.debugfmt("\n", .{}); + } + } + } + + var candidates = try allocator.alloc(u1, rulecount * rulecount); + defer allocator.free(candidates); + std.mem.set(u1, candidates, 1); + + if (aoc.debug) + aoc.debugfmt("{}\n", .{othervals.items.len}); + { + var index: u32 = 0; + while (index < othervals.items.len) : (index += 1) { + const ri = index % rulecount; + const v = othervals.items[index]; + for (rules.items) |r, rri| { + if (!matchesRule(r, v)) { + candidates[ri * rulecount + rri] = 0; + } + } + } + } + + var answer: u64 = 1; + { + var index: u32 = 0; + while (index < rulecount * rulecount) : (index += 1) { + var ri = index % rulecount; + const cc = candidates[ri * rulecount .. (ri + 1) * rulecount]; + if (std.mem.count(u1, cc, ([_]u1{1})[0..]) == 1) { + const field = std.mem.indexOfScalar(u1, cc, 1).?; + if (std.mem.indexOf(u8, rules.items[field].name, "departure") != null) + answer *= ownvals.items[ri]; + for (ownvals.items) |_, ii| { + candidates[ii * rulecount + field] = 0; + } + if (aoc.debug) { + printCandidates(candidates, rulecount, ri); + aoc.debugfmt("\n", .{}); + } + } + } + } + + return try std.fmt.allocPrint(allocator, "{}", .{answer}); +} + +pub const main = aoc.main(part1, part2, .{ "25972", "622670335901" }); diff --git a/src/day16/part1 b/src/16/part1 diff --git a/src/day16/part2 b/src/16/part2 diff --git a/src/day17/input b/src/17/input diff --git a/src/day17/input-test b/src/17/input-test diff --git a/src/17/main.zig b/src/17/main.zig @@ -0,0 +1,195 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +fn PosVec(comptime N: u32) type { + return struct { data: [N]i64 }; +} + +const Active = bool; + +fn forNeighbors(comptime dims: u32, map: *std.AutoHashMap(PosVec(dims), Active), p: PosVec(dims), data: anytype, func: fn (map: *std.AutoHashMap(PosVec(dims), Active), p: PosVec(dims), d: @TypeOf(data)) anyerror!void) !void { + var i: u32 = 0; + while (i < comptime std.math.pow(u32, 3, dims)) : (i += 1) { + var np: PosVec(dims) = undefined; + var d: u32 = 0; + var skip = true; + while (d < dims) : (d += 1) { + const offset = (i / std.math.pow(u32, 3, d)) % 3; + np.data[d] = p.data[d] + @intCast(i64, offset) - 1; + if (skip and offset != 1) skip = false; + } + if (skip) continue; + try func(map, np, data); + } +} + +fn posStr(comptime dims: u32, p: PosVec(dims)) void { + var d: u32 = 0; + while (d < dims) : (d += 1) { + aoc.debugfmt("{} ", .{p.data[d]}); + } +} + +fn countActive(comptime dims: u32) fn (map: *std.AutoHashMap(PosVec(dims), Active), p: PosVec(dims), d: *u32) anyerror!void { + const impl = struct { + fn func(map: *std.AutoHashMap(PosVec(dims), Active), p: PosVec(dims), d: *u32) anyerror!void { + d.* += @boolToInt(map.get(p) != null); + } + }; + return impl.func; +} + +fn addNew(comptime dims: u32) fn (oldmap: *std.AutoHashMap(PosVec(dims), Active), p: PosVec(dims), newmap: *std.AutoHashMap(PosVec(dims), Active)) anyerror!void { + const impl = struct { + fn func(oldmap: *std.AutoHashMap(PosVec(dims), Active), p: PosVec(dims), newmap: *std.AutoHashMap(PosVec(dims), Active)) anyerror!void { + if (newmap.get(p) != null) return; + + var v = oldmap.get(p); + var state = (v != null); + + var count: u32 = 0; + try forNeighbors(dims, oldmap, p, &count, countActive(dims)); + if (state and count >= 2 and count <= 3 or !state and count == 3) { + try newmap.put(p, true); + } + } + }; + return impl.func; +} + +fn simulateRound(comptime dims: u32, map: *std.AutoHashMap(PosVec(dims), Active), allocator: std.mem.Allocator) !void { + var newmap = std.AutoHashMap(PosVec(dims), Active).init(allocator); + errdefer newmap.deinit(); + + var mapit = map.iterator(); + while (mapit.next()) |kv| { + try forNeighbors(dims, map, kv.key_ptr.*, &newmap, addNew(dims)); + try addNew(dims)(map, kv.key_ptr.*, &newmap); + } + + std.mem.swap(std.AutoHashMap(PosVec(dims), Active), map, &newmap); + newmap.deinit(); +} + +fn sliceProduct(data: []i64) i64 { + var product: i64 = 1; + for (data) |v| { + product *= v; + } + return product; +} + +fn printMap(comptime dims: u32, map: *std.AutoHashMap(PosVec(dims), Active)) void { + var min: ?PosVec(dims) = null; + var max: ?PosVec(dims) = null; + + var mapit = map.iterator(); + while (mapit.next()) |kv| { + if (min == null) min = kv.key_ptr.*; + if (max == null) max = kv.key_ptr.*; + + var d: u32 = 0; + while (d < dims) : (d += 1) { + if (min.?.data[d] > kv.key_ptr.data[d]) min.?.data[d] = kv.key_ptr.data[d]; + if (max.?.data[d] < kv.key_ptr.data[d]) max.?.data[d] = kv.key_ptr.data[d]; + } + } + + if (min == null or max == null) return; + + var space: PosVec(dims) = undefined; + { + var d: u32 = 0; + while (d < dims) : (d += 1) { + space.data[d] = max.?.data[d] - min.?.data[d]; + } + } + + var i: usize = 0; + while (i < sliceProduct(space.data[0..])) : (i += @intCast(usize, space.data[0] * space.data[1])) { + var np: PosVec(dims) = undefined; + var d: u32 = 0; + while (d < dims) : (d += 1) { + np.data[d] = min.?.data[d] + @mod(@divFloor(@intCast(i64, i), sliceProduct(space.data[0..d])), space.data[d]); + } + + d = 2; + aoc.debugfmt("Slice at: ", .{}); + while (d < dims) : (d += 1) { + if (d > 2) aoc.debugfmt(",", .{}); + aoc.debugfmt("{}.Dim = {} ", .{ d + 1, np.data[d] }); + } + aoc.debugfmt("\n", .{}); + + var y = min.?.data[1]; + while (y <= max.?.data[1]) : (y += 1) { + var x = min.?.data[0]; + while (x <= max.?.data[0]) : (x += 1) { + var v = np; + v.data[0] = x; + v.data[1] = y; + var c: u8 = '.'; + if (map.get(v) != null) c = '#'; + aoc.debugfmt("{c}", .{c}); + } + aoc.debugfmt("\n", .{}); + } + aoc.debugfmt("\n", .{}); + } +} + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var map = std.AutoHashMap(PosVec(3), Active).init(allocator); + defer map.deinit(); + + var lineit = std.mem.tokenize(u8, input, "\n"); + var y: i64 = 0; + while (lineit.next()) |line| : (y += 1) { + for (line) |c, x| { + if (c != '#') continue; + try map.put(PosVec(3){ .data = [_]i64{ @intCast(i64, x), y, 0 } }, true); + } + } + + var i: usize = 0; + while (i < 6) : (i += 1) { + try simulateRound(3, &map, allocator); + if (aoc.debug) { + aoc.debugfmt("AFTER ROUND {}:\n", .{i + 1}); + printMap(3, &map); + } + } + + return try std.fmt.allocPrint(allocator, "{}", .{map.count()}); +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var map = std.AutoHashMap(PosVec(4), Active).init(allocator); + defer map.deinit(); + + var lineit = std.mem.tokenize(u8, input, "\n"); + var y: i64 = 0; + while (lineit.next()) |line| : (y += 1) { + for (line) |c, x| { + if (c != '#') continue; + try map.put(PosVec(4){ .data = [_]i64{ @intCast(i64, x), y, 0, 0 } }, true); + } + } + + var i: usize = 0; + while (i < 6) : (i += 1) { + try simulateRound(4, &map, allocator); + if (aoc.debug) { + aoc.debugfmt("AFTER ROUND {}:\n", .{i + 1}); + printMap(4, &map); + } + } + + return try std.fmt.allocPrint(allocator, "{}", .{map.count()}); +} + +pub const main = aoc.main(part1, part2, .{ "304", "1868" }); diff --git a/src/day17/part1 b/src/17/part1 diff --git a/src/day17/part2 b/src/17/part2 diff --git a/src/day18/input b/src/18/input diff --git a/src/day18/input-test b/src/18/input-test diff --git a/src/day18/input-test2 b/src/18/input-test2 diff --git a/src/18/main.zig b/src/18/main.zig @@ -0,0 +1,177 @@ +// Own twist on https://en.wikipedia.org/wiki/Shunting-yard_algorithm + +const std = @import("std"); +const aoc = @import("aoc"); + +const OpType = enum { ADD, MULT, COUNT }; +const Op = struct { prio: u32, optype: OpType }; +const Item = union(enum) { num: i64, op: OpType }; + +const op_names = [_][]const u8{ "ADD", "MULT" }; + +fn printStack(stack: []Item) void { + for (stack) |item| { + switch (item) { + .op => aoc.debugfmt("{s} ", .{op_names[@enumToInt(item.op)]}), + .num => aoc.debugfmt("{} ", .{item.num}), + } + } + aoc.debugfmt("\n", .{}); +} + +fn parseToken(line: []const u8, i: *usize, depth: *u32, op_prio: []const u32, mathstack: *std.ArrayList(Item), opstack: *std.ArrayList(Op)) !bool { + if (i.* > line.len) { + return false; + } else if (i.* == line.len) { + while (opstack.items.len > 0) + try mathstack.append(Item{ .op = opstack.pop().optype }); + } else { + const c = line[i.*]; + switch (c) { + '+', '*' => { + const optype = switch (c) { + '+' => OpType.ADD, + '*' => OpType.MULT, + else => unreachable, + }; + const prio = depth.* * @enumToInt(OpType.COUNT) + op_prio[@enumToInt(optype)]; + while (opstack.items.len > 0 and + opstack.items[opstack.items.len - 1].prio >= prio) + { + try mathstack.append(Item{ .op = opstack.pop().optype }); + } + try opstack.append(Op{ .optype = optype, .prio = prio }); + }, + '(' => depth.* += 1, + ')' => depth.* -= 1, + ' ' => {}, + else => { + // in this case we know that every number is one digit.. + // crappy zig parseInt requires span of exact int size + // before we even know the format >:((( + const val = try std.fmt.parseInt(i64, line[i.* .. i.* + 1], 10); + try mathstack.append(Item{ .num = val }); + }, + } + } + i.* += 1; + return true; +} + +fn reduceStack(mathstack: *std.ArrayList(Item), opstack: *std.ArrayList(Op), tmpstack: *std.ArrayList(Item)) !void { + _ = opstack; + + while (mathstack.items.len > 0) { + while (mathstack.items[mathstack.items.len - 1] != .op and + tmpstack.items.len > 0) + { + try mathstack.append(tmpstack.pop()); + } + if (mathstack.items.len < 3 or mathstack.items[mathstack.items.len - 1] != .op) + break; + const op = mathstack.pop(); + + // get first arg + if (mathstack.items[mathstack.items.len - 1] != .num) { + try tmpstack.append(op); + continue; + } + const a = mathstack.pop(); + + // get second arg + if (mathstack.items[mathstack.items.len - 1] != .num) { + try tmpstack.append(op); + try tmpstack.append(a); + continue; + } + const b = mathstack.pop(); + + const result: i64 = switch (op.op) { + OpType.ADD => a.num + b.num, + OpType.MULT => a.num * b.num, + else => unreachable, + }; + try mathstack.append(Item{ .num = result }); + } +} + +fn printRPN(allocator: std.mem.Allocator, input: []u8, prios: []const u32) !void { + var mathstack = std.ArrayList(Item).init(allocator); + defer mathstack.deinit(); + + var opstack = std.ArrayList(Op).init(allocator); + defer opstack.deinit(); + + var lineit = std.mem.tokenize(u8, input, "\n"); + while (lineit.next()) |line| { + var depth: u32 = 0; + var i: usize = 0; + while (try parseToken(line, &i, &depth, prios, &mathstack, &opstack)) {} + } + + printStack(mathstack.items); +} + +fn partProto(allocator: std.mem.Allocator, input: []u8, prios: []const u32) !?[]u8 { + var answer: i64 = 0; + + if (aoc.debug) { + aoc.debugfmt("Reverse Polish Notation:\n", .{}); + try printRPN(allocator, input, prios); + } + + var mathstack = std.ArrayList(Item).init(allocator); + defer mathstack.deinit(); + + var opstack = std.ArrayList(Op).init(allocator); + defer opstack.deinit(); + + var tmpstack = std.ArrayList(Item).init(allocator); + defer tmpstack.deinit(); + + var lineit = std.mem.tokenize(u8, input, "\n"); + while (lineit.next()) |line| { + var depth: u32 = 0; + var i: usize = 0; + while (try parseToken(line, &i, &depth, prios, &mathstack, &opstack)) { + try reduceStack(&mathstack, &opstack, &tmpstack); + } + + if (mathstack.items.len != 1) return aoc.Error.InvalidInput; + const last = mathstack.items[mathstack.items.len - 1]; + if (last != .num) return aoc.Error.InvalidInput; + answer += last.num; + + try mathstack.resize(0); + try opstack.resize(0); + try tmpstack.resize(0); + } + + return try std.fmt.allocPrint(allocator, "{}", .{answer}); +} + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + const prios = [_]u32{ 1, 1 }; + + return try partProto(allocator, input, prios[0..]); +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + const prios = [_]u32{ 2, 1 }; + + return try partProto(allocator, input, prios[0..]); +} + +test "unions" { + const item1 = Item{ .op = OpType.MULT }; + const item2 = Item{ .num = 1000 }; + const expect = std.testing.expect; + expect(item1 == .op); + expect(item2 == .num); +} + +pub const main = aoc.main(part1, part2, .{ "5783053349377", "74821486966872" }); diff --git a/src/day18/part1 b/src/18/part1 diff --git a/src/day18/part2 b/src/18/part2 diff --git a/src/day19/input b/src/19/input diff --git a/src/day19/input-test b/src/19/input-test diff --git a/src/day19/input-test2 b/src/19/input-test2 diff --git a/src/day19/input-test3 b/src/19/input-test3 diff --git a/src/19/main.zig b/src/19/main.zig @@ -0,0 +1,181 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const Rule = union(enum) { + c: u8, + combos: []const []const usize, +}; + +fn printSlice(slice: []const usize) void { + for (slice) |v, i| { + if (i > 0) { + aoc.debugfmt(" ", .{}); + } + aoc.debugfmt("{}", .{v}); + } +} + +var stacktrace: std.ArrayList(usize) = undefined; + +fn checkRule(rules: *std.AutoHashMap(usize, Rule), rid: usize, msg: []const u8, nextstack: *std.ArrayList(usize)) anyerror!bool { + if (msg.len == 0) return true; + const rule = rules.get(rid) orelse return aoc.Error.InvalidInput; + + if (rule == .c) { + if (msg[0] != rule.c) return false; + if (msg.len == 1) return (nextstack.items.len == 0); + if (nextstack.items.len == 0) return false; + + const next = nextstack.pop(); + + const valid = try checkRule(rules, next, msg[1..], nextstack); + if (valid) try stacktrace.append(next); + if (valid) return true; + + try nextstack.append(next); + } else { + for (rule.combos) |ruleseq| { + try nextstack.appendSlice(ruleseq[1..]); + std.mem.reverse(usize, nextstack.items[nextstack.items.len - (ruleseq.len - 1) ..]); + + const valid = try checkRule(rules, ruleseq[0], msg, nextstack); + if (valid) try stacktrace.append(ruleseq[0]); + if (valid) return true; + + try nextstack.resize(nextstack.items.len - (ruleseq.len - 1)); + } + } + return false; +} + +fn freeRules(allocator: std.mem.Allocator, rules: *std.AutoHashMap(usize, Rule), ignore: ?[]const usize) void { + var mapit = rules.iterator(); + while (mapit.next()) |kv| { + if (ignore != null and std.mem.indexOfScalar(usize, ignore.?, kv.key_ptr.*) != null) continue; + if (kv.value_ptr.* == .combos) { + for (kv.value_ptr.combos) |rs| { + allocator.free(rs); + } + allocator.free(kv.value_ptr.combos); + } + } + rules.deinit(); +} + +fn parseRule(allocator: std.mem.Allocator, rules: *std.AutoHashMap(usize, Rule), line: []const u8) !void { + const indexsep = try aoc.unwrap(std.mem.indexOf(u8, line, ": ")); + const index = try std.fmt.parseInt(usize, line[0..indexsep], 10); + + var chrsep = std.mem.indexOf(u8, line, "\""); + if (chrsep != null) { + if (chrsep.? == line.len - 1) return aoc.Error.InvalidInput; + try rules.put(index, Rule{ .c = line[chrsep.? + 1] }); + } else { + var vals = std.ArrayList([]usize).init(allocator); + errdefer vals.deinit(); + + var ruleit = std.mem.split(u8, line[indexsep + 2 ..], " | "); + while (ruleit.next()) |r| { + var ruleids = std.ArrayList(usize).init(allocator); + errdefer ruleids.deinit(); + + var spaceit = std.mem.tokenize(u8, r, " "); + while (spaceit.next()) |word| { + try ruleids.append(try std.fmt.parseInt(usize, word, 10)); + } + + if (ruleids.items.len == 0) return aoc.Error.InvalidInput; + + try vals.append(ruleids.items); + } + + try rules.put(index, Rule{ .combos = vals.items }); + } +} + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var rules = std.AutoHashMap(usize, Rule).init(allocator); + defer freeRules(allocator, &rules, null); + + var partit = std.mem.split(u8, input, "\n\n"); + + var lineit = std.mem.tokenize(u8, try aoc.unwrap(partit.next()), "\n"); + while (lineit.next()) |line| { + try parseRule(allocator, &rules, line); + } + + var answer: u32 = 0; + var nextstack = std.ArrayList(usize).init(allocator); + defer nextstack.deinit(); + + stacktrace = std.ArrayList(usize).init(allocator); + defer stacktrace.deinit(); + + lineit = std.mem.tokenize(u8, try aoc.unwrap(partit.next()), "\n"); + var count: usize = 0; + while (lineit.next()) |line| { + count += 1; + if (try checkRule(&rules, 0, line, &nextstack)) + answer += 1; + try nextstack.resize(0); + } + + return try std.fmt.allocPrint(allocator, "{}", .{answer}); +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var rules = std.AutoHashMap(usize, Rule).init(allocator); + defer freeRules(allocator, &rules, ([_]usize{ 8, 11 })[0..]); + + var partit = std.mem.split(u8, input, "\n\n"); + + var lineit = std.mem.tokenize(u8, try aoc.unwrap(partit.next()), "\n"); + while (lineit.next()) |line| { + if (std.mem.eql(u8, line[0..2], "8:")) { + const p1: []const usize = ([_]usize{42})[0..]; + const p2: []const usize = ([_]usize{ 42, 8 })[0..]; + try rules.put(8, Rule{ .combos = ([_][]const usize{ p1, p2 })[0..] }); + } else if (std.mem.eql(u8, line[0..3], "11:")) { + const p1: []const usize = ([_]usize{ 42, 31 })[0..]; + const p2: []const usize = ([_]usize{ 42, 11, 31 })[0..]; + try rules.put(11, Rule{ .combos = ([_][]const usize{ p1, p2 })[0..] }); + } else { + try parseRule(allocator, &rules, line); + } + } + + var answer: u32 = 0; + var nextstack = std.ArrayList(usize).init(allocator); + defer nextstack.deinit(); + + stacktrace = std.ArrayList(usize).init(allocator); + defer stacktrace.deinit(); + + var count: usize = 0; + lineit = std.mem.tokenize(u8, try aoc.unwrap(partit.next()), "\n"); + while (lineit.next()) |line| { + count += 1; + const valid = try checkRule(&rules, 0, line, &nextstack); + answer += @boolToInt(valid); + if (aoc.debug) { + aoc.debugfmt("TRACE: [", .{}); + std.mem.reverse(usize, stacktrace.items); + printSlice(stacktrace.items); + aoc.debugfmt("]\n", .{}); + if (valid) { + aoc.debugfmt("MATCH {s}\n\n", .{line}); + } else { + aoc.debugfmt("NO MATCH {s}\n\n", .{line}); + } + } + try nextstack.resize(0); + } + + return try std.fmt.allocPrint(allocator, "{}", .{answer}); +} + +pub const main = aoc.main(part1, part2, .{ "134", "377" }); diff --git a/src/day19/part1 b/src/19/part1 diff --git a/src/day19/part2 b/src/19/part2 diff --git a/src/day19/trace b/src/19/trace diff --git a/src/day20/input b/src/20/input diff --git a/src/day20/input-test b/src/20/input-test diff --git a/src/day20/input-test2 b/src/20/input-test2 diff --git a/src/20/main.zig b/src/20/main.zig @@ -0,0 +1,470 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const tile_width: u32 = 10; +const Tile = struct { + id: u32, + data: [tile_width * tile_width]u1, + sides: [4][tile_width]u1, // NOTE: update these values when placed accordingly + adj: [4]?usize, + pos: aoc.Pos, +}; +const Flip = enum { NONE, HOR, VERT }; +const flips = [_]Flip{ Flip.NONE, Flip.HOR, Flip.VERT }; + +const seamonster = [_][]const u8{ + " # ", + "# ## ## ###", + " # # # # # # ", +}; + +const dirs = [4]aoc.Pos{ + aoc.Pos{ .x = 0, .y = -1 }, + aoc.Pos{ .x = 1, .y = 0 }, + aoc.Pos{ .x = 0, .y = 1 }, + aoc.Pos{ .x = -1, .y = 0 }, +}; + +fn printGrid(tiles: []Tile, grid: []?usize, grid_width: u32) void { + var y: u32 = 0; + while (y < grid_width * tile_width) : (y += 1) { + var x: u32 = 0; + if (y > 0 and y % tile_width == 0) aoc.debugfmt("\n", .{}); + while (x < grid_width * tile_width) : (x += 1) { + if (x > 0 and x % tile_width == 0) aoc.debugfmt(" ", .{}); + var tile_index: ?usize = grid[@divFloor(y, tile_width) * grid_width + @divFloor(x, tile_width)]; + if (tile_index != null) { + var tile = tiles[tile_index.?]; + var c: u8 = if (tile.data[(y % tile_width) * tile_width + (x % tile_width)] == 1) '#' else '.'; + aoc.debugfmt("{c}", .{c}); + } else { + aoc.debugfmt("?", .{}); + } + } + aoc.debugfmt("\n", .{}); + } + aoc.debugfmt("\n\n", .{}); +} + +fn printTile(data: []u1, width: u32) void { + var y: u32 = 0; + while (y < width) : (y += 1) { + printTileSlice(data[y * width .. (y + 1) * width]); + aoc.debugfmt("\n", .{}); + } +} + +fn printTileSlice(slice: []const u1) void { + var i: u32 = 0; + while (i < slice.len) : (i += 1) { + var c: u8 = if (slice[i] == 1) '#' else '.'; + aoc.debugfmt("{c}", .{c}); + } +} + +fn couldMatch(side1: [tile_width]u1, side2: [tile_width]u1) bool { + var side1_cpy = side1; + if (std.mem.eql(u1, side1_cpy[0..], side2[0..])) return true; + std.mem.reverse(u1, side1_cpy[0..]); + if (std.mem.eql(u1, side1_cpy[0..], side2[0..])) return true; + return false; +} + +fn parseTiles(tiles: *std.ArrayList(Tile), input: []u8) !void { + var tileit = std.mem.split(u8, input, "\n\n"); + while (tileit.next()) |tilestr| { + if (tilestr.len <= 1) continue; + var tile: Tile = undefined; + var lineit = std.mem.tokenize(u8, tilestr, "\n"); + + // read tile id + var numstr = (try aoc.unwrap(lineit.next()))[5..9]; + tile.id = try std.fmt.parseInt(u32, numstr, 10); + + // read tile data + var i: u32 = 0; + var line: []const u8 = undefined; + while (i < tile_width * tile_width) : (i += 1) { + if (i % tile_width == 0) line = try aoc.unwrap(lineit.next()); + tile.data[i] = @boolToInt(line[i % tile_width] == '#'); + } + + // read side bits + i = 0; + while (i < 4) : (i += 1) { + var sidebits: [tile_width]u1 = undefined; + for (sidebits) |_, k| { + sidebits[k] = switch (i) { + @enumToInt(aoc.Dir.Name.NORTH) => tile.data[k], + @enumToInt(aoc.Dir.Name.WEST) => tile.data[(tile_width - 1 - k) * tile_width], + @enumToInt(aoc.Dir.Name.EAST) => tile.data[tile_width - 1 + k * tile_width], + @enumToInt(aoc.Dir.Name.SOUTH) => tile.data[tile_width * tile_width - 1 - k], + else => unreachable, + }; + } + tile.sides[i] = sidebits; + } + + // init misc data + tile.adj = [_]?usize{ null, null, null, null }; + + try tiles.append(tile); + } +} + +fn matchTiles(tiles: *std.ArrayList(Tile)) void { + // for each items's side, look for another item with a matching side + for (tiles.items) |*tile, tile_index| { + sides_loop: for (tile.sides) |_, i| { + for (tiles.items) |*otile, otile_index| { + if (tile_index == otile_index) continue; + + for (otile.sides) |_, oi| { + if (tile.adj[i] == null and otile.adj[oi] == null and couldMatch(tile.sides[i], otile.sides[oi])) { + tile.adj[i] = otile_index; + otile.adj[oi] = tile_index; + continue :sides_loop; + } + } + } + } + + if (aoc.debug) { + aoc.debugfmt("{}:\nSIDES ", .{tile.id}); + for (tile.sides) |side| { + for (side) |b| { + var c: u8 = if (b == 1) '#' else '.'; + aoc.debugfmt("{c}", .{c}); + } + aoc.debugfmt(" ", .{}); + } + aoc.debugfmt("\n", .{}); + for (tile.adj) |adj, i| { + if (tile.adj[i] == null) { + aoc.debugfmt("null ", .{}); + } else { + var adjtile = tiles.items[adj.?]; + aoc.debugfmt("{} ", .{adjtile.id}); + } + } + aoc.debugfmt("\n\n", .{}); + } + } +} + +fn checkSides(tiles: []Tile, grid: []?usize, grid_width: u32, p: aoc.Pos, adj: [4]?usize, sides: [4][10]u1) bool { + var rot: u32 = 0; + while (rot < 4) : (rot += 1) { + const np = p.add(dirs[rot]); + aoc.debugfmt("{}\n", .{@intToEnum(aoc.Dir.Name, @intCast(u2, rot))}); + aoc.debugfmt("{} {}\n", .{ np.x, np.y }); + + if (np.x < 0 or np.x >= grid_width or np.y < 0 or np.y >= grid_width) { + aoc.debugfmt("Side is null\n", .{}); + if (adj[rot] == null) { + continue; + } else { + aoc.debugfmt("Side {} should be NULL!\n", .{@intToEnum(aoc.Dir.Name, @intCast(u2, rot))}); + return false; + } + } + + var ti: ?usize = grid[@intCast(u32, np.y * @intCast(i64, grid_width) + np.x)]; + if (ti == null) continue; + + var otherside = tiles[ti.?].sides[aoc.Dir.nextCW(rot, 2)]; + std.mem.reverse(u1, otherside[0..]); + printTileSlice(otherside[0..]); + aoc.debugfmt(" vs ", .{}); + printTileSlice(sides[rot][0..]); + aoc.debugfmt("\n", .{}); + + if (!std.mem.eql(u1, otherside[0..], sides[rot][0..])) return false; + } + return true; +} + +fn rotateData(newdata: *[]u1, olddata: []u1, width: u32, rot: usize) void { + for (olddata) |v, di| { + const dy = @divFloor(di, width); + const dx = di % width; + var nx = dx; + var ny = dy; + switch (rot) { + @enumToInt(aoc.Dir.Name.NORTH) => {}, + @enumToInt(aoc.Dir.Name.EAST) => { + nx = width - 1 - dy; + ny = dx; + }, + @enumToInt(aoc.Dir.Name.SOUTH) => { + nx = width - 1 - dx; + ny = width - 1 - dy; + }, + @enumToInt(aoc.Dir.Name.WEST) => { + nx = dy; + ny = width - 1 - dx; + }, + else => {}, + } + newdata.*[ny * width + nx] = v; + } +} + +fn flipData(data: []u1, width: u32, flip: Flip) void { + switch (flip) { + Flip.HOR => { + var y: u32 = 0; + while (y < width) : (y += 1) { + std.mem.reverse(u1, data[y * width .. (y + 1) * width]); + } + }, + Flip.VERT => { + var x: u32 = 0; + while (x < width) : (x += 1) { + var y: u32 = 0; + while (y < width / 2) : (y += 1) { + std.mem.swap(u1, &data[x + y * width], &data[x + (width - 1 - y) * width]); + } + } + }, + else => {}, + } +} + +fn manipulateAndPlace(allocator: std.mem.Allocator, tiles: []Tile, grid: []?usize, grid_width: u32, p: aoc.Pos, tile_index: usize) !void { + var tile = &tiles[tile_index]; + + for (dirs) |_, rot| { + for (flips) |flip| { + // apply manipulation to only sides first for checking + var sides = tile.sides; + var tmp_sides = tile.sides; + var adj = tile.adj; + var tmp_adj = tile.adj; + switch (flip) { + Flip.HOR => { + std.mem.reverse(u1, tmp_sides[@enumToInt(aoc.Dir.Name.NORTH)][0..]); + std.mem.reverse(u1, tmp_sides[@enumToInt(aoc.Dir.Name.SOUTH)][0..]); + tmp_sides[@enumToInt(aoc.Dir.Name.WEST)] = sides[@enumToInt(aoc.Dir.Name.EAST)]; + tmp_sides[@enumToInt(aoc.Dir.Name.EAST)] = sides[@enumToInt(aoc.Dir.Name.WEST)]; + std.mem.reverse(u1, tmp_sides[@enumToInt(aoc.Dir.Name.EAST)][0..]); + std.mem.reverse(u1, tmp_sides[@enumToInt(aoc.Dir.Name.WEST)][0..]); + std.mem.swap(?usize, &adj[@enumToInt(aoc.Dir.Name.EAST)], &adj[@enumToInt(aoc.Dir.Name.WEST)]); + }, + Flip.VERT => { + std.mem.reverse(u1, tmp_sides[@enumToInt(aoc.Dir.Name.WEST)][0..]); + std.mem.reverse(u1, tmp_sides[@enumToInt(aoc.Dir.Name.EAST)][0..]); + tmp_sides[@enumToInt(aoc.Dir.Name.NORTH)] = sides[@enumToInt(aoc.Dir.Name.SOUTH)]; + tmp_sides[@enumToInt(aoc.Dir.Name.SOUTH)] = sides[@enumToInt(aoc.Dir.Name.NORTH)]; + std.mem.reverse(u1, tmp_sides[@enumToInt(aoc.Dir.Name.NORTH)][0..]); + std.mem.reverse(u1, tmp_sides[@enumToInt(aoc.Dir.Name.SOUTH)][0..]); + std.mem.swap(?usize, &adj[@enumToInt(aoc.Dir.Name.NORTH)], &adj[@enumToInt(aoc.Dir.Name.SOUTH)]); + }, + else => {}, + } + sides = tmp_sides; + + for (dirs) |_, i| { + tmp_sides[i] = sides[aoc.Dir.nextCCW(i, rot)]; + tmp_adj[i] = adj[aoc.Dir.nextCCW(i, rot)]; + } + + aoc.debugfmt("\nROT: {}\n", .{rot}); + aoc.debugfmt("FLIP: {}\n", .{flip}); + aoc.debugfmt("SIDES: \n", .{}); + for (tmp_sides) |side| { + printTileSlice(side[0..]); + aoc.debugfmt("\n", .{}); + } + const valid = checkSides(tiles, grid, grid_width, p, tmp_adj, tmp_sides); + + // now apply manipulations to image data + if (valid) { + // for (tile.sides) |side| { + // printTileSlice(side[0..]); + // aoc.debugfmt("\n", .{}); + // } + aoc.debugfmt("{} {} {}\n", .{ tile.id, flip, rot }); + printTile(tile.data[0..], tile_width); + aoc.debugfmt("\n", .{}); + tile.sides = tmp_sides; + tile.pos = p; + tile.adj = tmp_adj; + grid[@intCast(u32, p.y * @intCast(i64, grid_width) + p.x)] = tile_index; + + flipData(tile.data[0..], tile_width, flip); + + var newdata = try allocator.alloc(u1, tile_width * tile_width); + defer allocator.free(newdata); + + rotateData(&newdata, tile.data[0..], tile_width, rot); + std.mem.copy(u1, tile.data[0..], newdata); + return; + } + } + } + return aoc.Error.InvalidInput; +} + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var tiles = std.ArrayList(Tile).init(allocator); + defer tiles.deinit(); + + try parseTiles(&tiles, input); + + matchTiles(&tiles); + + var answer: u64 = 1; + for (tiles.items) |tile| { + var unknown: u32 = 0; + for (tile.adj) |v| { + unknown += @boolToInt(v == null); + } + if (unknown == 2) answer *= tile.id; + } + + return try std.fmt.allocPrint(allocator, "{}", .{answer}); +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var tiles = std.ArrayList(Tile).init(allocator); + defer tiles.deinit(); + + try parseTiles(&tiles, input); + + matchTiles(&tiles); + + var grid = try allocator.alloc(?usize, tiles.items.len); + std.mem.set(?usize, grid, null); + defer allocator.free(grid); + var grid_width = @intCast(u32, std.math.sqrt(tiles.items.len)); + + var left = std.ArrayList(usize).init(allocator); + defer left.deinit(); + + for (tiles.items) |_, tile_index| { + try left.append(tile_index); + } + + // add a corner as first item to add onto + for (tiles.items) |tile, tile_index| { + var unknown: u32 = 0; + for (tile.adj) |v| { + unknown += @boolToInt(v == null); + } + if (unknown == 2) { + try manipulateAndPlace(allocator, tiles.items, grid, grid_width, aoc.Pos{ .x = 0, .y = 0 }, tile_index); + _ = left.swapRemove(tile_index); + break; + } + } + + // find adjacents and manipulate until they fit + next_tile: while (left.items.len > 0) { + if (aoc.debug) printGrid(tiles.items, grid, grid_width); + + for (grid) |_, gi| { + aoc.debugfmt("POS {}\n", .{gi}); + const tile_index: ?usize = grid[gi] orelse continue; + aoc.debugfmt("POS {} YES\n", .{gi}); + + var placed = &tiles.items[tile_index.?]; + for (placed.adj) |want, dir| { + if (want == null) continue; + // aoc.debugfmt("LOOKING FOR {}\n", .{want.?}); + // for (left.items) |item| { + // aoc.debugfmt("> {}\n", .{item}); + // } + if (std.mem.indexOfScalar(usize, left.items, want.?)) |left_index| { + const new_index = left.items[left_index]; + const new_tile = tiles.items[new_index]; + var npos = placed.pos.add(dirs[dir]); + aoc.debugfmt("TRYING TO PLACE {} NEW AT: {} {}\n", .{ new_tile.id, npos.x, npos.y }); + + try manipulateAndPlace(allocator, tiles.items, grid, grid_width, npos, new_index); + + _ = left.swapRemove(left_index); + continue :next_tile; + } + } + } + return aoc.Error.InvalidInput; + } + + if (aoc.debug) printGrid(tiles.items, grid, grid_width); + + // convert grid to image + var image = try allocator.alloc(u1, grid.len * (tile_width - 2) * (tile_width - 2)); + defer allocator.free(image); + + const image_width = grid_width * (tile_width - 2); + var y: u32 = 0; + while (y < grid_width) : (y += 1) { + var x: u32 = 0; + while (x < grid_width) : (x += 1) { + var tile = tiles.items[grid[y * grid_width + x].?]; + var doff = (y * grid_width * (tile_width - 2) + x) * (tile_width - 2); + var dy: u32 = 1; + while (dy < tile_width - 1) : (dy += 1) { + var dx: u32 = 1; + while (dx < tile_width - 1) : (dx += 1) { + image[doff + (dy - 1) * image_width + (dx - 1)] = tile.data[dy * tile_width + dx]; + } + } + } + } + + y = 0; + while (y < grid_width * (tile_width - 2)) : (y += 1) { + printTileSlice(image[y * grid_width * (tile_width - 2) .. (y + 1) * grid_width * (tile_width - 2)]); + aoc.debugfmt("\n", .{}); + } + + // rotate image till we find a sea monster + var image_copy = try allocator.dupe(u1, image); + defer allocator.free(image_copy); + + var rot: usize = 0; + while (rot < 4) : (rot += 1) { + for (flips) |flip| { + flipData(image, image_width, flip); + rotateData(&image_copy, image, image_width, rot); + + y = 0; + var count: u32 = 0; + while (y < image_width - seamonster.len) : (y += 1) { + var x: u32 = 0; + check_next: while (x < image_width - seamonster[0].len) : (x += 1) { + var sy: u32 = 0; + while (sy < seamonster.len) : (sy += 1) { + var sx: u32 = 0; + while (sx < seamonster[0].len) : (sx += 1) { + if (seamonster[sy][sx] == '#' and image_copy[(y + sy) * image_width + (x + sx)] != 1) + continue :check_next; + } + } + count += 1; + } + } + + if (count > 0) { + var image_count = std.mem.count(u1, image_copy, ([_]u1{1})[0..]); + var seamonster_count: usize = 0; + var sy: u32 = 0; + while (sy < seamonster.len) : (sy += 1) { + seamonster_count += std.mem.count(u8, seamonster[sy], "#"); + } + const answer = image_count - count * seamonster_count; + return try std.fmt.allocPrint(allocator, "{}", .{answer}); + } + } + } + + return null; +} + +pub const main = aoc.main(part1, part2, .{ "54755174472007", "1692" }); diff --git a/src/day20/part1 b/src/20/part1 diff --git a/src/day20/part2 b/src/20/part2 diff --git a/src/day21/input b/src/21/input diff --git a/src/day21/input-test b/src/21/input-test diff --git a/src/21/main.zig b/src/21/main.zig @@ -0,0 +1,188 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const hasher = std.hash.CityHash32; +const IngredientInfo = struct { + count: u32, +}; +const Pair = struct { ingredient: []const u8, allergen: []const u8 }; + +fn freeListmap(listmap: *std.AutoHashMap(u32, std.ArrayList(u32))) void { + var mapit = listmap.iterator(); + while (mapit.next()) |kv| { + kv.value_ptr.deinit(); + } + listmap.deinit(); +} + +fn getAllergen(allergens: *std.AutoHashMap(u32, std.ArrayList(u32)), ingredient: u32) ?u32 { + var allergenit = allergens.iterator(); + while (allergenit.next()) |alg| { + if (alg.value_ptr.items.len == 1 and alg.value_ptr.items[0] == ingredient) + return alg.key_ptr.*; + } + return null; +} + +fn updateAllergens(allocator: std.mem.Allocator, allergens: *std.AutoHashMap(u32, std.ArrayList(u32)), allergen: u32) !void { + var next = std.ArrayList(u32).init(allocator); + defer next.deinit(); + + try next.append(allergen); + + while (next.items.len > 0) { + const key = next.items[0]; + var ingredient = allergens.getEntry(key).?.value_ptr.items[0]; + var mapit = allergens.iterator(); + while (mapit.next()) |alg| { + if (alg.key_ptr.* == key) continue; + const ind = std.mem.indexOfScalar(u32, alg.value_ptr.items, ingredient); + if (ind != null) { + _ = alg.value_ptr.swapRemove(ind.?); + if (alg.value_ptr.items.len == 1) { + try next.append(alg.key_ptr.*); + } + } + } + _ = next.swapRemove(0); + } +} + +fn parseAllergens(allocator: std.mem.Allocator, allergens: *std.AutoHashMap(u32, std.ArrayList(u32)), unhash: *std.AutoHashMap(u32, []const u8), ingredient_counts: *std.AutoHashMap(u32, u32), input: []const u8) !void { + var ingredients = std.ArrayList(u32).init(allocator); + defer ingredients.deinit(); + + var lineit = std.mem.tokenize(u8, input, "\n"); + while (lineit.next()) |line| { + var allergen = false; + + try ingredients.resize(0); + + var spaceit = std.mem.tokenize(u8, line, " "); + while (spaceit.next()) |word| { + if (std.mem.eql(u8, word, "(contains")) { + allergen = true; + continue; + } + + if (allergen) { + const allergen_name = if (word[word.len - 1] == ')' or word[word.len - 1] == ',') word[0 .. word.len - 1] else word; + const allergen_hash = hasher.hash(allergen_name); + try unhash.put(allergen_hash, allergen_name); + var algentry = allergens.getEntry(allergen_hash); + if (algentry) |algent| { + var i: u32 = 0; + while (i < algent.value_ptr.items.len) { + if (std.mem.indexOfScalar(u32, ingredients.items, algent.value_ptr.items[i]) == null) { + _ = algent.value_ptr.swapRemove(i); + } else { + i += 1; + } + } + + if (algent.value_ptr.items.len == 1) { + try updateAllergens(allocator, allergens, algent.key_ptr.*); + } + } else { + var copy = std.ArrayList(u32).init(allocator); + errdefer copy.deinit(); + for (ingredients.items) |ing| { + if (getAllergen(allergens, ing) == null) { + try copy.append(ing); + } + } + try allergens.put(allergen_hash, copy); + } + } else { + const ingredient_hash = hasher.hash(word); + try unhash.put(ingredient_hash, word); + try ingredients.append(ingredient_hash); + const entry = ingredient_counts.getEntry(ingredient_hash); + if (entry == null) { + try ingredient_counts.put(ingredient_hash, 1); + } else { + entry.?.value_ptr.* += 1; + } + } + } + } +} + +fn ascendingAllergen(ctx: void, p1: Pair, p2: Pair) bool { + _ = ctx; + var i: u32 = 0; + while (i < std.math.min(p1.allergen.len, p2.allergen.len) and p1.allergen[i] == p2.allergen[i]) : (i += 1) {} + return p1.allergen[i] < p2.allergen[i]; +} + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var allergens = std.AutoHashMap(u32, std.ArrayList(u32)).init(allocator); + defer freeListmap(&allergens); + + var ingredient_counts = std.AutoHashMap(u32, u32).init(allocator); + defer ingredient_counts.deinit(); + + var unhash = std.AutoHashMap(u32, []const u8).init(allocator); + defer unhash.deinit(); + + try parseAllergens(allocator, &allergens, &unhash, &ingredient_counts, input); + + var answer: u32 = 0; + + var algit = allergens.iterator(); + while (algit.next()) |alg| { + aoc.debugfmt("{} - ", .{alg.key_ptr.*}); + for (alg.value_ptr.items) |v| { + aoc.debugfmt("{} ", .{v}); + } + aoc.debugfmt("\n", .{}); + } + + var mapit = ingredient_counts.iterator(); + while (mapit.next()) |kv| { + if (getAllergen(&allergens, kv.key_ptr.*) == null) answer += kv.value_ptr.*; + } + + return try std.fmt.allocPrint(allocator, "{}", .{answer}); +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var allergens = std.AutoHashMap(u32, std.ArrayList(u32)).init(allocator); + defer freeListmap(&allergens); + + var ingredient_counts = std.AutoHashMap(u32, u32).init(allocator); + defer ingredient_counts.deinit(); + + var unhash = std.AutoHashMap(u32, []const u8).init(allocator); + defer unhash.deinit(); + + try parseAllergens(allocator, &allergens, &unhash, &ingredient_counts, input); + + var pairs = std.ArrayList(Pair).init(allocator); + defer pairs.deinit(); + + var mapit = ingredient_counts.iterator(); + while (mapit.next()) |kv| { + const alg = getAllergen(&allergens, kv.key_ptr.*); + if (alg != null) { + try pairs.append(Pair{ .ingredient = unhash.get(kv.key_ptr.*).?, .allergen = unhash.get(alg.?).? }); + } + } + + std.sort.sort(Pair, pairs.items, {}, ascendingAllergen); + + var parts = std.ArrayList([]const u8).init(allocator); + defer parts.deinit(); + for (pairs.items) |p, i| { + if (i > 0) try parts.append(","); + try parts.append(p.ingredient); + } + return try std.mem.join(allocator, "", parts.items); +} + +const sol2 = "fqhpsl,zxncg,clzpsl,zbbnj,jkgbvlxh,dzqc,ppj,glzb"; +pub const main = aoc.main(part1, part2, .{ "2584", sol2 }); diff --git a/src/day21/part1 b/src/21/part1 diff --git a/src/day21/part2 b/src/21/part2 diff --git a/src/day22/input b/src/22/input diff --git a/src/day22/input-test b/src/22/input-test diff --git a/src/day22/input-test2 b/src/22/input-test2 diff --git a/src/22/main.zig b/src/22/main.zig @@ -0,0 +1,181 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const Player = enum { P1, P2 }; +const GameResult = struct { score: u64, winner: Player }; + +fn parseInput(input: []u8, decks: [2]*std.ArrayList(u32)) !void { + var partit = std.mem.split(u8, input, "\n\n"); + var decknum: u32 = 0; + while (partit.next()) |part| : (decknum += 1) { + if (decknum == 2) break; + var lineit = std.mem.tokenize(u8, part, "\n"); + _ = lineit.next(); + while (lineit.next()) |line| { + try decks[decknum].append(try std.fmt.parseInt(u32, line, 10)); + } + } + if (decknum < 2) return aoc.Error.InvalidInput; +} + +fn printDeck(deck: *std.ArrayList(u32)) void { + for (deck.items) |v, i| { + if (i > 0) aoc.debugfmt(", ", .{}); + aoc.debugfmt("{}", .{v}); + } +} + +fn printRoundInfo(roundnum: u32, decks: [2]*std.ArrayList(u32)) void { + aoc.debugfmt("\n-- ROUND {} --\n", .{roundnum}); + aoc.debugfmt("Player 1's deck: ", .{}); + printDeck(decks[0]); + aoc.debugfmt("\nPlayer 2's deck: ", .{}); + printDeck(decks[1]); + aoc.debugfmt("\n", .{}); +} + +fn printPickedCards(card1: u32, card2: u32) void { + aoc.debugfmt("Player 1 plays: {}\n", .{card1}); + aoc.debugfmt("Player 2 plays: {}\n", .{card2}); +} + +fn calcScore(deck: *std.ArrayList(u32)) u64 { + var score: u64 = 0; + for (deck.items) |v, i| { + score += v * (deck.items.len - i); + } + return score; +} + +fn copyList(allocator: std.mem.Allocator, list: []u32) !std.ArrayList(u32) { + var newlist = std.ArrayList(u32).init(allocator); + errdefer newlist.deinit(); + + for (list) |v| { + try newlist.append(v); + } + + return newlist; +} + +fn part1Round(allocator: std.mem.Allocator, roundnum: u32, decks: [2]*std.ArrayList(u32)) anyerror!void { + _ = allocator; + if (aoc.debug) printRoundInfo(roundnum, decks); + const card1: u32 = decks[0].orderedRemove(0); + const card2: u32 = decks[1].orderedRemove(0); + if (aoc.debug) printPickedCards(card1, card2); + if (card1 > card2) { + if (aoc.debug) aoc.debugfmt("Player1 wins\n", .{}); + try decks[0].append(card1); + try decks[0].append(card2); + } else if (card2 > card1) { + if (aoc.debug) aoc.debugfmt("Player2 wins\n", .{}); + try decks[1].append(card2); + try decks[1].append(card1); + } else return aoc.Error.InvalidInput; +} + +fn part2Round( + allocator: std.mem.Allocator, + roundnum: u32, + decks: [2]*std.ArrayList(u32), +) anyerror!void { + if (aoc.debug) printRoundInfo(roundnum, decks); + const card1: u32 = decks[0].orderedRemove(0); + const card2: u32 = decks[1].orderedRemove(0); + if (aoc.debug) printPickedCards(card1, card2); + + var winner = @intToEnum(Player, @boolToInt(card2 > card1)); + if (card1 <= decks[0].items.len and card2 <= decks[1].items.len) { + if (aoc.debug) aoc.debugfmt("\nStarting subgame..\n", .{}); + var tmp_deck1 = try copyList(allocator, decks[0].items[0..card1]); + defer tmp_deck1.deinit(); + + var tmp_deck2 = try copyList(allocator, decks[1].items[0..card2]); + defer tmp_deck2.deinit(); + + const result = try playGame(allocator, [2]*std.ArrayList(u32){ &tmp_deck1, &tmp_deck2 }, part2Round); + winner = result.winner; + if (aoc.debug) { + const wp: u32 = if (winner == Player.P1) 1 else 2; + aoc.debugfmt("Player{} wins the subgame\n...anyway, back to previous game\n\n", .{wp}); + } + } + + if (winner == Player.P1) { + if (aoc.debug) aoc.debugfmt("Player1 wins the round\n", .{}); + try decks[0].append(card1); + try decks[0].append(card2); + } else if (winner == Player.P2) { + if (aoc.debug) aoc.debugfmt("Player2 wins the round\n", .{}); + try decks[1].append(card2); + try decks[1].append(card1); + } else return aoc.Error.InvalidInput; +} + +fn playGame( + allocator: std.mem.Allocator, + decks: [2]*std.ArrayList(u32), + roundFunc: *const fn ( + alloc: std.mem.Allocator, + num: u32, + decks: [2]*std.ArrayList(u32), + ) anyerror!void, +) !GameResult { + var ctxstack = std.ArrayList(u64).init(allocator); + defer ctxstack.deinit(); + + var roundnum: u32 = 1; + while (decks[0].items.len > 0 and decks[1].items.len > 0) : (roundnum += 1) { + try roundFunc(allocator, roundnum, decks); + + var hash: u64 = calcScore(decks[0]) + 100000 * calcScore(decks[1]); + if (std.mem.indexOfScalar(u64, ctxstack.items, hash) != null) { + return GameResult{ .score = calcScore(decks[0]), .winner = Player.P1 }; + } + + try ctxstack.append(hash); + } + + var nonempty = if (decks[0].items.len != 0) decks[0] else decks[1]; + return GameResult{ + .score = calcScore(nonempty), + .winner = if (nonempty == decks[0]) Player.P1 else Player.P2, + }; +} + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var deck1 = std.ArrayList(u32).init(allocator); + defer deck1.deinit(); + + var deck2 = std.ArrayList(u32).init(allocator); + defer deck2.deinit(); + + const decks = [2]*std.ArrayList(u32){ &deck1, &deck2 }; + try parseInput(input, decks); + + const result = try playGame(allocator, decks, part1Round); + + return try std.fmt.allocPrint(allocator, "{}", .{result.score}); +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var deck1 = std.ArrayList(u32).init(allocator); + defer deck1.deinit(); + + var deck2 = std.ArrayList(u32).init(allocator); + defer deck2.deinit(); + + const decks = [2]*std.ArrayList(u32){ &deck1, &deck2 }; + try parseInput(input, decks); + + const result = try playGame(allocator, decks, part2Round); + + return try std.fmt.allocPrint(allocator, "{}", .{result.score}); +} + +pub const main = aoc.main(part1, part2, .{ "32272", "33206" }); diff --git a/src/day22/part1 b/src/22/part1 diff --git a/src/day22/part2 b/src/22/part2 diff --git a/src/day23/input b/src/23/input diff --git a/src/day23/input-test b/src/23/input-test diff --git a/src/23/main.zig b/src/23/main.zig @@ -0,0 +1,194 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +// Links in this list are arranges in a circle +const RingLink = struct { + data: u32, + next: *RingLink, + const Self = @This(); + + pub fn insertNext(self: *Self, link: *RingLink) void { + link.next = self.next; + self.next = link; + } + + pub fn popNext(self: *Self) !*Self { + const nlink = self.next; + if (nlink == self) return error.EmptyRingList; + self.next = nlink.next; + return nlink; + } + + pub fn length(self: *Self) usize { + var count: usize = 1; + var link = self.next; + while (link != self) : (link = link.next) { + count += 1; + } + return count; + } + + pub fn advance(self: *Self, count: usize) *RingLink { + var link = self; + var counter: usize = 0; + while (counter < count) : (link = link.next) { + counter += 1; + } + return link; + } +}; + +fn printLinks(start: *RingLink, end: *RingLink, skipfirst: bool) void { + var link = start; + var b = skipfirst; + while (b or link != end) : (link = link.next) { + b = false; + aoc.debugfmt("{} ", .{link.data}); + } +} + +fn parseInput(input: []const u8, lookup: []RingLink, list: *?*RingLink) !u32 { + var max: u32 = 0; + var link: *RingLink = undefined; + for (input) |c, i| { + if (c == '\n') break; + const v = try std.fmt.parseInt(u32, input[i .. i + 1], 10); + if (v > max) max = v; + if (list.* == null) { + list.* = &lookup[v]; + link = list.*.?; + link.next = link; + } else { + link.insertNext(&lookup[v]); + link = link.next; + } + } + return max; +} + +fn doRound(list: *RingLink, len: u32, lookup: []RingLink, current: **RingLink, max: u32, round: usize) !void { + _ = list; + var target = (current.*.data + max - 2) % max + 1; + var k: usize = 0; + var check = current.*.next; + while (k < 9) : (k += 1) { + if (check.data == target) + target = (target + max - 2) % max + 1; + + check = if (k % 3 == 0) current.*.next else check.next; + } + var closest = &lookup[target]; + + if (aoc.debug) { + aoc.debugfmt("\n-- move {} --\ncups:", .{round}); + + if (aoc.debuglvl == 1) { + var start = current.*.advance(len - ((round - 1) % len)); + var link = start; + while (true) { + if (link == current.*) { + aoc.debugfmt(" ({})", .{link.data}); + } else { + aoc.debugfmt(" {} ", .{link.data}); + } + link = link.next; + if (link == start) break; + } + aoc.debugfmt("\n", .{}); + } else { + aoc.debugfmt("\n{} {} {}\n", .{ current.*.data, target, closest.data }); + aoc.debugfmt(".. ", .{}); + printLinks(current.*, current.*.next.next.next.next, false); + aoc.debugfmt("..\n.. ", .{}); + printLinks(closest, closest.next.next.next.next, false); + aoc.debugfmt("..\n", .{}); + } + } + + var i: usize = 0; + while (i < 3) : (i += 1) { + const poplink = try current.*.popNext(); + closest.insertNext(poplink); + closest = poplink; + } + + current.* = current.*.next; +} + +fn createLookup(allocator: std.mem.Allocator, len: usize) ![]RingLink { + var lookup = try allocator.alloc(RingLink, len + 1); + errdefer allocator.free(lookup); + + var i: usize = 1; + while (i <= len) : (i += 1) { + lookup[i] = RingLink{ .data = @intCast(u32, i), .next = undefined }; + } + + return lookup; +} + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var lookup = try createLookup(allocator, 9); + defer allocator.free(lookup); + + var list: ?*RingLink = null; + const max = try parseInput(input, lookup, &list); + if (list == null) return aoc.Error.InvalidInput; + const real_len = @intCast(u32, list.?.length()); + + var round: usize = 0; + var current = list.?; + while (round < 100) : (round += 1) { + try doRound(list.?, real_len, lookup, &current, max, round + 1); + } + + var start = &lookup[1]; + var link = start.next; + + var parts = std.ArrayList(u8).init(allocator); + defer parts.deinit(); + while (link != start) : (link = link.next) { + try parts.append('0' + @intCast(u8, link.data)); + } + + return try std.fmt.allocPrint(allocator, "{s}", .{parts.items}); +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var lookup = try createLookup(allocator, 1000000); + defer allocator.free(lookup); + + var list: ?*RingLink = null; + _ = try parseInput(input, lookup, &list); + if (list == null) return aoc.Error.InvalidInput; + const real_len = list.?.length(); + + var end = list.?.advance(real_len - 1); + var i = real_len + 1; + while (i <= 1000000) : (i += 1) { + end.insertNext(&lookup[i]); + end = end.next; + } + + var round: usize = 0; + var current = list.?; + while (round < 10000000) : (round += 1) { + if (round % 1000000 == 0) aoc.debugfmt(".", .{}); + try doRound(list.?, 1000000, lookup, &current, 1000000, round + 1); + } + aoc.debugfmt("\n", .{}); + + var link = (&lookup[1]).next; + + const a = @intCast(u64, link.data); + const b = @intCast(u64, link.next.data); + aoc.debugfmt("{} * {} = {}\n", .{ a, b, a * b }); + + return try std.fmt.allocPrint(allocator, "{}", .{a * b}); +} + +pub const main = aoc.main(part1, part2, .{ "97245386", "156180332979" }); diff --git a/src/day23/notes b/src/23/notes diff --git a/src/day23/part1 b/src/23/part1 diff --git a/src/day23/part2 b/src/23/part2 diff --git a/src/day24/input b/src/24/input diff --git a/src/day24/input-test b/src/24/input-test diff --git a/src/24/main.zig b/src/24/main.zig @@ -0,0 +1,125 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const Color = enum { BLACK, WHITE }; +const Dir = enum { E, SE, SW, W, NW, NE }; +const dirs = [_]aoc.Pos{ + aoc.Pos{ .x = 2, .y = 0 }, + aoc.Pos{ .x = 1, .y = -1 }, + aoc.Pos{ .x = -1, .y = -1 }, + aoc.Pos{ .x = -2, .y = 0 }, + aoc.Pos{ .x = -1, .y = 1 }, + aoc.Pos{ .x = 1, .y = 1 }, +}; +const tokens = [_][]const u8{ + "e", + "se", + "sw", + "w", + "nw", + "ne", +}; +const Tile = struct { + color: Color, +}; + +fn parseInput(tiles: *std.AutoHashMap(aoc.Pos, Tile), input: []const u8) !void { + var lineit = std.mem.tokenize(u8, input, "\n"); + while (lineit.next()) |line| { + var pos = aoc.Pos{ .x = 0, .y = 0 }; + + var i: usize = 0; + while (i < line.len) { + var dir = for (tokens) |tok, ti| { + if (i + tok.len > line.len) continue; + if (std.mem.eql(u8, tok, line[i .. i + tok.len])) { + i += tok.len; + break @intToEnum(Dir, @intCast(u3, ti)); + } + } else return aoc.Error.InvalidInput; + if (aoc.debug) std.debug.print("{} ", .{dir}); + pos = pos.add(dirs[@enumToInt(dir)]); + } + if (aoc.debug) std.debug.print("=> {} {}\n", .{ pos.x, pos.y }); + + var tile = tiles.getEntry(pos); + if (tile != null) { + tile.?.value_ptr.color = if (tile.?.value_ptr.color == Color.WHITE) Color.BLACK else Color.WHITE; + } else { + try tiles.put(pos, Tile{ .color = Color.BLACK }); + } + } +} + +fn applyRule(pos: aoc.Pos, before: *std.AutoHashMap(aoc.Pos, Tile), after: *std.AutoHashMap(aoc.Pos, Tile)) !void { + if (after.contains(pos)) return; + const old_tile = before.get(pos); + const old_color = if (old_tile == null) Color.WHITE else old_tile.?.color; + + var adj: u32 = 0; + for (dirs) |d| { + const tile = before.get(pos.add(d)); + if (tile != null and tile.?.color == Color.BLACK) adj += 1; + } + + if (adj == 2 and old_color == Color.WHITE) { + try after.put(pos, Tile{ .color = Color.BLACK }); + } else if ((adj == 0 or adj > 2) and old_color == Color.BLACK) { + try after.put(pos, Tile{ .color = Color.WHITE }); + } else { + try after.put(pos, Tile{ .color = old_color }); + } +} + +fn doRound(allocator: std.mem.Allocator, tiles: *std.AutoHashMap(aoc.Pos, Tile)) !void { + var result = std.AutoHashMap(aoc.Pos, Tile).init(allocator); + defer result.deinit(); + + var mapit = tiles.iterator(); + while (mapit.next()) |kv| { + for (dirs) |d| { + try applyRule(kv.key_ptr.add(d), tiles, &result); + } + try applyRule(kv.key_ptr.*, tiles, &result); + } + + std.mem.swap(std.AutoHashMap(aoc.Pos, Tile), &result, tiles); +} + +fn countBlack(tiles: *std.AutoHashMap(aoc.Pos, Tile)) u32 { + var count: u32 = 0; + var mapit = tiles.iterator(); + while (mapit.next()) |kv| { + if (kv.value_ptr.color == Color.BLACK) count += 1; + } + return count; +} + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var tiles = std.AutoHashMap(aoc.Pos, Tile).init(allocator); + defer tiles.deinit(); + + try parseInput(&tiles, input); + + return try std.fmt.allocPrint(allocator, "{}", .{countBlack(&tiles)}); +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = args; + + var tiles = std.AutoHashMap(aoc.Pos, Tile).init(allocator); + defer tiles.deinit(); + + try parseInput(&tiles, input); + + var round: usize = 0; + while (round < 100) : (round += 1) { + try doRound(allocator, &tiles); + } + + return try std.fmt.allocPrint(allocator, "{}", .{countBlack(&tiles)}); +} + +pub const main = aoc.main(part1, part2, .{ "317", "3804" }); diff --git a/src/day24/part1 b/src/24/part1 diff --git a/src/day24/part2 b/src/24/part2 diff --git a/src/day25/input b/src/25/input diff --git a/src/day25/input-test b/src/25/input-test diff --git a/src/25/main.zig b/src/25/main.zig @@ -0,0 +1,58 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +fn transform(subject_num: u64, loops: u64) u64 { + var num: u64 = 1; + var i: u64 = 0; + while (i < loops) : (i += 1) { + num *= subject_num; + num %= 20201227; + } + return num; +} + +fn bfLoops(subject_num: u64, pubkey: u64) ?u64 { + var i: u64 = 0; + var tmp: u64 = 1; + while (i < ~@as(u64, 0)) : (i += 1) { + if (tmp == pubkey) return i; + tmp *= subject_num; + tmp %= 20201227; + } + return null; +} + +fn parseInput(door_pubkey: *u64, card_pubkey: *u64, input: []const u8) !void { + var lineit = std.mem.tokenize(u8, input, "\n"); + door_pubkey.* = try std.fmt.parseInt(u64, try aoc.unwrap(lineit.next()), 10); + card_pubkey.* = try std.fmt.parseInt(u64, try aoc.unwrap(lineit.next()), 10); +} + +fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + var door_pubkey: u64 = undefined; + var card_pubkey: u64 = undefined; + + try parseInput(&door_pubkey, &card_pubkey, input); + + if (args.len == 0 or std.mem.eql(u8, args[0], "bf_door")) { + if (bfLoops(7, door_pubkey)) |door_loops| { + std.debug.print("{}\n", .{transform(card_pubkey, door_loops)}); + return null; + } + } else if (args.len > 0 and std.mem.eql(u8, args[0], "bf_card")) { + if (bfLoops(7, card_pubkey)) |card_loops| { + const answer = transform(door_pubkey, card_loops); + return try std.fmt.allocPrint(allocator, "{}", .{answer}); + } + } + return null; +} + +fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { + _ = input; + _ = args; + + return try std.fmt.allocPrint(allocator, "", .{}); +} + +pub const main = aoc.main(part1, part2, .{ "15217943", "" }); diff --git a/src/day25/part1 b/src/25/part1 diff --git a/src/day25/part2 b/src/25/part2 diff --git a/src/Makefile b/src/Makefile @@ -0,0 +1,21 @@ +DAYS = $(shell seq 1 25 | xargs printf "%02i\n") + +.PHONY: all +all:: + +define make-day +all:: $1/run +.PHONY: $1/run +$1/main: $1/main.zig + zig build-exe \ + --pkg-begin "aoc" "common/aoc.zig" --pkg-end \ + --pkg-begin "console8" "common/console8.zig" --pkg-end \ + --cache-dir .cache/zig -femit-bin=$1/main -freference-trace \ + $1/main.zig +$1/run: $1/main + @echo "== day $1 ==" + @echo -en "\npart 1: " && cd $1 && time ./main 1 + @echo -en "\npart 2: " && cd $1 && time ./main 2 + @echo "" +endef +$(foreach day,$(DAYS),$(eval $(call make-day,$(day)))) diff --git a/src/common/aoc.zig b/src/common/aoc.zig @@ -0,0 +1,145 @@ +const std = @import("std"); + +pub const Error = error{InvalidInput}; + +pub var debug = false; +pub var debuglvl: u32 = 0; + +pub fn debugfmt(comptime fmt: []const u8, args: anytype) void { + if (debug) + std.debug.print(fmt, args); +} + +const part_type = fn (alloc: std.mem.Allocator, input: []u8, args: [][]u8) anyerror!?[]u8; +pub fn main(comptime part1: part_type, comptime part2: part_type, comptime sols: [2]?[]const u8) fn () anyerror!void { + const impl = struct { + fn main() !void { + const envvar = std.os.getenv("AOC_DEBUG"); + if (envvar != null) { + debuglvl = try std.fmt.parseInt(u32, envvar.?, 10); + debug = debuglvl != 0; + } + + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer _ = gpa.deinit(); + var heapalloc = gpa.allocator(); + + const args = try std.process.argsAlloc(heapalloc); + defer heapalloc.free(args); + if (args.len < 2) return; + + var filename: []const u8 = std.mem.span("input"); + for (std.os.environ) |v| { + const kv = std.mem.span(v); + if (std.mem.indexOfScalar(u8, kv, '=')) |sep| { + if (sep == kv.len - 1) continue; + if (std.mem.eql(u8, kv[0..sep], "AOC_INPUT")) { + filename = kv[sep + 1 ..]; + std.debug.print("Using input file: {s}\n", .{filename}); + break; + } else if (std.mem.eql(u8, kv[0..sep], "AOCDEBUG")) { + debug = true; + debuglvl = try std.fmt.parseInt(u32, kv[sep + 1 ..], 10); + } + } + } + + const file = try std.fs.cwd().openFile(filename, .{}); + const text = try file.reader().readAllAlloc(heapalloc, std.math.maxInt(u32)); + defer heapalloc.free(text); + + var part = try std.fmt.parseInt(u8, args[1], 10); + var answer: ?[]u8 = null; + switch (part) { + 1 => answer = try part1(heapalloc, text, args[2..]), + 2 => answer = try part2(heapalloc, text, args[2..]), + else => { + std.debug.print("Invalid part number!\n", .{}); + std.os.exit(1); + }, + } + part = part - 1; + + if (answer != null) { + const stdout = std.io.getStdOut().writer(); + try std.fmt.format(stdout, "{s}\n", .{answer.?}); + if (sols[part] != null and !std.mem.eql(u8, answer.?, sols[part].?)) { + std.debug.print("Invalid answer\n", .{}); + std.os.exit(1); + } + heapalloc.free(answer.?); + } + + if (sols[part] == null) { + std.debug.print("Missing solution\n", .{}); + std.os.exit(1); + } + } + }; + return impl.main; +} + +pub const Pos = struct { + x: i64, + y: i64, + const Self = @This(); + + pub fn add(self: Self, other: Self) Self { + return Self{ .x = self.x + other.x, .y = self.y + other.y }; + } + + pub fn mult(self: Self, val: i64) Self { + return Self{ .x = self.x * val, .y = self.y * val }; + } +}; + +pub const Dir = struct { + pub const East = Pos{ .x = 1, .y = 0 }; + pub const West = Pos{ .x = -1, .y = 0 }; + pub const South = Pos{ .x = 0, .y = -1 }; + pub const North = Pos{ .x = 0, .y = 1 }; + + pub const Name = enum(u8) { NORTH = 0, EAST = 1, SOUTH = 2, WEST = 3 }; + pub const dirs = [_]Pos{ North, East, South, West }; + + pub fn get(name: Name) Pos { + return dirs[@enumToInt(name)]; + } + + pub fn nextCW(dir: usize, offset: usize) usize { + return (dir + @intCast(u32, offset)) % @intCast(u32, dirs.len); + } + + pub fn nextCCW(dir: usize, offset: usize) usize { + const constrained = offset % dirs.len; + if (dir >= constrained) { + return dir - constrained; + } else { + return dirs.len - (constrained - dir); + } + } + + const cos90vs = [_]i32{ 1, 0, -1, 0 }; + const sin90vs = [_]i32{ 0, 1, 0, -1 }; + + pub fn rotCW(pos: Pos, offset: usize) Pos { + const constrained = (4 - offset % 4) % 4; + return Pos{ + .x = cos90vs[constrained] * pos.x - sin90vs[constrained] * pos.y, + .y = sin90vs[constrained] * pos.x + cos90vs[constrained] * pos.y, + }; + } + + pub fn rotCCW(pos: Pos, offset: usize) Pos { + const constrained = offset % 4; + return Pos{ + .x = cos90vs[constrained] * pos.x - sin90vs[constrained] * pos.y, + .y = sin90vs[constrained] * pos.x + cos90vs[constrained] * pos.y, + }; + } +}; + +pub fn unwrap(v: anytype) !@TypeOf(v.?) { + if (v == null) return Error.InvalidInput; + return v.?; +} diff --git a/src/common/console8.zig b/src/common/console8.zig @@ -0,0 +1,91 @@ +const std = @import("std"); +const aoc = @import("aoc.zig"); + +pub const OpError = error{ InstructionPointerOOB, InvalidFormat, InstructionUnknown }; +pub const OpFuncSig = fn (ctx: *Console, arg: i64) OpError!void; +pub const Instruction = struct { opcode: []const u8, opfunc: *const OpFuncSig, argval: i64 }; + +pub const Console = struct { + accumulator: i64 = 0, + instructptr: u64 = 0, + + jumpAddr: i65 = 0, + + code: []const u8, + instlist: [][]const u8, + allocator: std.mem.Allocator, + const Self = @This(); + + pub fn init(code: []const u8, allocator: std.mem.Allocator) !Self { + var instvec = std.ArrayList([]const u8).init(allocator); + errdefer instvec.deinit(); + + var instit = std.mem.tokenize(u8, code, "\n"); + while (instit.next()) |inst| { + try instvec.append(inst); + } + return Console{ + .code = code, + .instlist = instvec.toOwnedSlice(), + .allocator = allocator, + }; + } + + pub fn deinit(self: *Self) void { + self.allocator.free(self.instlist); + } + + pub fn reset(self: *Self) void { + self.accumulator = 0; + self.instructptr = 0; + } + + const instructionMap = std.ComptimeStringMap(*const OpFuncSig, .{ + .{ "jmp", jumpInstruction }, + .{ "acc", accInstruction }, + .{ "nop", nopInstruction }, + }); + + pub fn jumpInstruction(self: *Self, arg: i64) OpError!void { + self.jumpAddr = @intCast(i65, self.instructptr) + @intCast(i65, arg); + if (self.jumpAddr < 0 or self.jumpAddr >= self.instlist.len) + return error.InstructionPointerOOB; + self.instructptr = @intCast(u64, self.jumpAddr); + } + + pub fn accInstruction(self: *Self, arg: i64) OpError!void { + self.accumulator += arg; + self.instructptr += 1; + } + + pub fn nopInstruction(self: *Self, arg: i64) OpError!void { + _ = arg; + self.instructptr += 1; + } + + pub fn parseNext(self: *Self) !?Instruction { + if (self.instructptr >= self.instlist.len) + return null; + + const inststr = self.instlist[self.instructptr]; + const sep = std.mem.indexOfScalar(u8, inststr, ' '); + if (sep == null) return OpError.InvalidFormat; + + const opcode = inststr[0..sep.?]; + if (instructionMap.get(opcode)) |opfunc| { + const arg = inststr[sep.? + 1 ..]; + const val = std.fmt.parseInt(i64, arg, 10) catch { + aoc.debugfmt("Failed to parse arg value: {s}\n", .{arg}); + return OpError.InvalidFormat; + }; + return Instruction{ .opcode = opcode, .opfunc = opfunc, .argval = val }; + } else { + aoc.debugfmt("Unknown instruction: {s}\n", .{inststr}); + return OpError.InstructionUnknown; + } + } + + pub fn exec(self: *Self, inst: *Instruction) !void { + try inst.opfunc(self, inst.argval); + } +}; diff --git a/src/day1/main.zig b/src/day1/main.zig @@ -1,67 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); - -const Parts = struct { - a: u32, b: u32 -}; - -fn findparts(intlist: *const std.ArrayList(u32), sum: u32) ?Parts { - var start: usize = 0; - const items = intlist.items; - var end: usize = items.len - 1; - while (start != end) { - const csum = items[start] + items[end]; - if (csum == sum) { - return Parts{ .a = items[start], .b = items[end] }; - } else if (csum > sum) { - end -= 1; - } else { - start += 1; - } - } - return null; -} - -fn parse(allocator: *std.mem.Allocator, input: []u8) !std.ArrayList(u32) { - var intlist = std.ArrayList(u32).init(allocator); - errdefer intlist.deinit(); - - var it = std.mem.tokenize(input, "\n"); - while (it.next()) |line| { - try intlist.append(try std.fmt.parseInt(u32, line, 10)); - } - - return intlist; -} - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - const intlist = try parse(allocator, input); - defer intlist.deinit(); - - std.sort.sort(u32, intlist.items, {}, comptime std.sort.asc(u32)); - - if (findparts(&intlist, 2020)) |parts| { - std.debug.print("{}\n", .{parts.a * parts.b}); - } -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - const intlist = try parse(allocator, input); - defer intlist.deinit(); - - std.sort.sort(u32, intlist.items, {}, comptime std.sort.asc(u32)); - - const target: u16 = 2020; - var third: u32 = 0; - while (third < intlist.items.len) : (third += 1) { - const tmp = intlist.items[third]; - intlist.items[third] = target + 1; - if (findparts(&intlist, target - tmp)) |parts| { - std.debug.print("{}\n", .{parts.a * parts.b * tmp}); - return; - } - intlist.items[third] = tmp; - } -} - -pub const main = aoc.gen_main(part1, part2); diff --git a/src/day10/main.zig b/src/day10/main.zig @@ -1,154 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); - -fn part1(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 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 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); - - 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/day11/main.zig b/src/day11/main.zig @@ -1,161 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); - -const SeatState = enum { EMPTY, FLOOR, TAKEN }; -const MapDims = struct { width: usize, height: usize }; -const Dir = struct { x: i8, y: i8 }; -const Pos = struct { x: i128, y: i128 }; - -const adjacent = [_]Dir{ - Dir{ .x = -1, .y = -1 }, - Dir{ .x = -0, .y = -1 }, - Dir{ .x = 1, .y = -1 }, - Dir{ .x = -1, .y = 0 }, - Dir{ .x = 1, .y = 0 }, - Dir{ .x = -1, .y = 1 }, - Dir{ .x = 0, .y = 1 }, - Dir{ .x = 1, .y = 1 }, -}; - -fn parseMap(mapitems: *[]SeatState, dims: *MapDims, input: []const u8, allocator: *std.mem.Allocator) !void { - var lineit = std.mem.tokenize(input, "\n"); - var map = std.ArrayList(SeatState).init(allocator); - errdefer map.deinit(); - while (lineit.next()) |line| { - if (dims.width == 0) { - dims.width = line.len; - } else if (dims.width != line.len) { - return aoc.Error.InvalidInput; - } - for (line) |c, i| { - try map.append(switch (c) { - '#' => SeatState.TAKEN, - 'L' => SeatState.EMPTY, - '.' => SeatState.FLOOR, - else => { - return aoc.Error.InvalidInput; - }, - }); - } - dims.height += 1; - } - mapitems.* = map.toOwnedSlice(); -} - -fn printMap(mapitems: []SeatState, dims: MapDims) void { - for (mapitems) |state, i| { - const c: u8 = switch (state) { - SeatState.EMPTY => 'L', - SeatState.TAKEN => '#', - SeatState.FLOOR => '.', - }; - if (i % dims.width == 0) { - std.debug.print("\n", .{}); - } - std.debug.print("{c}", .{c}); - } -} - -fn simulateStrategyOne(before: []SeatState, after: []SeatState, dims: MapDims) u32 { - var seat_changes: u32 = 0; - for (before) |state, i| { - const p = Pos{ .x = i % dims.width, .y = i / dims.width }; - var count: u32 = 0; - for (adjacent) |ap| { - if (p.x + ap.x >= dims.width or p.x + ap.x < 0 or p.y + ap.y >= dims.height or p.y + ap.y < 0) continue; - const ni = @intCast(u64, @intCast(i64, i) + ap.x + ap.y * @intCast(i64, dims.width)); - count += @boolToInt(before[ni] == SeatState.TAKEN); - } - if (state == SeatState.EMPTY and count == 0) { - after[i] = SeatState.TAKEN; - seat_changes += 1; - } else if (state == SeatState.TAKEN and count >= 4) { - after[i] = SeatState.EMPTY; - seat_changes += 1; - } else { - after[i] = before[i]; - } - } - return seat_changes; -} - -fn simulateStrategyTwo(before: []SeatState, after: []SeatState, dims: MapDims) u32 { - var seat_changes: u32 = 0; - for (before) |state, i| { - const p = Pos{ .x = i % dims.width, .y = i / dims.width }; - var count: u32 = 0; - for (adjacent) |ap| { - var iterp = Pos{ .x = p.x + ap.x, .y = p.y + ap.y }; - while (iterp.x < dims.width and iterp.x >= 0 and iterp.y < dims.height and iterp.y >= 0) { - const ni = @intCast(u64, iterp.x + iterp.y * @intCast(i64, dims.width)); - if (before[ni] == SeatState.TAKEN) { - count += 1; - break; - } else if (before[ni] == SeatState.EMPTY) { - break; - } else { - iterp.x += ap.x; - iterp.y += ap.y; - } - } - } - if (state == SeatState.EMPTY and count == 0) { - after[i] = SeatState.TAKEN; - seat_changes += 1; - } else if (state == SeatState.TAKEN and count >= 5) { - after[i] = SeatState.EMPTY; - seat_changes += 1; - } else { - after[i] = before[i]; - } - } - return seat_changes; -} - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var mapdims = MapDims{ .width = 0, .height = 0 }; - var mapitems: []SeatState = undefined; - try parseMap(&mapitems, &mapdims, input, allocator); - defer allocator.free(mapitems); - - var mapresult = try allocator.alloc(SeatState, mapitems.len); - defer allocator.free(mapresult); - - var round: u32 = 0; - while (simulateStrategyOne(mapitems, mapresult, mapdims) > 0) : (round += 1) { - std.debug.print("\rRound: {}", .{round}); - std.mem.copy(SeatState, mapitems, mapresult); - } - - var taken_count: u64 = 0; - for (mapresult) |state| { - taken_count += @boolToInt(state == SeatState.TAKEN); - } - - std.debug.print("\nSeats Occupied: {}\n", .{taken_count}); -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var mapdims = MapDims{ .width = 0, .height = 0 }; - var mapitems: []SeatState = undefined; - try parseMap(&mapitems, &mapdims, input, allocator); - defer allocator.free(mapitems); - - var mapresult = try allocator.alloc(SeatState, mapitems.len); - defer allocator.free(mapresult); - - var round: u32 = 0; - while (simulateStrategyTwo(mapitems, mapresult, mapdims) > 0) : (round += 1) { - std.debug.print("\rRound: {}", .{round}); - std.mem.copy(SeatState, mapitems, mapresult); - } - - var taken_count: u64 = 0; - for (mapresult) |state| { - taken_count += @boolToInt(state == SeatState.TAKEN); - } - - std.debug.print("\nSeats Occupied: {}\n", .{taken_count}); -} - -pub const main = aoc.gen_main(part1, part2); diff --git a/src/day12/main.zig b/src/day12/main.zig @@ -1,60 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); -const Pos = aoc.Pos; -const Dir = aoc.Dir; - -const Ship = struct { pos: Pos, waypoint: Pos, dir: usize }; - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var ship = Ship{ .pos = Pos{ .x = 0, .y = 0 }, .waypoint = Pos{ .x = 0, .y = 0 }, .dir = 0 }; - var lineit = std.mem.tokenize(input, "\n"); - while (lineit.next()) |line| { - const val = try std.fmt.parseInt(u32, line[1..], 10); - switch (line[0]) { - 'N' => ship.pos = ship.pos.add(Dir.North.mult(val)), - 'S' => ship.pos = ship.pos.add(Dir.South.mult(val)), - 'E' => ship.pos = ship.pos.add(Dir.East.mult(val)), - 'W' => ship.pos = ship.pos.add(Dir.West.mult(val)), - 'L' => ship.dir = Dir.nextCCW(ship.dir, @divExact(val, 90)), - 'R' => ship.dir = Dir.nextCW(ship.dir, @divExact(val, 90)), - 'F' => ship.pos = ship.pos.add(Dir.dirs[ship.dir].mult(val)), - else => { - return aoc.Error.InvalidInput; - }, - } - } - - std.debug.print("{}\n", .{std.math.absCast(ship.pos.x) + std.math.absCast(ship.pos.y)}); -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var ship = Ship{ .pos = Pos{ .x = 0, .y = 0 }, .waypoint = Pos{ .x = 10, .y = 1 }, .dir = 0 }; - var lineit = std.mem.tokenize(input, "\n"); - while (lineit.next()) |line| { - const val = try std.fmt.parseInt(u32, line[1..], 10); - switch (line[0]) { - 'N' => ship.waypoint = ship.waypoint.add(Dir.North.mult(val)), - 'S' => ship.waypoint = ship.waypoint.add(Dir.South.mult(val)), - 'E' => ship.waypoint = ship.waypoint.add(Dir.East.mult(val)), - 'W' => ship.waypoint = ship.waypoint.add(Dir.West.mult(val)), - // WORKAROUND for language bug (parameter is implicitly passed as reference and assignment order) - 'L' => { - var new = Dir.rotCCW(ship.waypoint, @divExact(val, 90)); - ship.waypoint = new; - }, - 'R' => { - var new = Dir.rotCW(ship.waypoint, @divExact(val, 90)); - ship.waypoint = new; - }, - 'F' => ship.pos = ship.pos.add(ship.waypoint.mult(val)), - else => { - return aoc.Error.InvalidInput; - }, - } - std.debug.print("{} {} {} {} {}\n", .{ line, ship.waypoint.x, ship.waypoint.y, ship.pos.x, ship.pos.y }); - } - - std.debug.print("{}\n", .{std.math.absCast(ship.pos.x) + std.math.absCast(ship.pos.y)}); -} - -pub const main = aoc.gen_main(part1, part2); diff --git a/src/day13/main.zig b/src/day13/main.zig @@ -1,105 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); - -fn gcd(a: u64, b: u64) u64 { - var ac = a; - var bc = b; - while (true) { - if (ac > bc) { - ac = ac % bc; - if (ac == 0) return bc; - } else { - bc = bc % ac; - if (bc == 0) return ac; - } - } -} - -fn lcm(a: u64, b: u64) u64 { - return @divExact(a * b, gcd(a, b)); -} - -fn waittime(start: u32, busid: u32) u32 { - return (busid - (start % busid)) % busid; -} - -fn printRegion(buses: []u32, start: u64, end: u64) void { - std.debug.print(" ", .{}); - for (buses) |bus| { - std.debug.print(" {:^5}", .{bus}); - } - std.debug.print("\n", .{}); - - var i = start; - while (i < end) : (i += 1) { - std.debug.print("{:<10}", .{i}); - for (buses) |bus| { - var c: u8 = '.'; - if (bus > 0 and i % bus == 0) c = 'D'; - std.debug.print(" {c} ", .{c}); - } - std.debug.print("\n", .{}); - } - std.debug.print("\n", .{}); -} - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var lineit = std.mem.tokenize(input, "\n"); - var start = try std.fmt.parseInt(u32, lineit.next().?, 10); - - var bestbus: ?u32 = null; - var busit = std.mem.tokenize(lineit.next().?, ","); - while (busit.next()) |bus| { - if (bus[0] == 'x') continue; - const val = try std.fmt.parseInt(u32, bus, 10); - if (bestbus == null or waittime(start, val) < waittime(start, bestbus.?)) { - bestbus = val; - } - } - - std.debug.print("{} {}\n", .{ bestbus.?, bestbus.? * waittime(start, bestbus.?) }); -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var lineit = std.mem.tokenize(input, "\n"); - _ = try std.fmt.parseInt(u32, lineit.next().?, 10); - - var busses = std.ArrayList(u32).init(allocator); - defer busses.deinit(); - - var busit = std.mem.tokenize(lineit.next().?, ","); - - const first = try std.fmt.parseInt(u32, busit.next().?, 10); - try busses.append(first); - - var offset: u64 = 0; - var cycle: u64 = first; - var delay: u32 = 1; - while (busit.next()) |bus| : (delay += 1) { - if (bus[0] == 'x') { - try busses.append(0); - continue; - } - const val = try std.fmt.parseInt(u32, bus, 10); - try busses.append(val); - - var mult: u64 = @divFloor(offset + delay, val); - if ((offset + delay) % val != 0) mult += 1; - while ((mult * val - offset - delay) % cycle != 0) { - mult += std.math.max(1, @divFloor(cycle - (mult * val - offset - delay) % cycle, val)); - } - offset = mult * val - delay; - - cycle = lcm(cycle, val); - if (aoc.debug) { - printRegion(busses.items, offset, offset + delay + 1); - printRegion(busses.items, offset + cycle, offset + cycle + delay + 1); - std.debug.print("{}\n", .{offset}); - } - } - - printRegion(busses.items, offset, offset + busses.items.len); - std.debug.print("{}\n", .{offset}); -} - -pub const main = aoc.gen_main(part1, part2); diff --git a/src/day14/main.zig b/src/day14/main.zig @@ -1,118 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); - -fn getBit(v: u36, i: u6) bool { - return ((v >> i) & 1) > 0; -} - -fn clearBit(v: u36, i: u6) u36 { - return v & ~(@as(u36, 1) << i); -} - -fn setBit(v: u36, i: u6) u36 { - return v | (@as(u36, 1) << i); -} - -fn getValStr(line: []const u8) ![]const u8 { - const sep = std.mem.indexOf(u8, line, " = "); - if (sep == null) return aoc.Error.InvalidInput; - return line[sep.? + 3 ..]; -} - -fn getAddrStr(line: []const u8) ![]const u8 { - const sep = std.mem.indexOfScalar(u8, line, ']'); - if (sep == null) return aoc.Error.InvalidInput; - return line[4..sep.?]; -} - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var answer: u64 = 0; - - var memmap = std.AutoHashMap(u36, u64).init(allocator); - defer memmap.deinit(); - - var ormask: u36 = 0; - var andmask: u36 = ~@as(u36, 0); - - var lineit = std.mem.tokenize(input, "\n"); - while (lineit.next()) |line| { - if (std.mem.eql(u8, line[0..4], "mask")) { - andmask = ~@as(u36, 0); - ormask = 0; - for (try getValStr(line)) |c, i| { - if (c == '0') { - andmask = clearBit(andmask, @intCast(u6, 35 - i)); - } else if (c == '1') { - ormask = setBit(ormask, @intCast(u6, 35 - i)); - } - } - } else { - const val = try std.fmt.parseInt(u36, try getValStr(line), 10); - const addr = try std.fmt.parseInt(u36, try getAddrStr(line), 10); - try memmap.put(addr, (val & andmask) | ormask); - } - } - - var mapit = memmap.iterator(); - while (mapit.next()) |kv| { - answer += kv.value; - } - - std.debug.print("{}\n", .{answer}); -} - -fn bitRecurse(map: *std.AutoHashMap(u36, u64), addr: *u36, val: u64, mask: u36, index: u6) anyerror!void { - if (index == 36) { - std.debug.print("SET: {b} {}\n", .{ addr.*, val }); - try map.put(addr.*, val); - } else { - if (getBit(mask, index)) { - addr.* = setBit(addr.*, index); - try bitRecurse(map, addr, val, mask, index + 1); - addr.* = clearBit(addr.*, index); - try bitRecurse(map, addr, val, mask, index + 1); - } else { - try bitRecurse(map, addr, val, mask, index + 1); - } - } -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var answer: u64 = 0; - - var memmap = std.AutoHashMap(u36, u64).init(allocator); - defer memmap.deinit(); - - var ormask: u36 = 0; - var flipmask: u36 = 0; - - var lineit = std.mem.tokenize(input, "\n"); - while (lineit.next()) |line| { - if (std.mem.eql(u8, line[0..4], "mask")) { - ormask = 0; - flipmask = 0; - for (try getValStr(line)) |c, i| { - if (c == '1') { - ormask = setBit(ormask, @intCast(u6, 35 - i)); - } else if (c == 'X') { - flipmask = setBit(flipmask, @intCast(u6, 35 - i)); - } - } - } else { - var addr = try std.fmt.parseInt(u36, try getAddrStr(line), 10); - const val = try std.fmt.parseInt(u36, try getValStr(line), 10); - addr = addr | ormask; - std.debug.print("{b} {b} {} {}\n", .{ flipmask, ormask, addr, val }); - try bitRecurse(&memmap, &addr, val, flipmask, 0); - } - } - - var mapit = memmap.iterator(); - while (mapit.next()) |kv| { - answer += kv.value; - } - - std.debug.print("{}\n", .{answer}); -} - -pub const main = aoc.gen_main(part1, part2); diff --git a/src/day15/main.zig b/src/day15/main.zig @@ -1,55 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); - -const Occurance = struct { last: u32, beforelast: u32 }; - -fn getNthSpoken(allocator: *std.mem.Allocator, input: []u8, spoken: u32) !u32 { - var occurances = std.AutoHashMap(u32, Occurance).init(allocator); - defer occurances.deinit(); - - var intlist = std.ArrayList(u32).init(allocator); - defer intlist.deinit(); - - var numit = std.mem.tokenize(input, "\n,"); - var i: u32 = 0; - while (numit.next()) |numstr| : (i += 1) { - const num = try std.fmt.parseInt(u32, numstr, 10); - try intlist.append(num); - try occurances.put(num, Occurance{ .last = i, .beforelast = i }); - } - - while (i < spoken) : (i += 1) { - if (aoc.debug and i % 100000 == 0) - std.debug.print("\r{}", .{i}); - - const num = intlist.items[i - 1]; - var entry = occurances.getEntry(num); - var diff: u32 = 0; - if (entry) |occ_entry| { - diff = occ_entry.value.last - occ_entry.value.beforelast; - } - entry = occurances.getEntry(diff); - if (entry) |occ_entry| { - occ_entry.value.beforelast = occ_entry.value.last; - occ_entry.value.last = i; - } else { - try occurances.put(diff, Occurance{ .last = i, .beforelast = i }); - } - try intlist.append(diff); - } - - if (aoc.debug) - std.debug.print("\r \r", .{}); - - return intlist.items[spoken - 1]; -} - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - std.debug.print("{}\n", .{try getNthSpoken(allocator, input, 2020)}); -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - std.debug.print("{}\n", .{try getNthSpoken(allocator, input, 30000000)}); -} - -pub const main = aoc.gen_main(part1, part2); diff --git a/src/day16/main.zig b/src/day16/main.zig @@ -1,176 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); - -const Range = struct { min: u32, max: u32 }; -const FieldRule = struct { name: []const u8, r1: Range, r2: Range }; - -fn assert(comptime T: type, part: ?T) !T { - if (part == null) return aoc.Error.InvalidInput; - return part.?; -} - -fn parseRange(str: []const u8) !Range { - const sep = try assert(usize, std.mem.indexOfScalar(u8, str, '-')); - if (sep == str.len - 1) return aoc.Error.InvalidInput; - return Range{ - .min = try std.fmt.parseInt(u32, str[0..sep], 10), - .max = try std.fmt.parseInt(u32, str[sep + 1 ..], 10), - }; -} - -fn parseRules(rules: *std.ArrayList(FieldRule), str: []const u8) !void { - var lineit = std.mem.tokenize(str, "\n"); - while (lineit.next()) |line| { - const sep = try assert(usize, std.mem.indexOf(u8, line, ": ")); - if (sep == line.len - 1) return aoc.Error.InvalidInput; - const name = line[0..sep]; - var rangepart = line[sep + 2 ..]; - var spaceit = std.mem.tokenize(rangepart, " "); - const r1 = try parseRange(try assert([]const u8, spaceit.next())); - _ = spaceit.next(); - const r2 = try parseRange(try assert([]const u8, spaceit.next())); - try rules.append(FieldRule{ .name = name, .r1 = r1, .r2 = r2 }); - } -} - -fn parseVals(vals: *std.ArrayList(u32), str: []const u8) !void { - var lineit = std.mem.tokenize(str, "\n"); - while (lineit.next()) |line| { - var partit = std.mem.tokenize(line, ","); - while (partit.next()) |part| { - try vals.append(try std.fmt.parseInt(u32, part, 10)); - } - } -} - -fn matchesRule(rule: FieldRule, v: u32) bool { - return (v >= rule.r1.min and v <= rule.r1.max or v >= rule.r2.min and v <= rule.r2.max); -} - -fn hasRule(rules: []FieldRule, v: u32) bool { - for (rules) |r, i| { - if (matchesRule(r, v)) return true; - } - return false; -} - -fn printCandidates(candidates: []u1, rulecount: usize, remove: usize) void { - var i: u32 = 0; - while (i < rulecount) : (i += 1) { - for (candidates[i * rulecount .. (i + 1) * rulecount]) |c| { - std.debug.print("{} ", .{c}); - } - if (i == remove) std.debug.print(" <<<", .{}); - std.debug.print("\n", .{}); - } -} - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var answer: u32 = 0; - - var rules = std.ArrayList(FieldRule).init(allocator); - defer rules.deinit(); - - var othervals = std.ArrayList(u32).init(allocator); - defer othervals.deinit(); - - var secit = std.mem.split(input, "\n\n"); - try parseRules(&rules, try assert([]const u8, secit.next())); - _ = secit.next(); - try parseVals(&othervals, (try assert([]const u8, secit.next()))[16..]); - - for (othervals.items) |v| { - if (!hasRule(rules.items, v)) answer += v; - } - - std.debug.print("{}\n", .{answer}); -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var rules = std.ArrayList(FieldRule).init(allocator); - defer rules.deinit(); - - var ownvals = std.ArrayList(u32).init(allocator); - defer ownvals.deinit(); - - var othervals = std.ArrayList(u32).init(allocator); - defer othervals.deinit(); - - var secit = std.mem.split(input, "\n\n"); - try parseRules(&rules, try assert([]const u8, secit.next())); - try parseVals(&ownvals, (try assert([]const u8, secit.next()))[13..]); - try parseVals(&othervals, (try assert([]const u8, secit.next()))[16..]); - - const rulecount = rules.items.len; - - { - var index: u32 = 0; - while (index < othervals.items.len) { - const ri = index % rulecount; - var i: u32 = 0; - while (i < rulecount) : (i += 1) { - if (!hasRule(rules.items, othervals.items[index + i])) - break; - } - - if (i == rulecount) { - index += @intCast(u32, rulecount); - } else { - if (aoc.debug) - std.debug.print("REMOVE: ", .{}); - i = 0; - while (i < rulecount) : (i += 1) { - if (aoc.debug) - std.debug.print("{} ", .{othervals.items[index]}); - _ = othervals.orderedRemove(index); - } - if (aoc.debug) - std.debug.print("\n", .{}); - } - } - } - - var candidates = try allocator.alloc(u1, rulecount * rulecount); - defer allocator.free(candidates); - std.mem.set(u1, candidates, 1); - - if (aoc.debug) - std.debug.print("{}\n", .{othervals.items.len}); - { - var index: u32 = 0; - while (index < othervals.items.len) : (index += 1) { - const ri = index % rulecount; - const v = othervals.items[index]; - for (rules.items) |r, rri| { - if (!matchesRule(r, v)) { - candidates[ri * rulecount + rri] = 0; - } - } - } - } - - var answer: u64 = 1; - { - var index: u32 = 0; - while (index < rulecount * rulecount) : (index += 1) { - var ri = index % rulecount; - const cc = candidates[ri * rulecount .. (ri + 1) * rulecount]; - if (std.mem.count(u1, cc, ([_]u1{1})[0..]) == 1) { - const field = std.mem.indexOfScalar(u1, cc, 1).?; - if (std.mem.indexOf(u8, rules.items[field].name, "departure") != null) - answer *= ownvals.items[ri]; - for (ownvals.items) |_, ii| { - candidates[ii * rulecount + field] = 0; - } - if (aoc.debug) { - printCandidates(candidates, rulecount, ri); - std.debug.print("\n", .{}); - } - } - } - } - - std.debug.print("{}\n", .{answer}); -} - -pub const main = aoc.gen_main(part1, part2); diff --git a/src/day17/main.zig b/src/day17/main.zig @@ -1,193 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); - -fn PosVec(comptime N: u32) type { - return struct { data: [N]i64 }; -} - -const Active = bool; - -fn forNeighbors(comptime dims: u32, map: *std.AutoHashMap(PosVec(dims), Active), p: PosVec(dims), data: anytype, func: fn (map: *std.AutoHashMap(PosVec(dims), Active), p: PosVec(dims), d: @TypeOf(data)) anyerror!void) !void { - var i: u32 = 0; - while (i < comptime std.math.pow(u32, 3, dims)) : (i += 1) { - var np: PosVec(dims) = undefined; - var d: u32 = 0; - var skip = true; - while (d < dims) : (d += 1) { - const offset = (i / std.math.pow(u32, 3, d)) % 3; - np.data[d] = p.data[d] + @intCast(i64, offset) - 1; - if (skip and offset != 1) skip = false; - } - if (skip) continue; - try func(map, np, data); - } -} - -fn posStr(comptime dims: u32, p: PosVec(dims)) void { - var d: u32 = 0; - while (d < dims) : (d += 1) { - std.debug.print("{} ", .{p.data[d]}); - } -} - -fn countActive(comptime dims: u32) fn (map: *std.AutoHashMap(PosVec(dims), Active), p: PosVec(dims), d: *u32) anyerror!void { - const impl = struct { - fn func(map: *std.AutoHashMap(PosVec(dims), Active), p: PosVec(dims), d: *u32) anyerror!void { - d.* += @boolToInt(map.get(p) != null); - } - }; - return impl.func; -} - -fn addNew(comptime dims: u32) fn (oldmap: *std.AutoHashMap(PosVec(dims), Active), p: PosVec(dims), newmap: *std.AutoHashMap(PosVec(dims), Active)) anyerror!void { - const impl = struct { - fn func(oldmap: *std.AutoHashMap(PosVec(dims), Active), p: PosVec(dims), newmap: *std.AutoHashMap(PosVec(dims), Active)) anyerror!void { - if (newmap.get(p) != null) return; - - var v = oldmap.get(p); - var state = (v != null); - - var count: u32 = 0; - try forNeighbors(dims, oldmap, p, &count, countActive(dims)); - if (state and count >= 2 and count <= 3 or !state and count == 3) { - try newmap.put(p, true); - } - } - }; - return impl.func; -} - -fn simulateRound(comptime dims: u32, map: *std.AutoHashMap(PosVec(dims), Active), allocator: *std.mem.Allocator) !void { - var newmap = std.AutoHashMap(PosVec(dims), Active).init(allocator); - errdefer newmap.deinit(); - - var mapit = map.iterator(); - while (mapit.next()) |kv| { - try forNeighbors(dims, map, kv.key, &newmap, addNew(dims)); - try addNew(dims)(map, kv.key, &newmap); - } - - std.mem.swap(std.AutoHashMap(PosVec(dims), Active), map, &newmap); - newmap.deinit(); -} - -fn sliceProduct(data: []i64) i64 { - var product: i64 = 1; - for (data) |v| { - product *= v; - } - return product; -} - -fn printMap(comptime dims: u32, map: *std.AutoHashMap(PosVec(dims), Active)) void { - var min: ?PosVec(dims) = null; - var max: ?PosVec(dims) = null; - - var mapit = map.iterator(); - while (mapit.next()) |kv| { - if (min == null) min = kv.key; - if (max == null) max = kv.key; - - var d: u32 = 0; - while (d < dims) : (d += 1) { - if (min.?.data[d] > kv.key.data[d]) min.?.data[d] = kv.key.data[d]; - if (max.?.data[d] < kv.key.data[d]) max.?.data[d] = kv.key.data[d]; - } - } - - if (min == null or max == null) return; - - var space: PosVec(dims) = undefined; - { - var d: u32 = 0; - while (d < dims) : (d += 1) { - space.data[d] = max.?.data[d] - min.?.data[d]; - } - } - - var i: usize = 0; - while (i < sliceProduct(space.data[0..])) : (i += @intCast(usize, space.data[0] * space.data[1])) { - var np: PosVec(dims) = undefined; - var d: u32 = 0; - while (d < dims) : (d += 1) { - np.data[d] = min.?.data[d] + @mod(@divFloor(@intCast(i64, i), sliceProduct(space.data[0..d])), space.data[d]); - } - - d = 2; - std.debug.print("Slice at: ", .{}); - while (d < dims) : (d += 1) { - if (d > 2) std.debug.print(",", .{}); - std.debug.print("{}.Dim = {} ", .{ d + 1, np.data[d] }); - } - std.debug.print("\n", .{}); - - var y = min.?.data[1]; - while (y <= max.?.data[1]) : (y += 1) { - var x = min.?.data[0]; - while (x <= max.?.data[0]) : (x += 1) { - var v = np; - v.data[0] = x; - v.data[1] = y; - var c: u8 = '.'; - if (map.get(v) != null) c = '#'; - std.debug.print("{c}", .{c}); - } - std.debug.print("\n", .{}); - } - std.debug.print("\n", .{}); - } -} - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var map = std.AutoHashMap(PosVec(3), Active).init(allocator); - defer map.deinit(); - - var lineit = std.mem.tokenize(input, "\n"); - var y: i64 = 0; - while (lineit.next()) |line| : (y += 1) { - for (line) |c, x| { - if (c != '#') continue; - try map.put(PosVec(3){ .data = [_]i64{ @intCast(i64, x), y, 0 } }, true); - } - } - - var i: usize = 0; - while (i < 6) : (i += 1) { - try simulateRound(3, &map, allocator); - if (aoc.debug) { - std.debug.print("AFTER ROUND {}:\n", .{i + 1}); - printMap(3, &map); - } - } - - var answer: u32 = map.count(); - std.debug.print("{}\n", .{answer}); -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var map = std.AutoHashMap(PosVec(4), Active).init(allocator); - defer map.deinit(); - - var lineit = std.mem.tokenize(input, "\n"); - var y: i64 = 0; - while (lineit.next()) |line| : (y += 1) { - for (line) |c, x| { - if (c != '#') continue; - try map.put(PosVec(4){ .data = [_]i64{ @intCast(i64, x), y, 0, 0 } }, true); - } - } - - var i: usize = 0; - while (i < 6) : (i += 1) { - try simulateRound(4, &map, allocator); - if (aoc.debug) { - std.debug.print("AFTER ROUND {}:\n", .{i + 1}); - printMap(4, &map); - } - } - - var answer: u32 = map.count(); - std.debug.print("{}\n", .{answer}); -} - -pub const main = aoc.gen_main(part1, part2); diff --git a/src/day18/main.zig b/src/day18/main.zig @@ -1,173 +0,0 @@ -// Own twist on https://en.wikipedia.org/wiki/Shunting-yard_algorithm - -const std = @import("std"); -const aoc = @import("aoc"); - -const OpType = enum { ADD, MULT, COUNT }; -const Op = struct { - prio: u32, optype: OpType -}; -const Item = union(enum) { - num: i64, op: OpType -}; - -const op_names = [_][]const u8{ "ADD", "MULT" }; - -fn printStack(stack: []Item) void { - for (stack) |item| { - switch (item) { - .op => std.debug.print("{} ", .{op_names[@enumToInt(item.op)]}), - .num => std.debug.print("{} ", .{item.num}), - } - } - std.debug.print("\n", .{}); -} - -fn parseToken(line: []const u8, i: *usize, depth: *u32, op_prio: []const u32, mathstack: *std.ArrayList(Item), opstack: *std.ArrayList(Op)) !bool { - if (i.* > line.len) { - return false; - } else if (i.* == line.len) { - while (opstack.items.len > 0) - try mathstack.append(Item{ .op = opstack.pop().optype }); - } else { - const c = line[i.*]; - switch (c) { - '+', '*' => { - const optype = switch (c) { - '+' => OpType.ADD, - '*' => OpType.MULT, - else => unreachable, - }; - const prio = depth.* * @enumToInt(OpType.COUNT) + op_prio[@enumToInt(optype)]; - while (opstack.items.len > 0 and - opstack.items[opstack.items.len - 1].prio >= prio) - { - try mathstack.append(Item{ .op = opstack.pop().optype }); - } - try opstack.append(Op{ .optype = optype, .prio = prio }); - }, - '(' => depth.* += 1, - ')' => depth.* -= 1, - ' ' => {}, - else => { - // in this case we know that every number is one digit.. - // crappy zig parseInt requires span of exact int size - // before we even know the format >:((( - const val = try std.fmt.parseInt(i64, line[i.* .. i.* + 1], 10); - try mathstack.append(Item{ .num = val }); - }, - } - } - i.* += 1; - return true; -} - -fn reduceStack(mathstack: *std.ArrayList(Item), opstack: *std.ArrayList(Op), tmpstack: *std.ArrayList(Item)) !void { - while (mathstack.items.len > 0) { - while (mathstack.items[mathstack.items.len - 1] != .op and - tmpstack.items.len > 0) - { - try mathstack.append(tmpstack.pop()); - } - if (mathstack.items.len < 3 or mathstack.items[mathstack.items.len - 1] != .op) - break; - const op = mathstack.pop(); - - // get first arg - if (mathstack.items[mathstack.items.len - 1] != .num) { - try tmpstack.append(op); - continue; - } - const a = mathstack.pop(); - - // get second arg - if (mathstack.items[mathstack.items.len - 1] != .num) { - try tmpstack.append(op); - try tmpstack.append(a); - continue; - } - const b = mathstack.pop(); - - const result: i64 = switch (op.op) { - OpType.ADD => a.num + b.num, - OpType.MULT => a.num * b.num, - else => unreachable, - }; - try mathstack.append(Item{ .num = result }); - } -} - -fn printRPN(allocator: *std.mem.Allocator, input: []u8, prios: []const u32) !void { - var mathstack = std.ArrayList(Item).init(allocator); - defer mathstack.deinit(); - - var opstack = std.ArrayList(Op).init(allocator); - defer opstack.deinit(); - - var lineit = std.mem.tokenize(input, "\n"); - while (lineit.next()) |line| { - var depth: u32 = 0; - var i: usize = 0; - while (try parseToken(line, &i, &depth, prios, &mathstack, &opstack)) {} - } - - printStack(mathstack.items); -} - -fn partProto(allocator: *std.mem.Allocator, input: []u8, prios: []const u32) !void { - var answer: i64 = 0; - - if (aoc.debug) { - std.debug.print("Reverse Polish Notation:\n", .{}); - try printRPN(allocator, input, prios); - } - - var mathstack = std.ArrayList(Item).init(allocator); - defer mathstack.deinit(); - - var opstack = std.ArrayList(Op).init(allocator); - defer opstack.deinit(); - - var tmpstack = std.ArrayList(Item).init(allocator); - defer tmpstack.deinit(); - - var lineit = std.mem.tokenize(input, "\n"); - while (lineit.next()) |line| { - var depth: u32 = 0; - var i: usize = 0; - while (try parseToken(line, &i, &depth, prios, &mathstack, &opstack)) { - try reduceStack(&mathstack, &opstack, &tmpstack); - } - - if (mathstack.items.len != 1) return aoc.Error.InvalidInput; - const last = mathstack.items[mathstack.items.len - 1]; - if (last != .num) return aoc.Error.InvalidInput; - answer += last.num; - - try mathstack.resize(0); - try opstack.resize(0); - try tmpstack.resize(0); - } - - std.debug.print("{}\n", .{answer}); -} - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - const prios = [_]u32{ 1, 1 }; - try partProto(allocator, input, prios[0..]); -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - const prios = [_]u32{ 2, 1 }; - try partProto(allocator, input, prios[0..]); -} - -test "unions" { - const item1 = Item{ .op = OpType.MULT }; - const item2 = Item{ .num = 1000 }; - const expect = std.testing.expect; - expect(item1 == .op); - expect(item2 == .num); -} - -pub const main = aoc.gen_main(part1, part2); diff --git a/src/day19/main.zig b/src/day19/main.zig @@ -1,177 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); - -const Rule = union(enum) { - c: u8, - combos: []const []const usize, -}; - -fn printSlice(slice: []const usize) void { - for (slice) |v, i| { - if (i > 0) { - std.debug.print(" ", .{}); - } - std.debug.print("{}", .{v}); - } -} - -var stacktrace: std.ArrayList(usize) = undefined; - -fn checkRule(rules: *std.AutoHashMap(usize, Rule), rid: usize, msg: []const u8, nextstack: *std.ArrayList(usize)) anyerror!bool { - if (msg.len == 0) return true; - const rule = rules.get(rid) orelse return aoc.Error.InvalidInput; - - if (rule == .c) { - if (msg[0] != rule.c) return false; - if (msg.len == 1) return (nextstack.items.len == 0); - if (nextstack.items.len == 0) return false; - - const next = nextstack.pop(); - - const valid = try checkRule(rules, next, msg[1..], nextstack); - if (valid) try stacktrace.append(next); - if (valid) return true; - - try nextstack.append(next); - } else { - for (rule.combos) |ruleseq| { - try nextstack.appendSlice(ruleseq[1..]); - std.mem.reverse(usize, nextstack.items[nextstack.items.len - (ruleseq.len - 1) ..]); - - const valid = try checkRule(rules, ruleseq[0], msg, nextstack); - if (valid) try stacktrace.append(ruleseq[0]); - if (valid) return true; - - try nextstack.resize(nextstack.items.len - (ruleseq.len - 1)); - } - } - return false; -} - -fn freeRules(allocator: *std.mem.Allocator, rules: *std.AutoHashMap(usize, Rule), ignore: ?[]const usize) void { - var mapit = rules.iterator(); - while (mapit.next()) |kv| { - if (ignore != null and std.mem.indexOfScalar(usize, ignore.?, kv.key) != null) continue; - if (kv.value == .combos) { - for (kv.value.combos) |rs| { - allocator.free(rs); - } - allocator.free(kv.value.combos); - } - } - rules.deinit(); -} - -fn parseRule(allocator: *std.mem.Allocator, rules: *std.AutoHashMap(usize, Rule), line: []const u8) !void { - const indexsep = try aoc.assertV(std.mem.indexOf(u8, line, ": ")); - const index = try std.fmt.parseInt(usize, line[0..indexsep], 10); - - var chrsep = std.mem.indexOf(u8, line, "\""); - if (chrsep != null) { - if (chrsep.? == line.len - 1) return aoc.Error.InvalidInput; - try rules.put(index, Rule{ .c = line[chrsep.? + 1] }); - } else { - var vals = std.ArrayList([]usize).init(allocator); - errdefer vals.deinit(); - - var ruleit = std.mem.split(line[indexsep + 2 ..], " | "); - while (ruleit.next()) |r| { - var ruleids = std.ArrayList(usize).init(allocator); - errdefer ruleids.deinit(); - - var spaceit = std.mem.tokenize(r, " "); - while (spaceit.next()) |word| { - try ruleids.append(try std.fmt.parseInt(usize, word, 10)); - } - - if (ruleids.items.len == 0) return aoc.Error.InvalidInput; - - try vals.append(ruleids.items); - } - - try rules.put(index, Rule{ .combos = vals.items }); - } -} - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var rules = std.AutoHashMap(usize, Rule).init(allocator); - defer freeRules(allocator, &rules, null); - - var partit = std.mem.split(input, "\n\n"); - - var lineit = std.mem.tokenize(try aoc.assertV(partit.next()), "\n"); - while (lineit.next()) |line| { - try parseRule(allocator, &rules, line); - } - - var answer: u32 = 0; - var nextstack = std.ArrayList(usize).init(allocator); - defer nextstack.deinit(); - - stacktrace = std.ArrayList(usize).init(allocator); - defer stacktrace.deinit(); - - lineit = std.mem.tokenize(try aoc.assertV(partit.next()), "\n"); - var count: usize = 0; - while (lineit.next()) |line| { - count += 1; - if (try checkRule(&rules, 0, line, &nextstack)) - answer += 1; - try nextstack.resize(0); - } - - std.debug.print("{} / {} matching\n", .{ answer, count }); -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var rules = std.AutoHashMap(usize, Rule).init(allocator); - defer freeRules(allocator, &rules, ([_]usize{ 8, 11 })[0..]); - - var partit = std.mem.split(input, "\n\n"); - - var lineit = std.mem.tokenize(try aoc.assertV(partit.next()), "\n"); - while (lineit.next()) |line| { - if (std.mem.eql(u8, line[0..2], "8:")) { - const p1: []const usize = ([_]usize{42})[0..]; - const p2: []const usize = ([_]usize{ 42, 8 })[0..]; - try rules.put(8, Rule{ .combos = ([_][]const usize{ p1, p2 })[0..] }); - } else if (std.mem.eql(u8, line[0..3], "11:")) { - const p1: []const usize = ([_]usize{ 42, 31 })[0..]; - const p2: []const usize = ([_]usize{ 42, 11, 31 })[0..]; - try rules.put(11, Rule{ .combos = ([_][]const usize{ p1, p2 })[0..] }); - } else { - try parseRule(allocator, &rules, line); - } - } - - var answer: u32 = 0; - var nextstack = std.ArrayList(usize).init(allocator); - defer nextstack.deinit(); - - stacktrace = std.ArrayList(usize).init(allocator); - defer stacktrace.deinit(); - - var count: usize = 0; - lineit = std.mem.tokenize(try aoc.assertV(partit.next()), "\n"); - while (lineit.next()) |line| { - count += 1; - const valid = try checkRule(&rules, 0, line, &nextstack); - answer += @boolToInt(valid); - if (aoc.debug) { - std.debug.print("TRACE: [", .{}); - std.mem.reverse(usize, stacktrace.items); - printSlice(stacktrace.items); - std.debug.print("]\n", .{}); - if (valid) { - std.debug.print("MATCH {}\n\n", .{line}); - } else { - std.debug.print("NO MATCH {}\n\n", .{line}); - } - } - try nextstack.resize(0); - } - - std.debug.print("{} / {} matching\n", .{ answer, count }); -} - -pub const main = aoc.gen_main(part1, part2); diff --git a/src/day2/main.zig b/src/day2/main.zig @@ -1,57 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); - -const Entry = struct { - a: u16, b: u16, char: u8, pass: []u8 -}; - -fn freeEntries(allocator: *std.mem.Allocator, entries: *const std.ArrayList(Entry)) void { - for (entries.items) |item| - allocator.free(item.pass); - entries.deinit(); -} - -fn parse(allocator: *std.mem.Allocator, input: []u8) !std.ArrayList(Entry) { - var entries = std.ArrayList(Entry).init(allocator); - errdefer freeEntries(allocator, &entries); - - var lineit = std.mem.tokenize(input, "\n"); - while (lineit.next()) |line| { - var entry: Entry = undefined; - var it = std.mem.tokenize(line, " -"); - entry.a = try std.fmt.parseInt(u16, it.next().?, 10); - entry.b = try std.fmt.parseInt(u16, it.next().?, 10); - entry.char = it.next().?[0]; - entry.pass = try allocator.dupe(u8, it.next().?); - try entries.append(entry); - } - - return entries; -} - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - const entries = try parse(allocator, input); - defer freeEntries(allocator, &entries); - - var valid: u32 = 0; - for (entries.items) |entry| { - var count: u16 = 0; - for (entry.pass) |c| count += @boolToInt(c == entry.char); - valid += @boolToInt(count >= entry.a and count <= entry.b); - } - std.debug.print("{}\n", .{valid}); -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - const entries = try parse(allocator, input); - defer freeEntries(allocator, &entries); - - var valid: u32 = 0; - for (entries.items) |entry| { - valid += @boolToInt((entry.pass[entry.a - 1] == entry.char) != - (entry.pass[entry.b - 1] == entry.char)); - } - std.debug.print("{}\n", .{valid}); -} - -pub const main = aoc.gen_main(part1, part2); diff --git a/src/day20/main.zig b/src/day20/main.zig @@ -1,463 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); - -const tile_width: u32 = 10; -const Tile = struct { - id: u32, - data: [tile_width * tile_width]u1, - sides: [4][tile_width]u1, // NOTE: update these values when placed accordingly - adj: [4]?usize, - pos: aoc.Pos, -}; -const Flip = enum { NONE, HOR, VERT }; -const flips = [_]Flip{ Flip.NONE, Flip.HOR, Flip.VERT }; - -const seamonster = [_][]const u8{ - " # ", - "# ## ## ###", - " # # # # # # ", -}; - -const dirs = [4]aoc.Pos{ - aoc.Pos{ .x = 0, .y = -1 }, - aoc.Pos{ .x = 1, .y = 0 }, - aoc.Pos{ .x = 0, .y = 1 }, - aoc.Pos{ .x = -1, .y = 0 }, -}; - -fn printGrid(tiles: []Tile, grid: []?usize, grid_width: u32) void { - var y: u32 = 0; - while (y < grid_width * tile_width) : (y += 1) { - var x: u32 = 0; - if (y > 0 and y % tile_width == 0) std.debug.print("\n", .{}); - while (x < grid_width * tile_width) : (x += 1) { - if (x > 0 and x % tile_width == 0) std.debug.print(" ", .{}); - var tile_index: ?usize = grid[@divFloor(y, tile_width) * grid_width + @divFloor(x, tile_width)]; - if (tile_index) |ti| { - var tile = tiles[tile_index.?]; - var c: u8 = if (tile.data[(y % tile_width) * tile_width + (x % tile_width)] == 1) '#' else '.'; - std.debug.print("{c}", .{c}); - } else { - std.debug.print("?", .{}); - } - } - std.debug.print("\n", .{}); - } - std.debug.print("\n\n", .{}); -} - -fn printTile(data: []u1, width: u32) void { - var y: u32 = 0; - while (y < width) : (y += 1) { - printTileSlice(data[y * width .. (y + 1) * width]); - std.debug.print("\n", .{}); - } -} - -fn printTileSlice(slice: []const u1) void { - var i: u32 = 0; - while (i < slice.len) : (i += 1) { - var c: u8 = if (slice[i] == 1) '#' else '.'; - std.debug.print("{c}", .{c}); - } -} - -fn couldMatch(side1: [tile_width]u1, side2: [tile_width]u1) bool { - var side1_cpy = side1; - if (std.mem.eql(u1, side1_cpy[0..], side2[0..])) return true; - std.mem.reverse(u1, side1_cpy[0..]); - if (std.mem.eql(u1, side1_cpy[0..], side2[0..])) return true; - return false; -} - -fn parseTiles(tiles: *std.ArrayList(Tile), input: []u8) !void { - var tileit = std.mem.split(input, "\n\n"); - while (tileit.next()) |tilestr| { - if (tilestr.len <= 1) continue; - var tile: Tile = undefined; - var lineit = std.mem.tokenize(tilestr, "\n"); - - // read tile id - var numstr = (try aoc.assertV(lineit.next()))[5..9]; - tile.id = try std.fmt.parseInt(u32, numstr, 10); - - // read tile data - var i: u32 = 0; - var line: []const u8 = undefined; - while (i < tile_width * tile_width) : (i += 1) { - if (i % tile_width == 0) line = try aoc.assertV(lineit.next()); - tile.data[i] = @boolToInt(line[i % tile_width] == '#'); - } - - // read side bits - i = 0; - while (i < 4) : (i += 1) { - var sidebits: [tile_width]u1 = undefined; - for (sidebits) |_, k| { - sidebits[k] = switch (i) { - @enumToInt(aoc.Dir.Name.NORTH) => tile.data[k], - @enumToInt(aoc.Dir.Name.WEST) => tile.data[(tile_width - 1 - k) * tile_width], - @enumToInt(aoc.Dir.Name.EAST) => tile.data[tile_width - 1 + k * tile_width], - @enumToInt(aoc.Dir.Name.SOUTH) => tile.data[tile_width * tile_width - 1 - k], - else => unreachable, - }; - } - tile.sides[i] = sidebits; - } - - // init misc data - tile.adj = [_]?usize{ null, null, null, null }; - - try tiles.append(tile); - } -} - -fn matchTiles(tiles: *std.ArrayList(Tile)) void { - // for each items's side, look for another item with a matching side - for (tiles.items) |*tile, tile_index| { - sides_loop: for (tile.sides) |_, i| { - for (tiles.items) |*otile, otile_index| { - if (tile_index == otile_index) continue; - - for (otile.sides) |_, oi| { - if (tile.adj[i] == null and otile.adj[oi] == null and couldMatch(tile.sides[i], otile.sides[oi])) { - tile.adj[i] = otile_index; - otile.adj[oi] = tile_index; - continue :sides_loop; - } - } - } - } - - if (aoc.debug) { - std.debug.print("{}:\nSIDES ", .{tile.id}); - for (tile.sides) |side| { - for (side) |b| { - var c: u8 = if (b == 1) '#' else '.'; - std.debug.print("{c}", .{c}); - } - std.debug.print(" ", .{}); - } - std.debug.print("\n", .{}); - for (tile.adj) |adj, i| { - if (tile.adj[i] == null) { - std.debug.print("null ", .{}); - } else { - var adjtile = tiles.items[adj.?]; - std.debug.print("{} ", .{adjtile.id}); - } - } - std.debug.print("\n\n", .{}); - } - } -} - -fn checkSides(tiles: []Tile, grid: []?usize, grid_width: u32, p: aoc.Pos, adj: [4]?usize, sides: [4][10]u1) bool { - var rot: u32 = 0; - while (rot < 4) : (rot += 1) { - const np = p.add(dirs[rot]); - std.debug.print("{}\n", .{@intToEnum(aoc.Dir.Name, @intCast(u2, rot))}); - std.debug.print("{} {}\n", .{ np.x, np.y }); - - var side: ?[tile_width]u1 = undefined; - if (np.x < 0 or np.x >= grid_width or np.y < 0 or np.y >= grid_width) { - std.debug.print("Side is null\n", .{}); - if (adj[rot] == null) { - continue; - } else { - std.debug.print("Side {} should be NULL!\n", .{@intToEnum(aoc.Dir.Name, @intCast(u2, rot))}); - return false; - } - } - - var ti: ?usize = grid[@intCast(u32, np.y * @intCast(i64, grid_width) + np.x)] orelse continue; - - var otherside = tiles[ti.?].sides[aoc.Dir.nextCW(rot, 2)]; - std.mem.reverse(u1, otherside[0..]); - printTileSlice(otherside[0..]); - std.debug.print(" vs ", .{}); - printTileSlice(sides[rot][0..]); - std.debug.print("\n", .{}); - - if (!std.mem.eql(u1, otherside[0..], sides[rot][0..])) return false; - } - return true; -} - -fn rotateData(newdata: *[]u1, olddata: []u1, width: u32, rot: usize) void { - for (olddata) |v, di| { - const dy = @divFloor(di, width); - const dx = di % width; - var nx = dx; - var ny = dy; - switch (rot) { - @enumToInt(aoc.Dir.Name.NORTH) => {}, - @enumToInt(aoc.Dir.Name.EAST) => { - nx = width - 1 - dy; - ny = dx; - }, - @enumToInt(aoc.Dir.Name.SOUTH) => { - nx = width - 1 - dx; - ny = width - 1 - dy; - }, - @enumToInt(aoc.Dir.Name.WEST) => { - nx = dy; - ny = width - 1 - dx; - }, - else => {}, - } - newdata.*[ny * width + nx] = v; - } -} - -fn flipData(data: []u1, width: u32, flip: Flip) void { - switch (flip) { - Flip.HOR => { - var y: u32 = 0; - while (y < width) : (y += 1) { - std.mem.reverse(u1, data[y * width .. (y + 1) * width]); - } - }, - Flip.VERT => { - var x: u32 = 0; - while (x < width) : (x += 1) { - var y: u32 = 0; - while (y < width / 2) : (y += 1) { - std.mem.swap(u1, &data[x + y * width], &data[x + (width - 1 - y) * width]); - } - } - }, - else => {}, - } -} - -fn manipulateAndPlace(allocator: *std.mem.Allocator, tiles: []Tile, grid: []?usize, grid_width: u32, p: aoc.Pos, tile_index: usize) !void { - var tile = &tiles[tile_index]; - - for (dirs) |_, rot| { - for (flips) |flip| { - // apply manipulation to only sides first for checking - var sides = tile.sides; - var tmp_sides = tile.sides; - var adj = tile.adj; - var tmp_adj = tile.adj; - switch (flip) { - Flip.HOR => { - std.mem.reverse(u1, tmp_sides[@enumToInt(aoc.Dir.Name.NORTH)][0..]); - std.mem.reverse(u1, tmp_sides[@enumToInt(aoc.Dir.Name.SOUTH)][0..]); - tmp_sides[@enumToInt(aoc.Dir.Name.WEST)] = sides[@enumToInt(aoc.Dir.Name.EAST)]; - tmp_sides[@enumToInt(aoc.Dir.Name.EAST)] = sides[@enumToInt(aoc.Dir.Name.WEST)]; - std.mem.reverse(u1, tmp_sides[@enumToInt(aoc.Dir.Name.EAST)][0..]); - std.mem.reverse(u1, tmp_sides[@enumToInt(aoc.Dir.Name.WEST)][0..]); - std.mem.swap(?usize, &adj[@enumToInt(aoc.Dir.Name.EAST)], &adj[@enumToInt(aoc.Dir.Name.WEST)]); - }, - Flip.VERT => { - std.mem.reverse(u1, tmp_sides[@enumToInt(aoc.Dir.Name.WEST)][0..]); - std.mem.reverse(u1, tmp_sides[@enumToInt(aoc.Dir.Name.EAST)][0..]); - tmp_sides[@enumToInt(aoc.Dir.Name.NORTH)] = sides[@enumToInt(aoc.Dir.Name.SOUTH)]; - tmp_sides[@enumToInt(aoc.Dir.Name.SOUTH)] = sides[@enumToInt(aoc.Dir.Name.NORTH)]; - std.mem.reverse(u1, tmp_sides[@enumToInt(aoc.Dir.Name.NORTH)][0..]); - std.mem.reverse(u1, tmp_sides[@enumToInt(aoc.Dir.Name.SOUTH)][0..]); - std.mem.swap(?usize, &adj[@enumToInt(aoc.Dir.Name.NORTH)], &adj[@enumToInt(aoc.Dir.Name.SOUTH)]); - }, - else => {}, - } - sides = tmp_sides; - - for (dirs) |_, i| { - tmp_sides[i] = sides[aoc.Dir.nextCCW(i, rot)]; - tmp_adj[i] = adj[aoc.Dir.nextCCW(i, rot)]; - } - - std.debug.print("\nROT: {}\n", .{rot}); - std.debug.print("FLIP: {}\n", .{flip}); - std.debug.print("SIDES: \n", .{}); - for (tmp_sides) |side| { - printTileSlice(side[0..]); - std.debug.print("\n", .{}); - } - const valid = checkSides(tiles, grid, grid_width, p, tmp_adj, tmp_sides); - - // now apply manipulations to image data - if (valid) { - // for (tile.sides) |side| { - // printTileSlice(side[0..]); - // std.debug.print("\n", .{}); - // } - std.debug.print("{} {} {}\n", .{ tile.id, flip, rot }); - printTile(tile.data[0..], tile_width); - std.debug.print("\n", .{}); - tile.sides = tmp_sides; - tile.pos = p; - tile.adj = tmp_adj; - grid[@intCast(u32, p.y * @intCast(i64, grid_width) + p.x)] = tile_index; - - flipData(tile.data[0..], tile_width, flip); - - var newdata = try allocator.alloc(u1, tile_width * tile_width); - defer allocator.free(newdata); - - rotateData(&newdata, tile.data[0..], tile_width, rot); - std.mem.copy(u1, tile.data[0..], newdata); - return; - } - } - } - return aoc.Error.InvalidInput; -} - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var tiles = std.ArrayList(Tile).init(allocator); - defer tiles.deinit(); - - try parseTiles(&tiles, input); - - matchTiles(&tiles); - - var answer: u64 = 1; - for (tiles.items) |tile| { - var unknown: u32 = 0; - for (tile.adj) |v| { - unknown += @boolToInt(v == null); - } - if (unknown == 2) answer *= tile.id; - } - - std.debug.print("{}\n", .{answer}); -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var tiles = std.ArrayList(Tile).init(allocator); - defer tiles.deinit(); - - try parseTiles(&tiles, input); - - matchTiles(&tiles); - - var grid = try allocator.alloc(?usize, tiles.items.len); - defer allocator.free(grid); - var grid_width = @intCast(u32, std.math.sqrt(tiles.items.len)); - - var left = std.ArrayList(usize).init(allocator); - defer left.deinit(); - - for (tiles.items) |_, tile_index| { - try left.append(tile_index); - } - - // add a corner as first item to add onto - for (tiles.items) |tile, tile_index| { - var unknown: u32 = 0; - for (tile.adj) |v| { - unknown += @boolToInt(v == null); - } - if (unknown == 2) { - try manipulateAndPlace(allocator, tiles.items, grid, grid_width, aoc.Pos{ .x = 0, .y = 0 }, tile_index); - _ = left.swapRemove(tile_index); - break; - } - } - - // find adjacents and manipulate until they fit - next_tile: while (left.items.len > 0) { - if (aoc.debug) printGrid(tiles.items, grid, grid_width); - - for (grid) |_, gi| { - std.debug.print("POS {}\n", .{gi}); - const tile_index: ?usize = grid[gi] orelse continue; - std.debug.print("POS {} YES\n", .{gi}); - - var placed = &tiles.items[tile_index.?]; - for (placed.adj) |want, dir| { - if (want == null) continue; - // std.debug.print("LOOKING FOR {}\n", .{want.?}); - // for (left.items) |item| { - // std.debug.print("> {}\n", .{item}); - // } - if (std.mem.indexOfScalar(usize, left.items, want.?)) |left_index| { - const new_index = left.items[left_index]; - const new_tile = tiles.items[new_index]; - var npos = placed.pos.add(dirs[dir]); - std.debug.print("TRYING TO PLACE {} NEW AT: {} {}\n", .{ new_tile.id, npos.x, npos.y }); - - try manipulateAndPlace(allocator, tiles.items, grid, grid_width, npos, new_index); - - _ = left.swapRemove(left_index); - continue :next_tile; - } - } - } - return aoc.Error.InvalidInput; - } - - if (aoc.debug) printGrid(tiles.items, grid, grid_width); - - // convert grid to image - var image = try allocator.alloc(u1, grid.len * (tile_width - 2) * (tile_width - 2)); - defer allocator.free(image); - - const image_width = grid_width * (tile_width - 2); - var y: u32 = 0; - while (y < grid_width) : (y += 1) { - var x: u32 = 0; - while (x < grid_width) : (x += 1) { - var tile = tiles.items[grid[y * grid_width + x].?]; - var doff = (y * grid_width * (tile_width - 2) + x) * (tile_width - 2); - var dy: u32 = 1; - while (dy < tile_width - 1) : (dy += 1) { - var dx: u32 = 1; - while (dx < tile_width - 1) : (dx += 1) { - image[doff + (dy - 1) * image_width + (dx - 1)] = tile.data[dy * tile_width + dx]; - } - } - } - } - - y = 0; - while (y < grid_width * (tile_width - 2)) : (y += 1) { - printTileSlice(image[y * grid_width * (tile_width - 2) .. (y + 1) * grid_width * (tile_width - 2)]); - std.debug.print("\n", .{}); - } - - // rotate image till we find a sea monster - var image_copy = try allocator.dupe(u1, image); - defer allocator.free(image_copy); - - var rot: usize = 0; - while (rot < 4) : (rot += 1) { - for (flips) |flip| { - flipData(image, image_width, flip); - rotateData(&image_copy, image, image_width, rot); - - y = 0; - var count: u32 = 0; - while (y < image_width - seamonster.len) : (y += 1) { - var x: u32 = 0; - check_next: while (x < image_width - seamonster[0].len) : (x += 1) { - var sy: u32 = 0; - while (sy < seamonster.len) : (sy += 1) { - var sx: u32 = 0; - while (sx < seamonster[0].len) : (sx += 1) { - if (seamonster[sy][sx] == '#' and image_copy[(y + sy) * image_width + (x + sx)] != 1) - continue :check_next; - } - } - count += 1; - } - } - - if (count > 0) { - var image_count = std.mem.count(u1, image_copy, ([_]u1{1})[0..]); - var seamonster_count: usize = 0; - var sy: u32 = 0; - while (sy < seamonster.len) : (sy += 1) { - seamonster_count += std.mem.count(u8, seamonster[sy], "#"); - } - std.debug.print("{}\n", .{image_count - count * seamonster_count}); - return; - } - } - } -} - -pub const main = aoc.gen_main(part1, part2); diff --git a/src/day21/main.zig b/src/day21/main.zig @@ -1,180 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); - -const hasher = std.hash.CityHash32; -const IngredientInfo = struct { - count: u32, -}; -const Pair = struct { ingredient: []const u8, allergen: []const u8 }; - -fn freeListmap(listmap: *std.AutoHashMap(u32, std.ArrayList(u32))) void { - var mapit = listmap.iterator(); - while (mapit.next()) |kv| { - kv.value.deinit(); - } - listmap.deinit(); -} - -fn getAllergen(allergens: *std.AutoHashMap(u32, std.ArrayList(u32)), ingredient: u32) ?u32 { - var allergenit = allergens.iterator(); - while (allergenit.next()) |alg| { - if (alg.value.items.len == 1 and alg.value.items[0] == ingredient) - return alg.key; - } - return null; -} - -fn updateAllergens(allocator: *std.mem.Allocator, allergens: *std.AutoHashMap(u32, std.ArrayList(u32)), allergen: u32) !void { - var next = std.ArrayList(u32).init(allocator); - defer next.deinit(); - - try next.append(allergen); - - while (next.items.len > 0) { - const key = next.items[0]; - var ingredient = allergens.getEntry(key).?.value.items[0]; - var mapit = allergens.iterator(); - while (mapit.next()) |alg| { - if (alg.key == key) continue; - const ind = std.mem.indexOfScalar(u32, alg.value.items, ingredient); - if (ind != null) { - _ = alg.value.swapRemove(ind.?); - if (alg.value.items.len == 1) { - try next.append(alg.key); - } - } - } - _ = next.swapRemove(0); - } -} - -fn parseAllergens(allocator: *std.mem.Allocator, allergens: *std.AutoHashMap(u32, std.ArrayList(u32)), unhash: *std.AutoHashMap(u32, []const u8), ingredient_counts: *std.AutoHashMap(u32, u32), input: []const u8) !void { - var ingredients = std.ArrayList(u32).init(allocator); - defer ingredients.deinit(); - - var lineit = std.mem.tokenize(input, "\n"); - while (lineit.next()) |line| { - var allergen = false; - - try ingredients.resize(0); - - var spaceit = std.mem.tokenize(line, " "); - while (spaceit.next()) |word| { - if (std.mem.eql(u8, word, "(contains")) { - allergen = true; - continue; - } - - if (allergen) { - const allergen_name = if (word[word.len - 1] == ')' or word[word.len - 1] == ',') word[0 .. word.len - 1] else word; - const allergen_hash = hasher.hash(allergen_name); - try unhash.put(allergen_hash, allergen_name); - var algentry = allergens.getEntry(allergen_hash); - if (algentry) |algent| { - var i: u32 = 0; - while (i < algent.value.items.len) { - if (std.mem.indexOfScalar(u32, ingredients.items, algent.value.items[i]) == null) { - _ = algent.value.swapRemove(i); - } else { - i += 1; - } - } - - if (algent.value.items.len == 1) { - try updateAllergens(allocator, allergens, algent.key); - } - } else { - var copy = std.ArrayList(u32).init(allocator); - errdefer copy.deinit(); - for (ingredients.items) |ing| { - if (getAllergen(allergens, ing) == null) { - try copy.append(ing); - } - } - try allergens.put(allergen_hash, copy); - } - } else { - const ingredient_hash = hasher.hash(word); - try unhash.put(ingredient_hash, word); - try ingredients.append(ingredient_hash); - const entry = ingredient_counts.getEntry(ingredient_hash); - if (entry == null) { - try ingredient_counts.put(ingredient_hash, 1); - } else { - entry.?.value += 1; - } - } - } - } -} - -fn ascendingAllergen(ctx: void, p1: Pair, p2: Pair) bool { - var i: u32 = 0; - while (i < std.math.min(p1.allergen.len, p2.allergen.len) and p1.allergen[i] == p2.allergen[i]) : (i += 1) {} - return p1.allergen[i] < p2.allergen[i]; -} - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var allergens = std.AutoHashMap(u32, std.ArrayList(u32)).init(allocator); - defer freeListmap(&allergens); - - var ingredient_counts = std.AutoHashMap(u32, u32).init(allocator); - defer ingredient_counts.deinit(); - - var unhash = std.AutoHashMap(u32, []const u8).init(allocator); - defer unhash.deinit(); - - try parseAllergens(allocator, &allergens, &unhash, &ingredient_counts, input); - - var answer: u32 = 0; - - var algit = allergens.iterator(); - while (algit.next()) |alg| { - std.debug.print("{} - ", .{alg.key}); - for (alg.value.items) |v| { - std.debug.print("{} ", .{v}); - } - std.debug.print("\n", .{}); - } - - var mapit = ingredient_counts.iterator(); - while (mapit.next()) |kv| { - if (getAllergen(&allergens, kv.key) == null) answer += kv.value; - } - - std.debug.print("{}\n", .{answer}); -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var allergens = std.AutoHashMap(u32, std.ArrayList(u32)).init(allocator); - defer freeListmap(&allergens); - - var ingredient_counts = std.AutoHashMap(u32, u32).init(allocator); - defer ingredient_counts.deinit(); - - var unhash = std.AutoHashMap(u32, []const u8).init(allocator); - defer unhash.deinit(); - - try parseAllergens(allocator, &allergens, &unhash, &ingredient_counts, input); - - var pairs = std.ArrayList(Pair).init(allocator); - defer pairs.deinit(); - - var mapit = ingredient_counts.iterator(); - while (mapit.next()) |kv| { - const alg = getAllergen(&allergens, kv.key); - if (alg != null) { - try pairs.append(Pair{ .ingredient = unhash.get(kv.key).?, .allergen = unhash.get(alg.?).? }); - } - } - - std.sort.sort(Pair, pairs.items, {}, ascendingAllergen); - - for (pairs.items) |p, i| { - if (i > 0) std.debug.print(",", .{}); - std.debug.print("{}", .{p.ingredient}); - } - std.debug.print("\n", .{}); -} - -pub const main = aoc.gen_main(part1, part2); diff --git a/src/day22/main.zig b/src/day22/main.zig @@ -1,180 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); - -const Player = enum { P1, P2 }; -const GameResult = struct { - score: u64, winner: Player -}; - -fn parseInput(input: []u8, decks: [2]*std.ArrayList(u32)) !void { - var partit = std.mem.split(input, "\n\n"); - var decknum: u32 = 0; - while (partit.next()) |part| : (decknum += 1) { - if (decknum == 2) break; - var lineit = std.mem.tokenize(part, "\n"); - _ = lineit.next(); - while (lineit.next()) |line| { - try decks[decknum].append(try std.fmt.parseInt(u32, line, 10)); - } - } - if (decknum < 2) return aoc.Error.InvalidInput; -} - -fn printDeck(deck: *std.ArrayList(u32)) void { - for (deck.items) |v, i| { - if (i > 0) std.debug.print(", ", .{}); - std.debug.print("{}", .{v}); - } -} - -fn printRoundInfo(roundnum: u32, decks: [2]*std.ArrayList(u32)) void { - std.debug.print("\n-- ROUND {} --\n", .{roundnum}); - std.debug.print("Player 1's deck: ", .{}); - printDeck(decks[0]); - std.debug.print("\nPlayer 2's deck: ", .{}); - printDeck(decks[1]); - std.debug.print("\n", .{}); -} - -fn printPickedCards(card1: u32, card2: u32) void { - std.debug.print("Player 1 plays: {}\n", .{card1}); - std.debug.print("Player 2 plays: {}\n", .{card2}); -} - -fn calcScore(deck: *std.ArrayList(u32)) u64 { - var score: u64 = 0; - for (deck.items) |v, i| { - score += v * (deck.items.len - i); - } - return score; -} - -fn copyList(allocator: *std.mem.Allocator, list: []u32) !std.ArrayList(u32) { - var newlist = std.ArrayList(u32).init(allocator); - errdefer newlist.deinit(); - - for (list) |v| { - try newlist.append(v); - } - - return newlist; -} - -fn part1Round( - allocator: *std.mem.Allocator, - roundnum: u32, - decks: [2]*std.ArrayList(u32), -) anyerror!void { - if (aoc.debug) printRoundInfo(roundnum, decks); - const card1: u32 = decks[0].orderedRemove(0); - const card2: u32 = decks[1].orderedRemove(0); - if (aoc.debug) printPickedCards(card1, card2); - if (card1 > card2) { - if (aoc.debug) std.debug.print("Player1 wins\n", .{}); - try decks[0].append(card1); - try decks[0].append(card2); - } else if (card2 > card1) { - if (aoc.debug) std.debug.print("Player2 wins\n", .{}); - try decks[1].append(card2); - try decks[1].append(card1); - } else return aoc.Error.InvalidInput; -} - -fn part2Round( - allocator: *std.mem.Allocator, - roundnum: u32, - decks: [2]*std.ArrayList(u32), -) anyerror!void { - if (aoc.debug) printRoundInfo(roundnum, decks); - const card1: u32 = decks[0].orderedRemove(0); - const card2: u32 = decks[1].orderedRemove(0); - if (aoc.debug) printPickedCards(card1, card2); - - var winner = @intToEnum(Player, @boolToInt(card2 > card1)); - if (card1 <= decks[0].items.len and card2 <= decks[1].items.len) { - if (aoc.debug) std.debug.print("\nStarting subgame..\n", .{}); - var tmp_deck1 = try copyList(allocator, decks[0].items[0..card1]); - defer tmp_deck1.deinit(); - - var tmp_deck2 = try copyList(allocator, decks[1].items[0..card2]); - defer tmp_deck2.deinit(); - - const result = try playGame(allocator, [2]*std.ArrayList(u32){ &tmp_deck1, &tmp_deck2 }, part2Round); - winner = result.winner; - if (aoc.debug) { - const wp: u32 = if (winner == Player.P1) 1 else 2; - std.debug.print("Player{} wins the subgame\n...anyway, back to previous game\n\n", .{wp}); - } - } - - if (winner == Player.P1) { - if (aoc.debug) std.debug.print("Player1 wins the round\n", .{}); - try decks[0].append(card1); - try decks[0].append(card2); - } else if (winner == Player.P2) { - if (aoc.debug) std.debug.print("Player2 wins the round\n", .{}); - try decks[1].append(card2); - try decks[1].append(card1); - } else return aoc.Error.InvalidInput; -} - -fn playGame( - allocator: *std.mem.Allocator, - decks: [2]*std.ArrayList(u32), - roundFunc: fn ( - alloc: *std.mem.Allocator, - num: u32, - decks: [2]*std.ArrayList(u32), - ) anyerror!void, -) !GameResult { - var ctxstack = std.ArrayList(u64).init(allocator); - defer ctxstack.deinit(); - - var roundnum: u32 = 1; - while (decks[0].items.len > 0 and decks[1].items.len > 0) : (roundnum += 1) { - try roundFunc(allocator, roundnum, decks); - - var hash: u64 = calcScore(decks[0]) + 100000 * calcScore(decks[1]); - if (std.mem.indexOfScalar(u64, ctxstack.items, hash) != null) { - return GameResult{ .score = calcScore(decks[0]), .winner = Player.P1 }; - } - - try ctxstack.append(hash); - } - - var nonempty = if (decks[0].items.len != 0) decks[0] else decks[1]; - return GameResult{ - .score = calcScore(nonempty), - .winner = if (nonempty == decks[0]) Player.P1 else Player.P2, - }; -} - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var deck1 = std.ArrayList(u32).init(allocator); - defer deck1.deinit(); - - var deck2 = std.ArrayList(u32).init(allocator); - defer deck2.deinit(); - - const decks = [2]*std.ArrayList(u32){ &deck1, &deck2 }; - try parseInput(input, decks); - - const result = try playGame(allocator, decks, part1Round); - std.debug.print("{}\n", .{result.score}); -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var deck1 = std.ArrayList(u32).init(allocator); - defer deck1.deinit(); - - var deck2 = std.ArrayList(u32).init(allocator); - defer deck2.deinit(); - - const decks = [2]*std.ArrayList(u32){ &deck1, &deck2 }; - try parseInput(input, decks); - - const result = try playGame(allocator, decks, part2Round); - std.debug.print("{}\n", .{result.score}); -} - -pub const main = aoc.gen_main(part1, part2); diff --git a/src/day23/main.zig b/src/day23/main.zig @@ -1,180 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); - -// Links in this list are arranges in a circle -const RingLink = struct { - data: u32, - next: *RingLink, - const Self = @This(); - - pub fn insertNext(self: *Self, link: *RingLink) void { - link.next = self.next; - self.next = link; - } - - pub fn popNext(self: *Self) !*Self { - const nlink = self.next; - if (nlink == self) return error.EmptyRingList; - self.next = nlink.next; - return nlink; - } - - pub fn length(self: *Self) usize { - var count: usize = 1; - var link = self.next; - while (link != self) : (link = link.next) { - count += 1; - } - return count; - } - - pub fn advance(self: *Self, count: usize) *RingLink { - var link = self; - var counter: usize = 0; - while (counter < count) : (link = link.next) { - counter += 1; - } - return link; - } -}; - -fn printLinks(start: *RingLink, end: *RingLink, skipfirst: bool) void { - var link = start; - var b = skipfirst; - while (b or link != end) : (link = link.next) { - b = false; - std.debug.print("{} ", .{link.data}); - } -} - -fn parseInput(input: []const u8, lookup: []RingLink, list: *?*RingLink) !u32 { - var max: u32 = 0; - var link: *RingLink = undefined; - for (input) |c, i| { - if (c == '\n') break; - const v = try std.fmt.parseInt(u32, input[i .. i + 1], 10); - if (v > max) max = v; - if (list.* == null) { - list.* = &lookup[v]; - link = list.*.?; - link.next = link; - } else { - link.insertNext(&lookup[v]); - link = link.next; - } - } - return max; -} - -fn doRound(list: *RingLink, len: u32, lookup: []RingLink, current: **RingLink, max: u32, round: usize) !void { - var target = (current.*.data + max - 2) % max + 1; - var k: usize = 0; - var check = current.*.next; - while (k < 9) : (k += 1) { - if (check.data == target) - target = (target + max - 2) % max + 1; - - check = if (k % 3 == 0) current.*.next else check.next; - } - var closest = &lookup[target]; - - if (aoc.debug) { - std.debug.print("\n-- move {} --\ncups:", .{round}); - - if (aoc.debuglvl == 1) { - var start = current.*.advance(len - ((round - 1) % len)); - var link = start; - while (true) { - if (link == current.*) { - std.debug.print(" ({})", .{link.data}); - } else { - std.debug.print(" {} ", .{link.data}); - } - link = link.next; - if (link == start) break; - } - std.debug.print("\n", .{}); - } else { - std.debug.print("\n{} {} {}\n", .{ current.*.data, target, closest.data }); - std.debug.print(".. ", .{}); - printLinks(current.*, current.*.next.next.next.next, false); - std.debug.print("..\n.. ", .{}); - printLinks(closest, closest.next.next.next.next, false); - std.debug.print("..\n", .{}); - } - } - - var i: usize = 0; - while (i < 3) : (i += 1) { - const poplink = try current.*.popNext(); - closest.insertNext(poplink); - closest = poplink; - } - - current.* = current.*.next; -} - -fn createLookup(allocator: *std.mem.Allocator, len: usize) ![]RingLink { - var lookup = try allocator.alloc(RingLink, len + 1); - errdefer allocator.free(lookup); - - var i: usize = 1; - while (i <= len) : (i += 1) { - lookup[i] = RingLink{ .data = @intCast(u32, i), .next = undefined }; - } - - return lookup; -} - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var lookup = try createLookup(allocator, 9); - defer allocator.free(lookup); - - var list: ?*RingLink = null; - const max = try parseInput(input, lookup, &list); - if (list == null) return aoc.Error.InvalidInput; - const real_len = @intCast(u32, list.?.length()); - - var round: usize = 0; - var current = list.?; - while (round < 100) : (round += 1) { - try doRound(list.?, real_len, lookup, &current, max, round + 1); - } - - var start = &lookup[1]; - var link = start.next; - while (link != start) : (link = link.next) { - std.debug.print("{}", .{link.data}); - } - std.debug.print("\n", .{}); -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var lookup = try createLookup(allocator, 1000000); - defer allocator.free(lookup); - - var list: ?*RingLink = null; - const max = try parseInput(input, lookup, &list); - if (list == null) return aoc.Error.InvalidInput; - const real_len = list.?.length(); - - var end = list.?.advance(real_len - 1); - var i = real_len + 1; - while (i <= 1000000) : (i += 1) { - end.insertNext(&lookup[i]); - end = end.next; - } - - var round: usize = 0; - var current = list.?; - while (round < 10000000) : (round += 1) { - if (round % 1000000 == 0) std.debug.print(".", .{}); - try doRound(list.?, 1000000, lookup, &current, 1000000, round + 1); - } - std.debug.print("\n", .{}); - - var link = (&lookup[1]).next; - std.debug.print("{} * {} = {}\n", .{ @intCast(u64, link.data), @intCast(u64, link.next.data), @intCast(u64, link.data) * @intCast(u64, link.next.data) }); -} - -pub const main = aoc.gen_main(part1, part2); diff --git a/src/day24/main.zig b/src/day24/main.zig @@ -1,121 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); - -const Color = enum { BLACK, WHITE }; -const Dir = enum { E, SE, SW, W, NW, NE }; -const dirs = [_]aoc.Pos{ - aoc.Pos{ .x = 2, .y = 0 }, - aoc.Pos{ .x = 1, .y = -1 }, - aoc.Pos{ .x = -1, .y = -1 }, - aoc.Pos{ .x = -2, .y = 0 }, - aoc.Pos{ .x = -1, .y = 1 }, - aoc.Pos{ .x = 1, .y = 1 }, -}; -const tokens = [_][]const u8{ - "e", - "se", - "sw", - "w", - "nw", - "ne", -}; -const Tile = struct { - color: Color, -}; - -fn parseInput(tiles: *std.AutoHashMap(aoc.Pos, Tile), input: []const u8) !void { - var lineit = std.mem.tokenize(input, "\n"); - while (lineit.next()) |line| { - var pos = aoc.Pos{ .x = 0, .y = 0 }; - - var i: usize = 0; - while (i < line.len) { - var dir = for (tokens) |tok, ti| { - if (i + tok.len > line.len) continue; - if (std.mem.eql(u8, tok, line[i .. i + tok.len])) { - i += tok.len; - break @intToEnum(Dir, @intCast(u3, ti)); - } - } else return aoc.Error.InvalidInput; - if (aoc.debug) std.debug.print("{} ", .{dir}); - pos = pos.add(dirs[@enumToInt(dir)]); - } - if (aoc.debug) std.debug.print("=> {} {}\n", .{ pos.x, pos.y }); - - var tile = tiles.getEntry(pos); - if (tile != null) { - tile.?.value.color = if (tile.?.value.color == Color.WHITE) Color.BLACK else Color.WHITE; - } else { - try tiles.put(pos, Tile{ .color = Color.BLACK }); - } - } -} - -fn applyRule(pos: aoc.Pos, before: *std.AutoHashMap(aoc.Pos, Tile), after: *std.AutoHashMap(aoc.Pos, Tile)) !void { - if (after.contains(pos)) return; - const old_tile = before.get(pos); - const old_color = if (old_tile == null) Color.WHITE else old_tile.?.color; - - var adj: u32 = 0; - for (dirs) |d| { - const tile = before.get(pos.add(d)); - if (tile != null and tile.?.color == Color.BLACK) adj += 1; - } - - if (adj == 2 and old_color == Color.WHITE) { - try after.put(pos, Tile{ .color = Color.BLACK }); - } else if ((adj == 0 or adj > 2) and old_color == Color.BLACK) { - try after.put(pos, Tile{ .color = Color.WHITE }); - } else { - try after.put(pos, Tile{ .color = old_color }); - } -} - -fn doRound(allocator: *std.mem.Allocator, tiles: *std.AutoHashMap(aoc.Pos, Tile)) !void { - var result = std.AutoHashMap(aoc.Pos, Tile).init(allocator); - defer result.deinit(); - - var mapit = tiles.iterator(); - while (mapit.next()) |kv| { - for (dirs) |d| { - try applyRule(kv.key.add(d), tiles, &result); - } - try applyRule(kv.key, tiles, &result); - } - - std.mem.swap(std.AutoHashMap(aoc.Pos, Tile), &result, tiles); -} - -fn countBlack(tiles: *std.AutoHashMap(aoc.Pos, Tile)) u32 { - var count: u32 = 0; - var mapit = tiles.iterator(); - while (mapit.next()) |kv| { - if (kv.value.color == Color.BLACK) count += 1; - } - return count; -} - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var tiles = std.AutoHashMap(aoc.Pos, Tile).init(allocator); - defer tiles.deinit(); - - try parseInput(&tiles, input); - - std.debug.print("{}\n", .{countBlack(&tiles)}); -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var tiles = std.AutoHashMap(aoc.Pos, Tile).init(allocator); - defer tiles.deinit(); - - try parseInput(&tiles, input); - - var round: usize = 0; - while (round < 100) : (round += 1) { - try doRound(allocator, &tiles); - } - - std.debug.print("{}\n", .{countBlack(&tiles)}); -} - -pub const main = aoc.gen_main(part1, part2); diff --git a/src/day25/main.zig b/src/day25/main.zig @@ -1,56 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); - -fn transform(subject_num: u64, loops: u64) u64 { - var num: u64 = 1; - var i: u64 = 0; - while (i < loops) : (i += 1) { - num *= subject_num; - num %= 20201227; - } - return num; -} - -fn bfLoops(subject_num: u64, pubkey: u64) ?u64 { - var i: u64 = 0; - var tmp: u64 = 1; - while (i < ~@as(u64, 0)) : (i += 1) { - if (tmp == pubkey) return i; - tmp *= subject_num; - tmp %= 20201227; - } - return null; -} - -fn parseInput(door_pubkey: *u64, card_pubkey: *u64, input: []const u8) !void { - var lineit = std.mem.tokenize(input, "\n"); - door_pubkey.* = try std.fmt.parseInt(u64, try aoc.assertV(lineit.next()), 10); - card_pubkey.* = try std.fmt.parseInt(u64, try aoc.assertV(lineit.next()), 10); -} - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var door_pubkey: u64 = undefined; - var card_pubkey: u64 = undefined; - - try parseInput(&door_pubkey, &card_pubkey, input); - - if (args.len == 0 or std.mem.eql(u8, args[0], "bf_door")) { - if (bfLoops(7, door_pubkey)) |door_loops| { - std.debug.print("{}\n", .{transform(card_pubkey, door_loops)}); - return; - } - } else if (args.len > 0 and std.mem.eql(u8, args[0], "bf_card")) { - if (bfLoops(7, card_pubkey)) |card_loops| { - std.debug.print("{}\n", .{transform(door_pubkey, card_loops)}); - return; - } - } -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var answer: u32 = 0; - - std.debug.print("{}\n", .{answer}); -} - -pub const main = aoc.gen_main(part1, part2); diff --git a/src/day3/main.zig b/src/day3/main.zig @@ -1,73 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); - -const Entry = []u1; -const Vector = struct { - x: u16, y: u16 -}; - -fn freeEntries(allocator: *std.mem.Allocator, entries: *const std.ArrayList(Entry)) void { - for (entries.items) |entry| { - allocator.free(entry); - } - entries.deinit(); -} - -fn parse(allocator: *std.mem.Allocator, input: []u8) !std.ArrayList(Entry) { - var entries = std.ArrayList(Entry).init(allocator); - errdefer freeEntries(allocator, &entries); - - var lineit = std.mem.tokenize(input, "\n"); - while (lineit.next()) |line| { - var linemap: []u1 = try allocator.alloc(u1, line.len); - errdefer allocator.free(linemap); - - for (line) |c, i| { - linemap[i] = @boolToInt(c == '#'); - } - try entries.append(linemap); - } - - return entries; -} - -fn treesHit(map: std.ArrayList(Entry), slope: Vector) u16 { - var count: u16 = 0; - var pos = Vector{ .x = 0, .y = 0 }; - while (pos.y < map.items.len - 1) { - pos.x += slope.x; - pos.y += slope.y; - - count += map.items[pos.y][pos.x % map.items[0].len]; - } - return count; -} - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - const map = try parse(allocator, input); - defer freeEntries(allocator, &map); - - const answer = treesHit(map, Vector{ .x = 3, .y = 1 }); - std.debug.print("{}\n", .{answer}); -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - const map = try parse(allocator, input); - defer freeEntries(allocator, &map); - - var answer: u32 = 1; - const slopes = [_]Vector{ - Vector{ .x = 1, .y = 1 }, - Vector{ .x = 3, .y = 1 }, - Vector{ .x = 5, .y = 1 }, - Vector{ .x = 7, .y = 1 }, - Vector{ .x = 1, .y = 2 }, - }; - for (slopes) |slope| { - answer *= treesHit(map, slope); - } - - std.debug.print("{}\n", .{answer}); -} - -pub const main = aoc.gen_main(part1, part2); diff --git a/src/day4/main.zig b/src/day4/main.zig @@ -1,100 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); - -fn intChecker( - comptime min: u32, - comptime max: u32, - comptime len: ?u32, - comptime suffix: ?[]const u8, -) fn ([]const u8) bool { - const impl = struct { - fn check(input: []const u8) bool { - var parsed = input; - if (suffix) |suf| { - if (!std.mem.eql(u8, suf, input[(input.len - suf.len)..])) - return false; - parsed = input[0..(input.len - suf.len)]; - } - if (len != null and parsed.len != len.?) - return false; - const val = std.fmt.parseInt(u32, parsed, 10) catch |x| { - return false; - }; - return (val >= min and val <= max); - } - }; - return impl.check; -} - -fn combineOr( - comptime f1: fn ([]const u8) bool, - comptime f2: fn ([]const u8) bool, -) fn ([]const u8) bool { - const impl = struct { - fn check(input: []const u8) bool { - return f1(input) or f2(input); - } - }; - return impl.check; -} - -fn isColorStr(input: []const u8) bool { - if (input.len != 7) return false; - _ = std.fmt.parseInt(u32, input[1..], 16) catch |x| { - return false; - }; - return input[0] == '#'; -} - -fn isEyeColor(input: []const u8) bool { - const valids = "amb blu brn gry grn hzl oth"; - return std.mem.indexOf(u8, valids[0..], input) != null; -} - -fn countValids(allocator: *std.mem.Allocator, input: []u8, validate: bool) !u16 { - var entryit = std.mem.split(input, "\n\n"); - const required_keys = [_][]const u8{ "byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid", "cid" }; - const validation_funcs = [required_keys.len]?fn ([]const u8) bool{ - intChecker(1920, 2002, 4, null), - intChecker(2010, 2020, 4, null), - intChecker(2020, 2030, 4, null), - combineOr(comptime intChecker(150, 193, 3, "cm"), comptime intChecker(59, 76, 2, "in")), - isColorStr, - isEyeColor, - intChecker(0, 999999999, 9, null), - null, - }; - var count: u16 = 0; - entryloop: while (entryit.next()) |entry| { - const key_mask = [required_keys.len]u8{ 1, 1, 1, 1, 1, 1, 1, 'X' }; - var present = [required_keys.len]u1{ 0, 0, 0, 0, 0, 0, 0, 0 }; - var partit = std.mem.tokenize(entry, ": \n"); - while (partit.next()) |key| { - const value = partit.next().?; - for (required_keys) |ckey, i| { - if (std.mem.eql(u8, key, ckey)) { - if (validate and validation_funcs[i] != null) { - if (!validation_funcs[i].?(value)) continue :entryloop; - } - present[i] = 1; - } - } - } - for (key_mask) |k, i| { - if (key_mask[i] != 'X' and present[i] != key_mask[i]) - continue :entryloop; - } - count += 1; - } - return count; -} - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - std.debug.print("{}\n", .{try countValids(allocator, input, false)}); -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - std.debug.print("{}\n", .{try countValids(allocator, input, true)}); -} - -pub const main = aoc.gen_main(part1, part2); diff --git a/src/day5/main.zig b/src/day5/main.zig @@ -1,46 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); - -const row_count = 128; -const col_count = 8; - -// Input is coded in a way that we can do an implicit binary search by replacing -// identifiers for the lower half with "0" and for the upper halb with "1" -// See commit <f1b717029cf3262c1fa2760124af258924d668da> for actual binary search - -fn code2Id(input: []const u8) !u32 { - var value: u32 = 0; - for (input) |c| - value = value * 2 + @boolToInt(c == 'B' or c == 'R'); - return value; -} - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var lineit = std.mem.tokenize(input, "\n"); - var answer: u32 = 0; - while (lineit.next()) |line| { - const id = try code2Id(line[0..10]); - answer = std.math.max(answer, id); - } - std.debug.print("{}\n", .{answer}); -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var lineit = std.mem.tokenize(input, "\n"); - var seats = [_]u1{0} ** (row_count * col_count); - var min: u32 = std.math.inf_u32; - while (lineit.next()) |line| { - const id = try code2Id(line); - min = std.math.min(min, id); - seats[id] = 1; - } - - for (seats[min..]) |b, i| { - if (seats[min + i] == 0) { - std.debug.print("{}\n", .{min + i}); - break; - } - } -} - -pub const main = aoc.gen_main(part1, part2); diff --git a/src/day6/main.zig b/src/day6/main.zig @@ -1,33 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var entryit = std.mem.split(input, "\n\n"); - var answer: u32 = 0; - while (entryit.next()) |group| { - var seen = [_]u1{0} ** 256; - for (group) |c| { - if (c == ' ' or c == '\n') continue; - answer += @boolToInt(seen[c] == 0); - seen[c] = 1; - } - } - std.debug.print("{}\n", .{answer}); -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var entryit = std.mem.split(input, "\n\n"); - var answer: u32 = 0; - while (entryit.next()) |group| { - var count = [_]u16{0} ** 256; - var members: u16 = 0; - var memberit = std.mem.tokenize(group, "\n "); - while (memberit.next()) |member| : (members += 1) { - for (member) |c| count[c] += 1; - } - for (count) |v| answer += @boolToInt(v == members); - } - std.debug.print("{}\n", .{answer}); -} - -pub const main = aoc.gen_main(part1, part2); diff --git a/src/day7/main.zig b/src/day7/main.zig @@ -1,105 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); - -const testv = @ptrToInt(&target); -const BagCount = struct { - id: u64, count: u32 -}; - -// null for 'not available' and errors for 'parsing failed' - -const BagRule = struct { - outer: u64, - inner_striter: std.mem.SplitIterator, - const Self = @This(); - - fn from(line: []const u8) !Self { - var partit = std.mem.split(line[0 .. line.len - 1], " bags contain "); - var outer = std.hash.CityHash64.hash(partit.next().?); - var innerit = std.mem.split(partit.next().?, ", "); - return Self{ .outer = outer, .inner_striter = innerit }; - } - - fn nextInner(self: *Self) !?BagCount { - if (self.inner_striter.next()) |bagstr| { - if (std.mem.eql(u8, bagstr[0..2], "no")) return null; - const lastsep = std.mem.lastIndexOfScalar(u8, bagstr, ' ').?; - const firstsep = std.mem.indexOfScalar(u8, bagstr, ' ').?; - const count = try std.fmt.parseInt(u32, bagstr[0..firstsep], 10); - const id = std.hash.CityHash64.hash(bagstr[firstsep + 1 .. lastsep]); - return BagCount{ .id = id, .count = count }; - } else { - return null; - } - } -}; - -fn countBagsTotal(map: *std.AutoHashMap(u64, std.ArrayList(BagCount)), outer: u64) u64 { - var sum: u64 = 0; - if (map.get(outer)) |inner| { - for (inner.items) |bag| - sum += bag.count * (1 + countBagsTotal(map, bag.id)); - } - return sum; -} - -fn countParentTypes(map: *std.AutoHashMap(u64, std.ArrayList(u64)), inner: u64, seen: *std.ArrayList(u64)) anyerror!u64 { - var sum: u64 = 0; - if (map.get(inner)) |outer| { - for (outer.items) |bagid| { - if (std.mem.indexOfScalar(u64, seen.items, bagid) == null) { - try seen.append(bagid); - sum += 1 + try countParentTypes(map, bagid, seen); - } - } - } - return sum; -} - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var lineit = std.mem.tokenize(input, "\n"); - var map = std.AutoHashMap(u64, std.ArrayList(u64)).init(allocator); - defer { - var mapit = map.iterator(); - while (mapit.next()) |kv| kv.value.deinit(); - map.deinit(); - } - - while (lineit.next()) |line| { - var rule = try BagRule.from(line); - while (try rule.nextInner()) |inner| { - if (map.get(inner.id) == null) - try map.put(inner.id, std.ArrayList(u64).init(allocator)); - try map.getEntry(inner.id).?.*.value.append(rule.outer); - } - } - - const target = std.hash.CityHash64.hash("shiny gold"); - var seen = std.ArrayList(u64).init(allocator); - defer seen.deinit(); - std.debug.print("{}\n", .{countParentTypes(&map, target, &seen)}); -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var lineit = std.mem.tokenize(input, "\n"); - var map = std.AutoHashMap(u64, std.ArrayList(BagCount)).init(allocator); - defer { - var mapit = map.iterator(); - while (mapit.next()) |kv| kv.value.deinit(); - map.deinit(); - } - - const target = std.hash.CityHash64.hash("shiny gold"); - while (lineit.next()) |line| { - var rule = try BagRule.from(line); - if (map.get(rule.outer) == null) - try map.put(rule.outer, std.ArrayList(BagCount).init(allocator)); - while (try rule.nextInner()) |bag| { - try map.getEntry(rule.outer).?.*.value.append(bag); - } - } - - std.debug.print("{}\n", .{countBagsTotal(&map, target)}); -} - -pub const main = aoc.gen_main(part1, part2); diff --git a/src/day8/main.zig b/src/day8/main.zig @@ -1,60 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); -const Console = @import("console8").Console; - -fn runTillRepeat(console: *Console, instlist: *std.ArrayList(u64), swapon: ?u64) !void { - while (try console.parseNext()) |*inst| { - // std.debug.print("{:<5}: {} {}\n", .{ console.instructptr, inst.opcode, inst.argval }); - if (std.mem.indexOfScalar(u64, instlist.items, console.instructptr) != null) { - return; - } - if (swapon != null and swapon.? == console.instructptr) { - inst.opfunc = switch (inst.opfunc) { - Console.jumpInstruction => Console.nopInstruction, - Console.nopInstruction => Console.jumpInstruction, - else => unreachable, - }; - } - try instlist.append(console.instructptr); - try console.exec(inst); - } -} - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var console = try Console.init(input, allocator); - defer console.deinit(); - - var instlist = std.ArrayList(u64).init(allocator); - defer instlist.deinit(); - - try runTillRepeat(&console, &instlist, null); - std.debug.print("{}\n", .{console.accumulator}); -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - var console = try Console.init(input, allocator); - defer console.deinit(); - - var instlist = std.ArrayList(u64).init(allocator); - defer instlist.deinit(); - - try runTillRepeat(&console, &instlist, null); - - for (instlist.items) |inst| { - if (std.mem.eql(u8, "acc"[0..3], console.instlist[inst][0..3])) - continue; - - var sim_seen = std.ArrayList(u64).init(allocator); - defer sim_seen.deinit(); - - console.reset(); - //std.debug.print("Swapping instruction: {:<5}: {}\n", .{ inst, console.instlist[inst] }); - runTillRepeat(&console, &sim_seen, inst) catch {}; - if (console.jumpAddr == console.instlist.len or console.instructptr == console.instlist.len) { - std.debug.print("{}\n", .{console.accumulator}); - break; - } - } -} - -pub const main = aoc.gen_main(part1, part2); diff --git a/src/day9/main.zig b/src/day9/main.zig @@ -1,74 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); - -const preamble_size = 25; - -fn preambleHasSum(preamble: []u64, target: u64) bool { - for (preamble) |v1| { - for (preamble) |v2| { - if (v1 != v2 and v1 + v2 == target) - return true; - } - } - return false; -} - -fn getInvalid(allocator: *std.mem.Allocator, input: []u8) !u64 { - var preamble = std.ArrayList(u64).init(allocator); - defer preamble.deinit(); - - var i: u64 = 0; - var numit = std.mem.tokenize(input, "\n"); - while (numit.next()) |numstr| : (i += 1) { - const num = try std.fmt.parseInt(u64, numstr, 10); - if (i > preamble_size) { - if (!preambleHasSum(preamble.items, num)) { - return num; - } - preamble.items[(i - 1) % preamble_size] = num; - } else { - try preamble.append(num); - } - } - - return aoc.Error.InvalidInput; -} - -fn listSum(items: []u64) u64 { - var sum: u64 = 0; - for (items) |i| - sum += i; - return sum; -} - -fn part1(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - std.debug.print("{}\n", .{try getInvalid(allocator, input)}); -} - -fn part2(allocator: *std.mem.Allocator, input: []u8, args: [][]u8) !void { - const invalid = try getInvalid(allocator, input); - - var numset = std.ArrayList(u64).init(allocator); - defer numset.deinit(); - - var numit = std.mem.tokenize(input, "\n"); - while (true) { - const sum = listSum(numset.items); - if (sum < invalid) { - const numstr = numit.next(); - if (numstr == null) break; - const num = try std.fmt.parseInt(u64, numstr.?, 10); - try numset.append(num); - } else if (sum > invalid) { - _ = numset.orderedRemove(0); - } else { - break; - } - } - - if (numset.items.len > 0) { - std.debug.print("{}\n", .{std.mem.min(u64, numset.items) + std.mem.max(u64, numset.items)}); - } -} - -pub const main = aoc.gen_main(part1, part2);