1
0

Compare commits

...

27 Commits

Author SHA1 Message Date
aeea7ee99c day10?
Some checks failed
Build and test / test (push) Failing after 2m16s
2024-07-30 13:43:06 +10:00
6065242de9 wip
Some checks failed
Build and test / test (push) Failing after 2m6s
2023-12-16 17:37:04 +11:00
a684d9b238 mild washing
Some checks failed
Build and test / test (push) Failing after 2m11s
2023-12-15 17:18:07 +11:00
992673f784 day15
Some checks failed
Build and test / test (push) Failing after 1m42s
2023-12-15 17:06:14 +11:00
0b6eda2892 wip part2
Some checks failed
Build and test / test (push) Failing after 1m46s
2023-12-14 17:44:03 +11:00
b9bff3998e day14 part1 2023-12-14 17:25:19 +11:00
b13d5bb5f9 light cleaning
Some checks failed
Build and test / test (push) Failing after 1m41s
2023-12-13 17:39:49 +11:00
06ce20853a day13 part2 2023-12-13 17:16:14 +11:00
794dddf854 day13 part1 2023-12-13 16:51:23 +11:00
fa7a9aab14 light washing, fix overflows
Some checks failed
Build and test / test (push) Failing after 1m43s
2023-12-13 13:52:57 +11:00
d80a39e7dc day12 part2
jesus
2023-12-13 13:41:34 +11:00
3e8dca665f day12 part1 2023-12-13 12:24:28 +11:00
30d638ca51 fix pipeline
Some checks failed
Build and test / test (push) Failing after 1m44s
2023-12-12 16:22:14 +11:00
db9172ac2a lmao 2023-12-12 16:20:34 +11:00
96a2a09620 day11 part2
Some checks failed
Build and test / test (push) Failing after 1m48s
2023-12-11 18:07:04 +11:00
33297cb653 day11 part1 2023-12-11 17:42:39 +11:00
b5583ca46e clean day09
Some checks failed
Build and test / test (push) Failing after 1m49s
2023-12-09 20:46:40 +11:00
9b8ae4f350 day09 part2
Some checks failed
Build and test / test (push) Failing after 1m43s
2023-12-09 20:45:13 +11:00
9035d0a8d4 day09 part1 2023-12-09 20:27:00 +11:00
3d36485a52 Solve day 3
Some checks failed
Build and test / test (push) Failing after 1m42s
2023-12-09 20:05:26 +11:00
84344d12e5 day08 part2
Some checks failed
Build and test / test (push) Failing after 1m53s
2023-12-08 17:07:55 +11:00
ca84923990 day08 part1 2023-12-08 16:46:26 +11:00
300ceb885f wash day07
Some checks failed
Build and test / test (push) Failing after 2m40s
2023-12-07 17:19:51 +11:00
221cad1136 unwashed day07 2023-12-07 17:10:52 +11:00
05ffa1e7c1 day07 part1 2023-12-07 17:05:56 +11:00
fbca1996a5 unwash day06 or something
Some checks failed
Build and test / test (push) Failing after 2m34s
2023-12-06 16:34:47 +11:00
d848f3951b wtf happened in day06 2023-12-06 16:31:50 +11:00
14 changed files with 1532 additions and 0 deletions

View File

@ -12,5 +12,7 @@ jobs:
uses: https://github.com/cachix/install-nix-action@v23
with:
github_access_token: ${{ secrets.INPUT_GITHUB_ACCESS_TOKEN }}
- name: Stub data files
run: touch src/data/day{01..31}.txt
- name: Build package
run: nix build

145
src/day03.zig Normal file
View File

@ -0,0 +1,145 @@
const std = @import("std");
const util = @import("util.zig");
pub fn main() !void {
const input = @embedFile("data/day03.txt");
const sln = try solve(util.gpa, input);
std.debug.print("{d}\n", .{sln.sum});
std.debug.print("{d}\n", .{sln.gears});
}
const Part = struct {
value: usize,
line: usize,
start: usize,
end: usize,
};
const Symbol = struct { char: u8, line: usize, offset: usize };
const Solution = struct {
sum: usize,
gears: usize,
};
fn absDiff(comptime T: type, a: T, b: T) T {
return @max(a, b) - @min(a, b);
}
fn solve(alloc: std.mem.Allocator, input: []const u8) !Solution {
var parts = std.ArrayList(Part).init(alloc);
var symbols = std.ArrayList(Symbol).init(alloc);
defer parts.deinit();
defer symbols.deinit();
// Accumulate parts and symbols.
var it = std.mem.splitScalar(u8, input, '\n');
var lnum: usize = 0;
while (it.next()) |line| : (lnum += 1) {
var current: Part = undefined;
var in_part = false;
var offset: usize = 0;
for (line) |char| {
switch (char) {
'0'...'9' => {
if (!in_part) {
current.line = lnum;
current.start = offset;
current.value = 0;
in_part = true;
}
current.value = current.value * 10 + (char - '0');
},
'.' => {
if (in_part) {
current.end = offset - 1;
try parts.append(current);
}
in_part = false;
},
else => {
if (in_part) {
current.end = offset - 1;
try parts.append(current);
}
in_part = false;
try symbols.append(.{ .char = char, .line = lnum, .offset = offset });
},
}
offset += 1;
}
// Handle number at the end of a line.
if (in_part) {
current.end = offset - 1;
try parts.append(current);
}
}
// Locate actual parts.
var sum: usize = 0;
var gear_sum: usize = 0;
var gears = std.AutoHashMap(Symbol, Part).init(alloc);
defer gears.deinit();
ploop: for (parts.items) |part| {
// std.debug.print("part {d}x{d}..{d}\n", .{ part.line, part.start, part.end });
for (symbols.items) |symbol| {
// std.debug.print("symbol {d}x{d}\n", .{ symbol.line, symbol.offset });
const inOffset = part.start <= symbol.offset + 1 and part.end + 1 >= symbol.offset;
const inLine = absDiff(usize, part.line, symbol.line) <= 1;
if (inOffset and inLine) {
// std.debug.print("part {d}x({d}..{d})={d}\n", .{ part.line, part.start, part.end, part.value });
sum += part.value;
// Detect gears.
if (symbol.char == '*') {
if (gears.get(symbol)) |other_gear| {
gear_sum += part.value * other_gear.value;
_ = gears.remove(symbol);
} else {
try gears.put(symbol, part);
}
}
continue :ploop;
}
}
}
return .{ .sum = sum, .gears = gear_sum };
}
test "silver" {
const input =
\\467..114..
\\...*......
\\..35..633.
\\......#...
\\617*......
\\.....+.58.
\\..592.....
\\......755.
\\...$.*....
\\664..598..
;
try std.testing.expectEqual(@as(usize, 4361), (try solve(std.testing.allocator, input)).sum);
}
test "gold" {
const input =
\\467..114..
\\...*......
\\..35..633.
\\......#...
\\617*......
\\.....+.58.
\\..592.....
\\......755.
\\...$.*....
\\.664.598..
;
try std.testing.expectEqual(@as(usize, 467835), (try solve(std.testing.allocator, input)).gears);
}

