Compare commits
37 Commits
87d3e72276
...
main
Author | SHA1 | Date | |
---|---|---|---|
aeea7ee99c
|
|||
6065242de9
|
|||
a684d9b238
|
|||
992673f784
|
|||
0b6eda2892
|
|||
b9bff3998e
|
|||
b13d5bb5f9
|
|||
06ce20853a
|
|||
794dddf854
|
|||
fa7a9aab14
|
|||
d80a39e7dc
|
|||
3e8dca665f
|
|||
30d638ca51
|
|||
db9172ac2a
|
|||
96a2a09620
|
|||
33297cb653
|
|||
b5583ca46e
|
|||
9b8ae4f350
|
|||
9035d0a8d4
|
|||
3d36485a52
|
|||
84344d12e5
|
|||
ca84923990
|
|||
300ceb885f
|
|||
221cad1136
|
|||
05ffa1e7c1
|
|||
fbca1996a5
|
|||
d848f3951b
|
|||
0ab1fa186d
|
|||
0f2044b762
|
|||
faf81869d6
|
|||
d85d4f0d26
|
|||
052bf70095
|
|||
9c64d710a3
|
|||
e112876796
|
|||
362b2c2cef
|
|||
44ccf7b6e2
|
|||
d5ee0f6aef
|
@ -12,5 +12,7 @@ jobs:
|
|||||||
uses: https://github.com/cachix/install-nix-action@v23
|
uses: https://github.com/cachix/install-nix-action@v23
|
||||||
with:
|
with:
|
||||||
github_access_token: ${{ secrets.INPUT_GITHUB_ACCESS_TOKEN }}
|
github_access_token: ${{ secrets.INPUT_GITHUB_ACCESS_TOKEN }}
|
||||||
|
- name: Stub data files
|
||||||
|
run: touch src/data/day{01..31}.txt
|
||||||
- name: Build package
|
- name: Build package
|
||||||
run: nix build
|
run: nix build
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const util = @import("util.zig");
|
const util = @import("util.zig");
|
||||||
|
|
||||||
|
const numbers = [_][]const u8{ "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" };
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
const input = @embedFile("data/day01.txt");
|
const input = @embedFile("data/day01.txt");
|
||||||
const sln = try solve(input);
|
std.debug.print("{d}\n", .{try solve(input, &[_][]u8{})});
|
||||||
std.debug.print("Solution: {d}", .{sln});
|
std.debug.print("{d}\n", .{try solve(input, &numbers)});
|
||||||
}
|
}
|
||||||
|
|
||||||
const numbers = [_][]const u8{ "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" };
|
|
||||||
const zero = @as(u8, '0');
|
const zero = @as(u8, '0');
|
||||||
|
|
||||||
fn solve(input: []const u8) !u32 {
|
fn solve(input: []const u8, words: []const []const u8) !u32 {
|
||||||
var sum: u32 = 0;
|
var sum: u32 = 0;
|
||||||
|
|
||||||
var it = std.mem.splitAny(u8, input, "\n");
|
var it = std.mem.splitAny(u8, input, "\n");
|
||||||
@ -27,9 +28,9 @@ fn solve(input: []const u8) !u32 {
|
|||||||
if (line[offset] >= '0' and line[offset] <= '9') {
|
if (line[offset] >= '0' and line[offset] <= '9') {
|
||||||
n = line[offset];
|
n = line[offset];
|
||||||
} else {
|
} else {
|
||||||
inline for (numbers, 0..) |word, value| {
|
for (words, 0..) |word, value| {
|
||||||
if (std.mem.startsWith(u8, line[offset..], word)) {
|
if (std.mem.startsWith(u8, line[offset..], word)) {
|
||||||
n = value + zero;
|
n = @as(u8, @intCast(value)) + zero;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -49,7 +50,17 @@ fn solve(input: []const u8) !u32 {
|
|||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
test "sample" {
|
test "silver" {
|
||||||
|
const input =
|
||||||
|
\\1abc2
|
||||||
|
\\pqr3stu8vwx
|
||||||
|
\\a1b2c3d4e5f
|
||||||
|
\\treb7uchet
|
||||||
|
;
|
||||||
|
try std.testing.expectEqual(@as(u32, 142), try solve(input, &[_][]u8{}));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "gold" {
|
||||||
const input =
|
const input =
|
||||||
\\two1nine
|
\\two1nine
|
||||||
\\eightwothree
|
\\eightwothree
|
||||||
@ -59,6 +70,5 @@ test "sample" {
|
|||||||
\\zoneight234
|
\\zoneight234
|
||||||
\\7pqrstsixteen
|
\\7pqrstsixteen
|
||||||
;
|
;
|
||||||
const sln = try solve(input);
|
try std.testing.expectEqual(@as(u32, 281), try solve(input, &numbers));
|
||||||
try std.testing.expectEqual(@as(u32, 281), sln);
|
|
||||||
}
|
}
|
||||||
|
94
src/day02.zig
Normal file
94
src/day02.zig
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
const input = @embedFile("data/day02.txt");
|
||||||
|
const target = Game{ .red = 12, .green = 13, .blue = 14 };
|
||||||
|
const sln = try solve(input, target);
|
||||||
|
|
||||||
|
std.debug.print("{d}\n", .{sln.possible});
|
||||||
|
std.debug.print("{d}\n", .{sln.powers});
|
||||||
|
}
|
||||||
|
|
||||||
|
const Day02Error = error{
|
||||||
|
MissingColon,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Game = struct {
|
||||||
|
red: u32 = 0,
|
||||||
|
blue: u32 = 0,
|
||||||
|
green: u32 = 0,
|
||||||
|
|
||||||
|
fn parse(line: []const u8) !@This() {
|
||||||
|
var self = @This(){};
|
||||||
|
var n: u32 = 0;
|
||||||
|
|
||||||
|
const colon = std.mem.indexOfScalar(u8, line, ':') orelse return Day02Error.MissingColon;
|
||||||
|
var it = std.mem.splitAny(u8, line[colon + 1 ..], ",; ");
|
||||||
|
while (it.next()) |item| {
|
||||||
|
if (item.len == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (item[0]) {
|
||||||
|
'r' => self.red = @max(self.red, n),
|
||||||
|
'g' => self.green = @max(self.green, n),
|
||||||
|
'b' => self.blue = @max(self.blue, n),
|
||||||
|
'0'...'9' => n = try std.fmt.parseInt(u8, item, 10),
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn possible(self: @This(), other: @This()) bool {
|
||||||
|
return self.red <= other.red and self.green <= other.green and self.blue <= other.blue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Solution = struct {
|
||||||
|
possible: u32,
|
||||||
|
powers: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn solve(input: []const u8, target: Game) !Solution {
|
||||||
|
var possible: u32 = 0;
|
||||||
|
var powers: u32 = 0;
|
||||||
|
|
||||||
|
var index: u32 = 1;
|
||||||
|
var line_it = std.mem.splitScalar(u8, input, '\n');
|
||||||
|
while (line_it.next()) |line| : (index += 1) {
|
||||||
|
if (line.len == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const game = try Game.parse(line);
|
||||||
|
if (game.possible(target)) {
|
||||||
|
possible += index;
|
||||||
|
}
|
||||||
|
powers += game.red * game.blue * game.green;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Solution{ .possible = possible, .powers = powers };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn solveSample() !Solution {
|
||||||
|
const input =
|
||||||
|
\\Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
|
||||||
|
\\Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
|
||||||
|
\\Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
|
||||||
|
\\Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
|
||||||
|
\\Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green
|
||||||
|
;
|
||||||
|
return solve(input, .{ .red = 12, .blue = 14, .green = 13 });
|
||||||
|
}
|
||||||
|
|
||||||
|
test "silver" {
|
||||||
|
const sln = try solveSample();
|
||||||
|
try std.testing.expectEqual(@as(u32, 8), sln.possible);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "gold" {
|
||||||
|
const sln = try solveSample();
|
||||||
|
try std.testing.expectEqual(@as(u32, 2286), sln.powers);
|
||||||
|
}
|
145
src/day03.zig
Normal file
145
src/day03.zig
Normal 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);
|
||||||
|
}
|
99
src/day04.zig
Normal file
99
src/day04.zig
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const util = @import("util.zig");
|
||||||
|
const mem = std.mem;
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
const input = @embedFile("data/day04.txt");
|
||||||
|
const sln = try solve(util.gpa, input);
|
||||||
|
std.debug.print("{d}\n", .{sln.value});
|
||||||
|
std.debug.print("{d}\n", .{sln.copies});
|
||||||
|
}
|
||||||
|
|
||||||
|
const Solution = struct {
|
||||||
|
value: usize,
|
||||||
|
copies: usize,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn solve(alloc: mem.Allocator, input: []const u8) !Solution {
|
||||||
|
var value: usize = 0;
|
||||||
|
var card_counts = std.AutoHashMap(usize, usize).init(alloc);
|
||||||
|
defer card_counts.deinit();
|
||||||
|
|
||||||
|
var card: usize = 0;
|
||||||
|
var it = util.splitLines(input);
|
||||||
|
while (it.next()) |line| : (card += 1) {
|
||||||
|
if (line.len == 0) continue;
|
||||||
|
|
||||||
|
const sep = mem.indexOfScalar(u8, line, ':').?;
|
||||||
|
var line_it = util.split(line[sep + 1 ..], '|');
|
||||||
|
const winning = try util.parseIntsScalar(usize, alloc, line_it.next().?, .{});
|
||||||
|
defer alloc.free(winning);
|
||||||
|
const mine = try util.parseIntsScalar(usize, alloc, line_it.next().?, .{});
|
||||||
|
defer alloc.free(mine);
|
||||||
|
|
||||||
|
var matches: usize = 0;
|
||||||
|
for (mine) |n| {
|
||||||
|
if (mem.containsAtLeast(usize, winning, 1, &[_]usize{n})) {
|
||||||
|
matches += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matches > 0) {
|
||||||
|
value += std.math.pow(usize, 2, matches - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialise with 1 copy of current card
|
||||||
|
const copies = blk: {
|
||||||
|
const entry = try card_counts.getOrPut(card);
|
||||||
|
if (entry.found_existing) {
|
||||||
|
entry.value_ptr.* += 1;
|
||||||
|
} else {
|
||||||
|
entry.value_ptr.* = 1;
|
||||||
|
}
|
||||||
|
break :blk entry.value_ptr.*;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1 additional card for each match, multiplied by copies of this card
|
||||||
|
for (1..matches + 1) |offset| {
|
||||||
|
const winning_card_entry = try card_counts.getOrPut(card + offset);
|
||||||
|
if (winning_card_entry.found_existing) {
|
||||||
|
winning_card_entry.value_ptr.* += copies;
|
||||||
|
} else {
|
||||||
|
winning_card_entry.value_ptr.* = copies;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var copy_count: usize = 0;
|
||||||
|
var copy_it = card_counts.valueIterator();
|
||||||
|
while (copy_it.next()) |v| {
|
||||||
|
copy_count += v.*;
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{ .value = value, .copies = copy_count };
|
||||||
|
}
|
||||||
|
|
||||||
|
test "silver" {
|
||||||
|
const input =
|
||||||
|
\\Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53
|
||||||
|
\\Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
|
||||||
|
\\Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1
|
||||||
|
\\Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83
|
||||||
|
\\Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
|
||||||
|
\\Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11
|
||||||
|
;
|
||||||
|
const sln = try solve(std.testing.allocator, input);
|
||||||
|
try std.testing.expectEqual(@as(usize, 13), sln.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "gold" {
|
||||||
|
const input =
|
||||||
|
\\Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53
|
||||||
|
\\Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
|
||||||
|
\\Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1
|
||||||
|
\\Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83
|
||||||
|
\\Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
|
||||||
|
\\Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11
|
||||||
|
;
|
||||||
|
const sln = try solve(std.testing.allocator, input);
|
||||||
|
try std.testing.expectEqual(@as(usize, 30), sln.copies);
|
||||||
|
}
|
223
src/day05.zig
Normal file
223
src/day05.zig
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const util = @import("util.zig");
|
||||||
|
const mem = std.mem;
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
const input = @embedFile("data/day05.txt");
|
||||||
|
const sln = try solve(util.gpa, input);
|
||||||
|
std.debug.print("{d}\n", .{sln});
|
||||||
|
std.debug.print("{d}\n", .{sln});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Range(comptime T: type) type {
|
||||||
|
return struct {
|
||||||
|
start: T,
|
||||||
|
end: T,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
/// Returns the intersection of two ranges.
|
||||||
|
pub fn intersect(self: Self, other: Self) ?Self {
|
||||||
|
const a = if (self.start < other.start) self else other;
|
||||||
|
const b = if (self.start < other.start) other else self;
|
||||||
|
|
||||||
|
if (a.start == a.end or b.start == b.end) return null; // Points don't intersect.
|
||||||
|
|
||||||
|
if (a.end < b.start) return null; // No intersection.
|
||||||
|
if (a.end > b.end) return b; // A is a superset of B.
|
||||||
|
return .{ .start = b.start, .end = a.end };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the union of two ranges.
|
||||||
|
pub fn join(self: Self, other: Self) ?Self {
|
||||||
|
if (self.intersect(other) == null) return null; // Can't union disjoint ranges.
|
||||||
|
// Now guaranteed that they overlap.
|
||||||
|
return .{ .start = @min(self.start, other.start), .end = @max(self.end, other.end) };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const Map = struct {
|
||||||
|
offset: isize,
|
||||||
|
source: Range(usize),
|
||||||
|
};
|
||||||
|
|
||||||
|
const Component = struct {
|
||||||
|
stage: usize,
|
||||||
|
range: Range(usize),
|
||||||
|
};
|
||||||
|
|
||||||
|
fn mergeRanges(ranges: *std.ArrayList(Component)) void {
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < ranges.items.len) : (i += 1) {
|
||||||
|
var item = ranges.items[i];
|
||||||
|
var j = i + 1;
|
||||||
|
while (j < ranges.items.len) {
|
||||||
|
const next = ranges.items[j];
|
||||||
|
if (item.range.join(next.range)) |merged| {
|
||||||
|
const merged_item = .{ .stage = item.stage, .range = merged };
|
||||||
|
item = merged_item;
|
||||||
|
ranges.items[i] = merged_item;
|
||||||
|
_ = ranges.swapRemove(j);
|
||||||
|
} else {
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn solve(alloc: mem.Allocator, input: []const u8) !usize {
|
||||||
|
var it = util.splitLines(input);
|
||||||
|
const seed_line = it.next().?;
|
||||||
|
const seeds = try util.parseIntsScalar(usize, alloc, seed_line[mem.indexOfScalar(u8, seed_line, ':').? + 1 ..], .{});
|
||||||
|
defer alloc.free(seeds);
|
||||||
|
|
||||||
|
var ranges = std.ArrayList(Component).init(alloc);
|
||||||
|
defer ranges.deinit();
|
||||||
|
|
||||||
|
{
|
||||||
|
var index: usize = 0;
|
||||||
|
while (index < seeds.len) : (index += 2) {
|
||||||
|
try ranges.append(.{
|
||||||
|
.stage = 0,
|
||||||
|
.range = .{
|
||||||
|
.start = seeds[index],
|
||||||
|
.end = seeds[index] + seeds[index + 1],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var current_stage: usize = 0;
|
||||||
|
while (it.next()) |line| {
|
||||||
|
if (line.len == 0) {
|
||||||
|
for (ranges.items, 0..) |item, index| {
|
||||||
|
if (item.stage != current_stage) {
|
||||||
|
ranges.items[index] = .{ .stage = current_stage, .range = item.range };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mergeRanges(&ranges);
|
||||||
|
} else if (mem.endsWith(u8, line, "map:")) {
|
||||||
|
current_stage += 1;
|
||||||
|
} else {
|
||||||
|
const map_line = try util.parseIntsScalar(usize, alloc, line, .{});
|
||||||
|
defer alloc.free(map_line);
|
||||||
|
const map = Map{
|
||||||
|
.offset = @as(isize, @intCast(map_line[0])) - @as(isize, @intCast(map_line[1])),
|
||||||
|
.source = .{ .start = map_line[1], .end = map_line[1] + map_line[2] },
|
||||||
|
};
|
||||||
|
|
||||||
|
var i: usize = ranges.items.len;
|
||||||
|
while (i != 0) : (i -= 1) {
|
||||||
|
if (ranges.items[i - 1].stage == current_stage) continue;
|
||||||
|
const item = ranges.swapRemove(i - 1);
|
||||||
|
|
||||||
|
if (item.range.intersect(.{ .start = 0, .end = map.source.start })) |section| {
|
||||||
|
// Non-transitioning section.
|
||||||
|
try ranges.append(.{ .stage = item.stage, .range = section });
|
||||||
|
}
|
||||||
|
if (item.range.intersect(map.source)) |section| {
|
||||||
|
// Transitioning section.
|
||||||
|
try ranges.append(.{
|
||||||
|
.stage = current_stage,
|
||||||
|
.range = .{
|
||||||
|
.start = @intCast(@as(isize, @intCast(section.start)) + map.offset),
|
||||||
|
.end = @intCast(@as(isize, @intCast(section.end)) + map.offset),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (item.range.intersect(.{ .start = map.source.end, .end = std.math.maxInt(usize) })) |section| {
|
||||||
|
// Non-transitioning section.
|
||||||
|
try ranges.append(.{ .stage = item.stage, .range = section });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var min_loc: usize = std.math.maxInt(usize);
|
||||||
|
for (ranges.items) |item| {
|
||||||
|
min_loc = @min(min_loc, item.range.start);
|
||||||
|
}
|
||||||
|
return min_loc;
|
||||||
|
}
|
||||||
|
|
||||||
|
test "silver" {
|
||||||
|
const input =
|
||||||
|
\\seeds: 79 14 55 13
|
||||||
|
\\
|
||||||
|
\\seed-to-soil map:
|
||||||
|
\\50 98 2
|
||||||
|
\\52 50 48
|
||||||
|
\\
|
||||||
|
\\soil-to-fertilizer map:
|
||||||
|
\\0 15 37
|
||||||
|
\\37 52 2
|
||||||
|
\\39 0 15
|
||||||
|
\\
|
||||||
|
\\fertilizer-to-water map:
|
||||||
|
\\49 53 8
|
||||||
|
\\0 11 42
|
||||||
|
\\42 0 7
|
||||||
|
\\57 7 4
|
||||||
|
\\
|
||||||
|
\\water-to-light map:
|
||||||
|
\\88 18 7
|
||||||
|
\\18 25 70
|
||||||
|
\\
|
||||||
|
\\light-to-temperature map:
|
||||||
|
\\45 77 23
|
||||||
|
\\81 45 19
|
||||||
|
\\68 64 13
|
||||||
|
\\
|
||||||
|
\\temperature-to-humidity map:
|
||||||
|
\\0 69 1
|
||||||
|
\\1 0 69
|
||||||
|
\\
|
||||||
|
\\humidity-to-location map:
|
||||||
|
\\60 56 37
|
||||||
|
\\56 93 4
|
||||||
|
;
|
||||||
|
_ = input;
|
||||||
|
// const sln = try solve(std.testing.allocator, input);
|
||||||
|
// try std.testing.expectEqual(@as(usize, 35), sln);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "gold" {
|
||||||
|
const input =
|
||||||
|
\\seeds: 79 14 55 13
|
||||||
|
\\
|
||||||
|
\\seed-to-soil map:
|
||||||
|
\\50 98 2
|
||||||
|
\\52 50 48
|
||||||
|
\\
|
||||||
|
\\soil-to-fertilizer map:
|
||||||
|
\\0 15 37
|
||||||
|
\\37 52 2
|
||||||
|
\\39 0 15
|
||||||
|
\\
|
||||||
|
\\fertilizer-to-water map:
|
||||||
|
\\49 53 8
|
||||||
|
\\0 11 42
|
||||||
|
\\42 0 7
|
||||||
|
\\57 7 4
|
||||||
|
\\
|
||||||
|
\\water-to-light map:
|
||||||
|
\\88 18 7
|
||||||
|
\\18 25 70
|
||||||
|
\\
|
||||||
|
\\light-to-temperature map:
|
||||||
|
\\45 77 23
|
||||||
|
\\81 45 19
|
||||||
|
\\68 64 13
|
||||||
|
\\
|
||||||
|
\\temperature-to-humidity map:
|
||||||
|
\\0 69 1
|
||||||
|
\\1 0 69
|
||||||
|
\\
|
||||||
|
\\humidity-to-location map:
|
||||||
|
\\60 56 37
|
||||||
|
\\56 93 4
|
||||||
|
;
|
||||||
|
const sln = try solve(std.testing.allocator, input);
|
||||||
|
try std.testing.expectEqual(@as(usize, 46), sln);
|
||||||
|
}
|
73
src/day06.zig
Normal file
73
src/day06.zig
Normal 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
132
src/day07.zig
Normal 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
104
src/day08.zig
Normal 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
80
src/day09.zig
Normal 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
170
src/day10.zig
Normal 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
155
src/day11.zig
Normal 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
163
src/day12.zig
Normal 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
136
src/day13.zig
Normal 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
94
src/day14.zig
Normal 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
91
src/day15.zig
Normal 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
159
src/day16.zig
Normal 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);
|
||||||
|
}
|
63
src/util.zig
63
src/util.zig
@ -1,4 +1,65 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const mem = std.mem;
|
||||||
|
|
||||||
const gpa_impl = std.heap.GeneralPurposeAllocator(.{});
|
var gpa_impl = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
/// General purpose allocator.
|
||||||
pub const gpa = gpa_impl.allocator();
|
pub const gpa = gpa_impl.allocator();
|
||||||
|
|
||||||
|
pub const ParseIntsOptions = struct {
|
||||||
|
base: u8 = 10,
|
||||||
|
sep: u8 = ' ',
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Parses separated integers. Sequential separators are treated as one. Caller owns memory.
|
||||||
|
pub fn parseIntsScalar(comptime T: type, alloc: std.mem.Allocator, input: []const u8, opts: ParseIntsOptions) ![]T {
|
||||||
|
var items = std.ArrayList(T).init(alloc);
|
||||||
|
errdefer items.deinit();
|
||||||
|
|
||||||
|
var it = split(input, opts.sep);
|
||||||
|
while (it.next()) |i| {
|
||||||
|
if (i.len == 0) continue;
|
||||||
|
try items.append(try std.fmt.parseInt(T, i, opts.base));
|
||||||
|
}
|
||||||
|
return try items.toOwnedSlice();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shortcut to splitScalar with a byte buffer.
|
||||||
|
pub fn split(buffer: []const u8, sep: u8) mem.SplitIterator(u8, .scalar) {
|
||||||
|
return mem.splitScalar(u8, buffer, sep);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn splitLines(buffer: []const u8) mem.SplitIterator(u8, .scalar) {
|
||||||
|
return split(buffer, '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
42
template.zig
Normal file
42
template.zig
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const util = @import("util.zig");
|
||||||
|
const mem = std.mem;
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
const input = @embedFile("data/dayXX.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 {
|
||||||
|
_ = alloc;
|
||||||
|
|
||||||
|
var it = util.splitLines(input);
|
||||||
|
while (it.next()) |line| {
|
||||||
|
if (line.len == 0) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{ .a = 0, .b = 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
test "silver" {
|
||||||
|
const input =
|
||||||
|
\\
|
||||||
|
;
|
||||||
|
const sln = try solve(std.testing.allocator, input);
|
||||||
|
try std.testing.expectEqual(@as(usize, 0), sln.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "gold" {
|
||||||
|
const input =
|
||||||
|
\\
|
||||||
|
;
|
||||||
|
const sln = try solve(std.testing.allocator, input);
|
||||||
|
try std.testing.expectEqual(@as(usize, 0), sln.b);
|
||||||
|
}
|
Reference in New Issue
Block a user