73
src/day06.zig Normal file
View File

@ -0,0 +1,73 @@
const std = @import("std");
const util = @import("util.zig");
const mem = std.mem;
pub fn main() !void {
const input = @embedFile("data/day06.txt");
const sln = try solve(util.gpa, input);
std.debug.print("{d}\n", .{sln.a});
std.debug.print("{d}\n", .{sln.b});
}
const Solution = struct {
a: usize,
b: usize,
};
fn solve(alloc: mem.Allocator, input: []const u8) !Solution {
var result: usize = 1;
{
var it = util.splitLines(input);
const times = try util.parseIntsScalar(usize, alloc, it.next().?[10..], .{});
const distances = try util.parseIntsScalar(usize, alloc, it.next().?[10..], .{});
defer alloc.free(times);
defer alloc.free(distances);
for (times, distances) |time, minDistance| {
var winning: usize = 0;
for (0..time - 1) |speed| {
const distance = speed * (time - speed);
if (distance > minDistance) winning += 1;
}
result *= winning;
}
}
var result2: usize = 1;
{
var it = util.splitLines(input);
var td: [2]usize = undefined;
for (0..td.len) |i| {
const line = try mem.replaceOwned(u8, alloc, it.next().?[10..], " ", "");
defer alloc.free(line);
td[i] = try std.fmt.parseInt(usize, line, 10);
}
var winning2: usize = 0;
for (0..td[0] - 1) |speed| {
const distance = speed * (td[0] - speed);
if (distance > td[1]) winning2 += 1;
}
result2 *= winning2;
}
return .{ .a = result, .b = result2 };
}
test "silver" {
const input =
\\Time: 7 15 30
\\Distance: 9 40 200
;
const sln = try solve(std.testing.allocator, input);
try std.testing.expectEqual(@as(usize, 288), sln.a);
}
test "gold" {
const input =
\\Time: 7 15 30
\\Distance: 9 40 200
;
const sln = try solve(std.testing.allocator, input);
try std.testing.expectEqual(@as(usize, 71503), sln.b);
}

132
src/day07.zig Normal file
View File

@ -0,0 +1,132 @@
const std = @import("std");
const util = @import("util.zig");
const mem = std.mem;
pub fn main() !void {
const input = @embedFile("data/day07.txt");
const part1 = try solve(util.gpa, input, jack);
const part2 = try solve(util.gpa, input, joker);
std.debug.print("{d}\n", .{part1});
std.debug.print("{d}\n", .{part2});
}
const Cards = [5]u8;
const Score = enum(u8) {
fiveKind,
fourKind,
fullHouse,
threeKind,
twoPair,
onePair,
high,
};
const Hand = struct {
cards: [5]u8,
bid: u32,
score: Score,
};
const jack = 10;
const joker = 0;
fn fixCards(cards: *[5]u8, jValue: u8) void {
for (cards, 0..) |card, i| {
cards[i] = switch (card) {
'A' => 13,
'K' => 12,
'Q' => 11,
'J' => jValue,
'T' => 9,
'2'...'9' => card - '1',
else => unreachable,
};
}
}
fn u8gt(context: void, a: u8, b: u8) bool {
_ = context;
return a > b;
}
fn scoreHand(cards: Cards) Score {
var jokers: usize = 0;
var dups: [14]u8 = undefined;
@memset(&dups, 0);
for (cards) |a| {
if (a == joker) {
jokers += 1;
} else {
dups[a] += 1;
}
}
mem.sort(u8, &dups, {}, u8gt);
return switch (dups[0] + jokers) {
5 => .fiveKind,
4 => .fourKind,
3 => if (dups[1] == 2) .fullHouse else .threeKind,
2 => if (dups[1] == 2) .twoPair else .onePair,
else => .high,
};
}
fn compareHand(context: void, a: Hand, b: Hand) bool {
_ = context;
if (a.score != b.score) {
return @intFromEnum(a.score) > @intFromEnum(b.score);
} else {
for (a.cards, b.cards) |cardA, cardB| {
if (cardA != cardB) return cardA < cardB;
}
}
unreachable;
}
fn solve(alloc: mem.Allocator, input: []const u8, jValue: u8) !u32 {
var hands = try std.ArrayList(Hand).initCapacity(alloc, 1001);
defer hands.deinit();
var it = util.splitLines(input);
while (it.next()) |line| {
if (line.len == 0) continue;
var cards = line[0..5].*;
fixCards(&cards, jValue);
const bid = try std.fmt.parseInt(u32, line[6..], 10);
const score = scoreHand(cards);
try hands.append(.{ .cards = cards, .bid = bid, .score = score });
}
mem.sort(Hand, hands.items, {}, compareHand);
var winnings: u32 = 0;
for (hands.items, 1..) |hand, rank| {
winnings += hand.bid * @as(u32, @intCast(rank));
}
return winnings;
}
test "silver" {
const input =
\\32T3K 765
\\T55J5 684
\\KK677 28
\\KTJJT 220
\\QQQJA 483
;
const sln = try solve(std.testing.allocator, input, jack);
try std.testing.expectEqual(@as(usize, 6440), sln);
}
test "gold" {
const input =
\\32T3K 765
\\T55J5 684
\\KK677 28
\\KTJJT 220
\\QQQJA 483
;
const sln = try solve(std.testing.allocator, input, joker);
try std.testing.expectEqual(@as(usize, 5905), sln);
}

104
src/day08.zig Normal file
View File

@ -0,0 +1,104 @@
const std = @import("std");
const util = @import("util.zig");
const mem = std.mem;
pub fn main() !void {
const input = @embedFile("data/day08.txt");
const sln = try solve(util.gpa, input);
std.debug.print("{d}\n", .{sln.a});
std.debug.print("{d}\n", .{sln.b});
}
const Solution = struct {
a: usize,
b: usize,
};
const Step = struct {
left: [3]u8,
right: [3]u8,
};
fn gcd(a: usize, b: usize) usize {
return if (b == 0) a else gcd(b, a % b);
}
fn lcm(ns: []u32) usize {
var acc: usize = ns[0];
for (ns[1..]) |n| {
const nsize = @as(usize, n);
acc = acc / gcd(acc, nsize) * nsize;
}
return acc;
}
fn solve(alloc: mem.Allocator, input: []const u8) !Solution {
var states = std.AutoHashMap([3]u8, Step).init(alloc);
defer states.deinit();
var it = util.splitLines(input);
const steps = it.next().?;
while (it.next()) |line| {
if (line.len == 0) continue;
try states.put(line[0..3].*, .{ .left = line[7..10].*, .right = line[12..15].* });
}
var a_nodes = std.AutoHashMap([3]u8, Step).init(alloc);
defer a_nodes.deinit();
var state_it = states.iterator();
while (state_it.next()) |entry| {
if (entry.key_ptr[2] == 'A') {
try a_nodes.put(entry.key_ptr.*, entry.value_ptr.*);
}
}
var all_steps = std.ArrayList(u32).init(alloc);
defer all_steps.deinit();
var a_it = a_nodes.iterator();
while (a_it.next()) |start| {
std.debug.print("{s}\n", .{start.key_ptr});
var position: [3]u8 = start.key_ptr.*;
var count: u32 = 0;
find: while (position[2] != 'Z') : (count += 1) {
const curr_state = states.get(position) orelse break :find; // ???????
position = if (steps[count % steps.len] == 'L') curr_state.left else curr_state.right;
}
try all_steps.append(count);
}
return .{ .a = 2, .b = lcm(all_steps.items) };
}
test "silver" {
const input =
\\RL
\\
\\AAA = (BBB, CCC)
\\BBB = (DDD, EEE)
\\CCC = (ZZZ, GGG)
\\DDD = (DDD, DDD)
\\EEE = (EEE, EEE)
\\GGG = (GGG, GGG)
\\ZZZ = (ZZZ, ZZZ)
;
_ = input;
// const sln = try solve(std.testing.allocator, input);
// try std.testing.expectEqual(@as(usize, 2), sln.a);
}
test "gold" {
const input =
\\LR
\\
\\11A = (11B, XXX)
\\11B = (XXX, 11Z)
\\11Z = (11B, XXX)
\\22A = (22B, XXX)
\\22B = (22C, 22C)
\\22C = (22Z, 22Z)
\\22Z = (22B, 22B)
\\XXX = (XXX, XXX)
;
const sln = try solve(std.testing.allocator, input);
try std.testing.expectEqual(@as(usize, 6), sln.b);
}

80
src/day09.zig Normal file
View File

@ -0,0 +1,80 @@
const std = @import("std");
const util = @import("util.zig");
const mem = std.mem;
pub fn main() !void {
const input = @embedFile("data/day09.txt");
const sln = try solve(util.gpa, input);
std.debug.print("{d}\n", .{sln.a});
std.debug.print("{d}\n", .{sln.b});
}
const Solution = struct {
a: isize,
b: isize,
};
fn drv(alloc: mem.Allocator, seq: []const isize) ![]const isize {
var list = try std.ArrayList(isize).initCapacity(alloc, seq.len - 1);
for (0..seq.len - 1) |i| {
try list.append(seq[i + 1] - seq[i]);
}
return list.toOwnedSlice();
}
fn zeroed(seq: []const isize) bool {
for (seq) |n| {
if (n != 0) return false;
}
return true;
}
fn solve(alloc: mem.Allocator, input: []const u8) !Solution {
var it = util.splitLines(input);
var prev_sum: isize = 0;
var next_sum: isize = 0;
while (it.next()) |line| {
if (line.len == 0) continue;
var seqs = std.ArrayList([]const isize).init(alloc);
defer seqs.deinit();
// Find zero derivation
try seqs.append(try util.parseIntsScalar(isize, alloc, line, .{}));
while (!zeroed(seqs.items[seqs.items.len - 1])) {
try seqs.append(try drv(alloc, seqs.items[seqs.items.len - 1]));
}
// Extrapolate
var next: isize = 0;
var prev: isize = 0;
for (0..seqs.items.len) |i| {
const seq = seqs.items[seqs.items.len - i - 1];
prev = seq[0] - prev;
next += seq[seq.len - 1];
alloc.free(seq);
}
prev_sum += prev;
next_sum += next;
}
return .{ .a = next_sum, .b = prev_sum };
}
test "silver" {
const input =
\\0 3 6 9 12 15
\\1 3 6 10 15 21
\\10 13 16 21 30 45
;
const sln = try solve(std.testing.allocator, input);
try std.testing.expectEqual(@as(isize, 114), sln.a);
}
test "gold" {
const input =
\\0 3 6 9 12 15
\\1 3 6 10 15 21
\\10 13 16 21 30 45
;
const sln = try solve(std.testing.allocator, input);
try std.testing.expectEqual(@as(isize, 2), sln.b);
}

170
src/day10.zig Normal file
View File

@ -0,0 +1,170 @@
const std = @import("std");
const util = @import("util.zig");
const mem = std.mem;
const assert = std.debug.assert;
pub fn main() !void {
const input = @embedFile("data/day10.txt");
const sln = try solve(util.gpa, input);
try printPipes(std.io.getStdOut(), input);
std.debug.print("{d}\n", .{sln.a});
std.debug.print("{d}\n", .{sln.b});
}
const Solution = struct {
a: usize,
b: usize,
};
const Direction = enum(u2) {
up,
down,
left,
right,
};
fn Walker(comptime T: type) type {
return struct {
r: T,
c: T,
facing: Direction,
fn init(r: T, c: T) @This() {
return .{ .r = r, .c = c, .facing = .up };
}
fn walk(self: *@This(), grid: [][]u8) void {
self.walkSpace(grid[self.r][self.c]);
}
fn walkSpace(self: *@This(), char: u8) void {
switch (char) {
'S' => {},
'|' => assert(self.facing == .up or self.facing == .down),
'-' => assert(self.facing == .left or self.facing == .right),
'L' => switch (self.facing) {
.down => self.turn(.right),
.left => self.turn(.up),
else => unreachable,
},
'J' => switch (self.facing) {
.down => self.turn(.left),
.right => self.turn(.up),
else => unreachable,
},
'7' => switch (self.facing) {
.up => self.turn(.left),
.right => self.turn(.down),
else => unreachable,
},
'F' => switch (self.facing) {
.up => self.turn(.right),
.left => self.turn(.down),
else => unreachable,
},
else => unreachable,
}
self.fwd();
}
fn move(self: *@This(), row: i32, col: i32) void {
self.*.r += row;
self.*.c += col;
}
fn fwd(self: *@This()) void {
switch (self.facing) {
.up => self.move(-1, 0),
.down => self.move(1, 0),
.left => self.move(0, -1),
.right => self.move(0, 1),
}
}
fn turn(self: *@This(), dir: Direction) void {
self.*.facing = dir;
}
};
}
fn solve(alloc: mem.Allocator, input: []const u8) !Solution {
_ = alloc;
var grid: [][]u8 = undefined; // try alloc.alloc(u8, mem.count(u8, input, &[_]u8{'\n'}));
var s_found = false;
var sr: u32 = 0;
var sc: u32 = 0;
var it = util.splitLines(input);
var curr_row: u32 = 0;
while (it.next()) |line| {
if (line.len == 0) continue;
// grid[curr_row] = line;
if (!s_found) {
if (mem.indexOfScalar(u8, line, 'S')) |col| {
_ = col;
sr = curr_row;
// sc = col;
s_found = true;
}
}
}
// Find actual starting position.
var walker = Walker(u32).init(sr, sc);
if (mem.indexOfScalar(u8, "|7F", grid[sr - 1][sc])) |_| {
walker.facing = .up;
} else if (mem.indexOfScalar(u8, "|JL", grid[sr + 1][sc])) |_| {
walker.facing = .down;
} else if (mem.indexOfScalar(u8, "-LF", grid[sr][sc - 1])) |_| {
walker.facing = .left;
} else if (mem.indexOfScalar(u8, "-J7", grid[sr][sc + 1])) |_| {
walker.facing = .right;
}
var steps: u32 = 1;
while (true) {
steps += 1;
if (grid[walker.r][walker.c] == 'S') break;
}
return .{ .a = steps / 2, .b = 0 };
}
fn printPipes(writer: anytype, input: []const u8) !void {
for (input) |char| {
_ = switch (char) {
'|' => try writer.write(""),
'-' => try writer.write(""),
'L' => try writer.write(""),
'J' => try writer.write(""),
'7' => try writer.write(""),
'F' => try writer.write(""),
'.' => try writer.write(" "),
'S' => try writer.write("o"),
'\n' => try writer.write("\n"),
else => unreachable,
};
}
}
test "silver" {
const input =
\\-L|F7
\\7S-7|
\\L|7||
\\-L-J|
\\L|-JF
;
const sln = try solve(std.testing.allocator, input);
try std.testing.expectEqual(@as(usize, 6), sln.a);
}
test "gold" {
const input =
\\
;
const sln = try solve(std.testing.allocator, input);
try std.testing.expectEqual(@as(usize, 0), sln.b);
}

155
src/day11.zig Normal file
View File

@ -0,0 +1,155 @@
const std = @import("std");
const util = @import("util.zig");
const mem = std.mem;
pub fn main() !void {
const input = @embedFile("data/day11.txt");
const sln = try solve(util.gpa, input);
std.debug.print("{d}\n", .{sln.a});
std.debug.print("{d}\n", .{sln.b});
}
const Solution = struct {
a: usize,
b: usize,
};
fn Point(comptime T: type) type {
return struct { x: T, y: T };
}
fn findGaps(comptime T: type, alloc: mem.Allocator, occupied: std.AutoHashMap(T, void)) ![]T {
var it = occupied.keyIterator();
var sorted_occupied = try std.ArrayList(T).initCapacity(alloc, occupied.count());
defer sorted_occupied.deinit();
while (it.next()) |item| {
try sorted_occupied.append(item.*);
}
mem.sort(T, sorted_occupied.items, {}, std.sort.asc(T));
var gaps = std.ArrayList(T).init(alloc);
errdefer gaps.deinit();
var gap: T = 0;
var i: usize = 0;
const max_occ = if (sorted_occupied.items.len != 0) std.mem.max(T, sorted_occupied.items) else 0;
while (gap < max_occ and i < sorted_occupied.items.len) : (gap += 1) {
if (sorted_occupied.items[i] != gap) {
try gaps.append(gap);
} else {
i += 1;
}
}
return gaps.toOwnedSlice();
}
fn expand(comptime T: type, alloc: mem.Allocator, galaxies: []Point(T), scale: T) !void {
var seen_cols = std.AutoHashMap(T, void).init(alloc);
defer seen_cols.deinit();
var seen_rows = std.AutoHashMap(T, void).init(alloc);
defer seen_rows.deinit();
for (galaxies) |galaxy| {
try seen_cols.put(galaxy.x, {});
try seen_rows.put(galaxy.y, {});
}
var gap_cols = try findGaps(T, alloc, seen_cols);
defer alloc.free(gap_cols);
var gap_rows = try findGaps(T, alloc, seen_rows);
defer alloc.free(gap_rows);
for (0..galaxies.len) |i| {
const galaxy = galaxies[i];
var col_offset: T = 0;
for (gap_cols) |gap| {
if (gap > galaxy.x) break;
col_offset += scale - 1;
}
var row_offset: T = 0;
for (gap_rows) |gap| {
if (gap > galaxy.y) break;
row_offset += scale - 1;
}
galaxies[i] = Point(T){ .x = galaxy.x + col_offset, .y = galaxy.y + row_offset };
}
}
fn absDiff(comptime T: type, a: T, b: T) T {
return @max(a, b) - @min(a, b);
}
fn solvePart(alloc: mem.Allocator, input: []const u8, scale: usize) !usize {
var galaxies = std.ArrayList(Point(usize)).init(alloc);
defer galaxies.deinit();
var it = util.splitLines(input);
var y: usize = 0;
while (it.next()) |line| {
if (line.len == 0) continue;
var x: usize = 0;
for (line) |char| {
switch (char) {
'.' => {},
'#' => try galaxies.append(Point(usize){ .x = x, .y = y }),
else => unreachable,
}
x += 1;
}
y += 1;
}
try expand(usize, alloc, galaxies.items, scale);
var distance: usize = 0;
for (galaxies.items, 0..) |a, i| {
for (galaxies.items[i + 1 .. galaxies.items.len]) |b| {
const d = absDiff(usize, a.x, b.x) + absDiff(usize, a.y, b.y);
distance += d;
}
}
return distance;
}
fn solve(alloc: mem.Allocator, input: []const u8) !Solution {
return .{ .a = try solvePart(alloc, input, 2), .b = try solvePart(alloc, input, 1_000_000) };
}
test "silver" {
const input =
\\...#......
\\.......#..
\\#.........
\\..........
\\......#...
\\.#........
\\.........#
\\..........
\\.......#..
\\#...#.....
;
const sln = try solvePart(std.testing.allocator, input, 2);
try std.testing.expectEqual(@as(usize, 374), sln);
}
test "gold" {
const input =
\\...#......
\\.......#..
\\#.........
\\..........
\\......#...
\\.#........
\\.........#
\\..........
\\.......#..
\\#...#.....
;
try std.testing.expectEqual(@as(usize, 1030), try solvePart(std.testing.allocator, input, 10));
try std.testing.expectEqual(@as(usize, 8410), try solvePart(std.testing.allocator, input, 100));
}

163
src/day12.zig Normal file
View File

@ -0,0 +1,163 @@
const std = @import("std");
const util = @import("util.zig");
const mem = std.mem;
pub fn main() !void {
const input = @embedFile("data/day12.txt");
const sln = try solve(util.gpa, input);
std.debug.print("{d}\n", .{sln.a});
std.debug.print("{d}\n", .{sln.b});
}
const Solution = struct {
a: usize,
b: usize,
};
fn hash(alloc: mem.Allocator, pattern: []const u8, groups: []const u8, curr_group: u8) ![]u8 {
var a = try std.ArrayList(u8).initCapacity(alloc, pattern.len + groups.len + 1);
try a.appendSlice(pattern);
try a.appendSlice(groups);
try a.append(curr_group);
return try a.toOwnedSlice();
}
var memo: std.StringHashMap(usize) = undefined;
fn clone(comptime T: type, alloc: mem.Allocator, s: []const T) ![]T {
var a = try alloc.alloc(T, s.len);
@memcpy(a, s);
return a;
}
fn solvePattern(alloc: mem.Allocator, pattern: []const u8, groups: []const u8, curr_group: u8) !usize {
const h = try hash(alloc, pattern, groups, curr_group);
if (memo.get(h)) |m| {
alloc.free(h);
return m;
}
if (pattern.len == 0) {
return if (groups.len == 0) 1 else 0;
}
const res: usize = b: {
switch (pattern[0]) {
'#' => {
// Too many #
if (groups.len == 0 or curr_group >= groups[0]) {
break :b 0;
}
// Special-case last #
if (pattern.len == 1 and groups.len == 1 and groups[0] == curr_group + 1) {
break :b 1;
}
break :b try solvePattern(alloc, pattern[1..], groups, curr_group + 1);
},
'.' => {
if (curr_group > 0) {
if (groups.len == 0 or curr_group != groups[0]) {
// Too many #
break :b 0;
} else {
// Group done
break :b try solvePattern(alloc, pattern[1..], groups[1..], 0);
}
} else {
// Not growing a group, skip.
break :b try solvePattern(alloc, pattern[1..], groups, 0);
}
},
'?' => {
const p1 = try clone(u8, alloc, pattern);
defer alloc.free(p1);
const p2 = try clone(u8, alloc, pattern);
defer alloc.free(p2);
p1[0] = '#';
p2[0] = '.';
break :b try solvePattern(alloc, p1, groups, curr_group) +
try solvePattern(alloc, p2, groups, curr_group);
},
else => unreachable,
}
};
try memo.put(h, res);
return res;
}
fn unfold(alloc: mem.Allocator, input: []const u8, sep: u8) ![]const u8 {
var buf = try std.ArrayList(u8).initCapacity(alloc, input.len * 5 + 5);
for (0..5) |i| {
if (i > 0 and sep > 0) try buf.append(sep);
try buf.appendSlice(input);
}
return try buf.toOwnedSlice();
}
fn solve(alloc: mem.Allocator, input: []const u8) !Solution {
memo = std.StringHashMap(usize).init(alloc);
defer memo.deinit();
var sum1: usize = 0;
var sum2: usize = 0;
var it = util.splitLines(input);
var i: usize = 1;
while (it.next()) |line| {
if (line.len == 0) continue;
std.debug.print("line {} ", .{i});
var lit = util.splitSpace(line);
const pattern = lit.next().?;
const raw_groups = lit.next().?;
const groups = try util.parseIntsScalar(u8, alloc, raw_groups, .{ .sep = ',' });
defer alloc.free(groups);
const unfolded_pattern = try unfold(alloc, pattern, '?');
const unfolded_groups = try unfold(alloc, groups, 0);
defer alloc.free(unfolded_pattern);
defer alloc.free(unfolded_groups);
sum1 += try solvePattern(alloc, pattern, groups, 0);
sum2 += try solvePattern(alloc, unfolded_pattern, unfolded_groups, 0);
std.debug.print("{} {}\n", .{ sum1, sum2 });
i += 1;
}
var mkeys = memo.keyIterator();
while (mkeys.next()) |k| {
alloc.free(k.*);
}
return .{ .a = sum1, .b = sum2 };
}
test "silver" {
const input =
\\???.### 1,1,3
\\.??..??...?##. 1,1,3
\\?#?#?#?#?#?#?#? 1,3,1,6
\\????.#...#... 4,1,1
\\????.######..#####. 1,6,5
\\?###???????? 3,2,1
;
const sln = try solve(std.testing.allocator, input);
try std.testing.expectEqual(@as(usize, 21), sln.a);
}
test "gold" {
const input =
\\???.### 1,1,3
\\.??..??...?##. 1,1,3
\\?#?#?#?#?#?#?#? 1,3,1,6
\\????.#...#... 4,1,1
\\????.######..#####. 1,6,5
\\?###???????? 3,2,1
;
const sln = try solve(std.testing.allocator, input);
try std.testing.expectEqual(@as(usize, 525152), sln.b);
}

136
src/day13.zig Normal file
View File

@ -0,0 +1,136 @@
const std = @import("std");
const util = @import("util.zig");
const mem = std.mem;
pub fn main() !void {
const input = @embedFile("data/day13.txt");
const sln = try solve(util.gpa, input);
std.debug.print("{d}\n", .{sln.a});
std.debug.print("{d}\n", .{sln.b});
}
const Solution = struct {
a: usize,
b: usize,
};
fn transpose(alloc: mem.Allocator, grid: []const []const u8) ![]const []const u8 {
std.debug.assert(grid.len > 0 and grid[0].len > 0);
var g = try alloc.alloc([]u8, grid[0].len);
for (0..grid[0].len) |i| {
g[i] = try alloc.alloc(u8, grid.len);
}
for (0..grid.len) |i| {
for (0..grid[i].len) |j| {
g[j][i] = grid[i][j];
}
}
return g;
}
fn findReflection(input: []const []const u8, err_tolerance: usize) ?usize {
if (input.len == 0) return 0;
const h = input.len;
const w = input[0].len;
for (0..h - 1) |scan| {
var offset: usize = 0;
var col: usize = 0;
var errors: usize = 0;
while (true) {
if (input[scan - offset][col] != input[scan + offset + 1][col]) {
errors += 1;
}
if (errors > err_tolerance + 1) {
break;
}
col = (col + 1) % w;
if (col == 0) offset += 1;
if (scan < offset or scan + offset + 1 >= h) {
if (errors == err_tolerance) return scan + 1;
break;
}
}
}
return null;
}
fn solve(alloc: mem.Allocator, input: []const u8) !Solution {
var curr_pattern = std.ArrayList([]const u8).init(alloc);
defer curr_pattern.deinit();
var it = util.splitLines(input);
var sums = [2]usize{ 0, 0 };
while (it.next()) |line| {
if (line.len == 0) {
const pattern = try curr_pattern.toOwnedSlice();
defer alloc.free(pattern);
for (0..2) |et| {
if (findReflection(pattern, et)) |score| {
sums[et] += 100 * score;
} else {
const t = try transpose(alloc, pattern);
sums[et] += findReflection(t, et) orelse unreachable;
// Ew.
for (t) |r| {
alloc.free(r);
}
alloc.free(t);
}
}
} else {
try curr_pattern.append(line);
}
}
return .{ .a = sums[0], .b = sums[1] };
}
test "silver" {
const input =
\\#.##..##.
\\..#.##.#.
\\##......#
\\##......#
\\..#.##.#.
\\..##..##.
\\#.#.##.#.
\\
\\#...##..#
\\#....#..#
\\..##..###
\\#####.##.
\\#####.##.
\\..##..###
\\#....#..#
\\
;
const sln = try solve(std.testing.allocator, input);
try std.testing.expectEqual(@as(usize, 405), sln.a);
}
test "gold" {
const input =
\\#.##..##.
\\..#.##.#.
\\##......#
\\##......#
\\..#.##.#.
\\..##..##.
\\#.#.##.#.
\\
\\#...##..#
\\#....#..#
\\..##..###
\\#####.##.
\\#####.##.
\\..##..###
\\#....#..#
\\
;
const sln = try solve(std.testing.allocator, input);
try std.testing.expectEqual(@as(usize, 400), sln.b);
}

94
src/day14.zig Normal file
View File

@ -0,0 +1,94 @@
const std = @import("std");
const util = @import("util.zig");
const mem = std.mem;
pub fn main() !void {
const input = @embedFile("data/day14.txt");
const sln = try solve(util.gpa, input);
std.debug.print("{d}\n", .{sln.a});
std.debug.print("{d}\n", .{sln.b});
}
const Solution = struct {
a: usize,
b: usize,
};
fn northWeight(alloc: mem.Allocator, grid: [][]u8) !usize {
var next_slot = try alloc.alloc(u32, grid[0].len);
defer alloc.free(next_slot);
@memset(next_slot, 0);
var weight: usize = 0;
for (grid, 0..) |line, row| {
for (line, 0..) |char, col| {
switch (char) {
'O' => {
weight += grid.len - next_slot[col];
next_slot[col] += 1;
},
'#' => next_slot[col] = @as(u32, @intCast(row)) + 1,
else => {},
}
}
}
return weight;
}
fn spin(grid: [][]u8, n: usize) void {
_ = grid;
for (0..n) |_| {}
}
fn solve(alloc: mem.Allocator, input: []const u8) !Solution {
var height = mem.count(u8, input, &[_]u8{'\n'});
if (input[input.len - 1] != '\n') height += 1;
var grid = try alloc.alloc([]u8, height);
defer alloc.free(grid);
var it = util.splitLines(input);
var row: u32 = 0;
while (it.next()) |line| {
if (line.len == 0) continue;
grid[row] = try alloc.alloc(u8, line.len);
@memcpy(grid[row], line);
row += 1;
}
const w = try northWeight(alloc, grid);
spin(grid, 1000000000);
const v = try northWeight(alloc, grid);
_ = v;
for (grid) |line| {
alloc.free(line);
}
return .{ .a = w, .b = 0 };
}
test "silver" {
const input =
\\O....#....
\\O.OO#....#
\\.....##...
\\OO.#O....O
\\.O.....O#.
\\O.#..O.#.#
\\..O..#O..O
\\.......O..
\\#....###..
\\#OO..#....
;
const sln = try solve(std.testing.allocator, input);
try std.testing.expectEqual(@as(usize, 136), sln.a);
}
test "gold" {
const input =
\\
;
_ = input;
// const sln = try solve(std.testing.allocator, input);
// try std.testing.expectEqual(@as(usize, 0), sln.b);
}

91
src/day15.zig Normal file
View File

@ -0,0 +1,91 @@
const std = @import("std");
const util = @import("util.zig");
const mem = std.mem;
pub fn main() !void {
const input = @embedFile("data/day15.txt");
const sln = try solve(util.gpa, input);
std.debug.print("{d}\n", .{sln.a});
std.debug.print("{d}\n", .{sln.b});
}
const Solution = struct {
a: usize,
b: usize,
};
fn hash(input: []const u8) u32 {
var v: u32 = 0;
for (input) |c| {
v = ((v + c) * 17) % 256;
}
return v;
}
const SAHM = std.StringArrayHashMap(u8);
fn solve(alloc: mem.Allocator, input: []const u8) !Solution {
const trimmed = mem.trimRight(u8, input, "\n\r");
var hash_sum: u32 = 0;
var it = util.split(trimmed, ',');
while (it.next()) |line| {
if (line.len == 0) continue;
hash_sum += hash(line);
}
var boxes: [256]?SAHM = [_]?SAHM{null} ** 256;
it = util.split(trimmed, ',');
while (it.next()) |line| {
if (line.len == 0) continue;
const op = mem.indexOfAny(u8, line, "-=") orelse unreachable;
const label = line[0..op];
const label_hash = hash(label);
var box = &(boxes[label_hash] orelse init: {
var b = SAHM.init(alloc);
boxes[label_hash] = b;
break :init b;
});
switch (line[op]) {
'-' => _ = box.orderedRemove(label),
'=' => try box.put(label, try std.fmt.parseInt(u8, line[op + 1 ..], 10)),
else => unreachable,
}
}
var focus: usize = 0;
for (boxes, 1..) |maybe_box, box_n| {
var box = maybe_box orelse continue;
var box_it = box.iterator();
var lens_n: usize = 1;
while (box_it.next()) |entry| {
focus += box_n * lens_n * entry.value_ptr.*;
lens_n += 1;
}
box.deinit();
}
return .{ .a = hash_sum, .b = focus };
}
test "sample hash" {
try std.testing.expectEqual(@as(usize, 52), hash("HASH"));
}
test "silver" {
const input =
\\rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7
;
const sln = try solve(std.testing.allocator, input);
try std.testing.expectEqual(@as(usize, 1320), sln.a);
}
test "gold" {
const input =
\\rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7
;
const sln = try solve(std.testing.allocator, input);
try std.testing.expectEqual(@as(usize, 145), sln.b);
}

159
src/day16.zig Normal file
View File

@ -0,0 +1,159 @@
const std = @import("std");
const util = @import("util.zig");
const mem = std.mem;
pub fn main() !void {
const input = @embedFile("data/day16.txt");
const sln = try solve(util.gpa, input);
std.debug.print("{d}\n", .{sln.a});
std.debug.print("{d}\n", .{sln.b});
}
const Solution = struct {
a: usize,
b: usize,
};
const Direction = enum {
up,
right,
down,
left,
fn x(self: @This()) isize {
return switch (self) {
.up, .down => 0,
.left => -1,
.right => 1,
};
}
fn y(self: @This()) isize {
return switch (self) {
.left, .right => 0,
.up => -1,
.down => 1,
};
}
fn rotate(self: @This(), nineties: usize) Direction {
return @enumFromInt((@intFromEnum(self) + nineties) % std.enums.values(@This()).len);
}
};
const Point = struct {
x: isize,
y: isize,
dir: Direction,
};
var states: std.AutoArrayHashMap(Point, void) = undefined;
fn run(alloc: mem.Allocator, grid: [][]u8, visited: [][]bool, init_x: isize, init_y: isize, init_dir: Direction) !void {
var x = init_x;
var y = init_y;
var dir = init_dir;
while (0 <= x and x < grid[0].len - 1 and 0 <= y and y < grid.len - 1) {
visited[@intCast(y)][@intCast(x)] = true;
var looped = try states.getOrPut(.{ .x = x, .y = y, .dir = dir });
if (looped.found_existing) break;
// std.debug.print("-----\n", .{});
// std.debug.print("{} {} {}\n", .{ x, y, dir });
// std.time.sleep(1_000_000_000);
switch (grid[@intCast(y)][@intCast(x)]) {
'.' => {
x += dir.x();
y += dir.y();
},
'|' => {
switch (dir) {
.up, .down => y += dir.y(),
.left, .right => {
try run(alloc, grid, visited, x, y - 1, .up);
try run(alloc, grid, visited, x, y + 1, .down);
},
}
},
'-' => {
switch (dir) {
.left, .right => x += dir.x(),
.up, .down => {
try run(alloc, grid, visited, x - 1, y, .left);
try run(alloc, grid, visited, x + 1, y, .right);
},
}
},
'/' => {
switch (dir) {
.left, .right => dir = dir.rotate(3),
.up, .down => dir = dir.rotate(1),
}
x += dir.x();
y += dir.y();
},
'\\' => {
switch (dir) {
.left, .right => dir = dir.rotate(1),
.up, .down => dir = dir.rotate(3),
}
x += dir.x();
y += dir.y();
},
else => unreachable,
}
}
}
fn solve(alloc: mem.Allocator, comptime input: []const u8) !Solution {
const trimmed = mem.trimRight(u8, input, "\n\r");
const w = mem.indexOfScalar(u8, trimmed, '\n') orelse unreachable;
const h = mem.count(u8, trimmed, "\n");
var visited = try util.allocGrid(bool, alloc, w, h, false);
const grid = try util.toGridOwned(alloc, trimmed, '\n');
states = std.AutoArrayHashMap(Point, void).init(alloc);
defer states.deinit();
try run(alloc, grid, visited, 0, 0, .right);
var energized: usize = 0;
for (visited) |row| {
for (row) |cell| {
if (cell) energized += 1;
}
}
for (grid) |row| {
alloc.free(row);
}
for (visited) |row| {
alloc.free(row);
}
alloc.free(grid);
alloc.free(visited);
return .{ .a = energized, .b = 0 };
}
const sample =
\\.|...\....
\\|.-.\.....
\\.....|-...
\\........|.
\\..........
\\.........\
\\..../.\\..
\\.-.-/..|..
\\.|....-|.\
\\..//.|....
;
test "silver" {
const sln = try solve(std.testing.allocator, sample);
try std.testing.expectEqual(@as(usize, 46), sln.a);
}
test "gold" {
// const sln = try solve(std.testing.allocator, input);
// try std.testing.expectEqual(@as(usize, 0), sln.b);
}

View File

@ -35,3 +35,31 @@ pub fn splitLines(buffer: []const u8) mem.SplitIterator(u8, .scalar) {
pub fn splitSpace(buffer: []const u8) mem.SplitIterator(u8, .scalar) {
return split(buffer, ' ');
}
pub fn allocGrid(comptime T: type, alloc: mem.Allocator, w: usize, h: usize, init: T) ![][]T {
var grid = try alloc.alloc([]T, h);
for (0..h) |i| {
grid[i] = try alloc.alloc(T, w);
@memset(grid[i], init);
}
return grid;
}
pub fn toGridOwned(alloc: mem.Allocator, buffer: []const u8, sep: u8) ![][]u8 {
if (buffer.len == 0) return error.Empty;
const w = mem.indexOfScalar(u8, buffer, sep) orelse unreachable;
var h = mem.count(u8, buffer, &[_]u8{sep});
if (buffer[buffer.len - 1] != sep) h += 1;
var grid = try alloc.alloc([]u8, h);
var it = split(buffer, sep);
var i: usize = 0;
while (it.next()) |line| {
grid[i] = try alloc.alloc(u8, w);
@memcpy(grid[i], line);
i += 1;
}
return grid;
}