day 9 finished

This commit is contained in:
Moritz Gmeiner 2024-12-09 16:20:46 +01:00
commit 8b34776508
7 changed files with 722 additions and 2 deletions

View file

@ -23,7 +23,7 @@ pub fn main() !void {
const result = try part2(alloc, file_reader.reader());
try std.io.getStdOut().writer().print("Day 6, part 1: {}\n", .{result});
try std.io.getStdOut().writer().print("Day 6, part 2: {}\n", .{result});
}
}

View file

@ -25,7 +25,7 @@ pub fn main() !void {
const result = try part2(alloc, file_reader.reader());
try std.io.getStdOut().writer().print("Day 7, part 1: {}\n", .{result});
try std.io.getStdOut().writer().print("Day 7, part 2: {}\n", .{result});
}
}

308
day8.zig Normal file
View file

@ -0,0 +1,308 @@
const std = @import("std");
const utils = @import("lib/utils.zig");
const spice = @import("lib/spice.zig");
const filename = "inputs/day8.txt";
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const alloc = gpa.allocator();
{
const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit();
const result = try part1(alloc, file_reader.reader());
try std.io.getStdOut().writer().print("Day 8, part 1: {}\n", .{result});
}
{
const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit();
const result = try part2(alloc, file_reader.reader());
try std.io.getStdOut().writer().print("Day 8, part 2: {}\n", .{result});
}
}
fn part1(alloc: std.mem.Allocator, reader: anytype) !u64 {
const Pos = struct {
x: usize,
y: usize,
};
var arena = std.heap.ArenaAllocator.init(alloc);
defer arena.deinit();
var freq_pos = std.AutoHashMap(u8, std.ArrayList(Pos)).init(arena.allocator());
defer freq_pos.deinit();
var line_reader = utils.lineReader(alloc, reader);
defer line_reader.deinit();
var x_size: usize = 0;
var y_size: usize = 0;
while (try line_reader.next()) |line| : (x_size += 1) {
if (line.len == 0) {
break;
}
if (y_size == 0) {
y_size = line.len;
} else {
std.debug.assert(y_size == line.len);
}
for (0.., line) |y, c| {
if (c != '.') {
const entry = try freq_pos.getOrPut(c);
const pos_list = entry.value_ptr;
if (!entry.found_existing) {
pos_list.* = std.ArrayList(Pos).init(arena.allocator());
}
try pos_list.append(.{ .x = x_size, .y = y });
}
}
}
const antinodes = try utils.allocGridInit(bool, alloc, x_size, y_size, false);
defer utils.deinitGrid(bool, alloc, antinodes);
for (antinodes) |line| {
for (line) |b| {
std.debug.assert(b == false);
}
}
var it = freq_pos.iterator();
while (it.next()) |entry| {
const pos_list = entry.value_ptr.items;
const n = pos_list.len;
for (0..n) |i| {
const pos_i = pos_list[i];
for (0..i) |j| {
const pos_j = pos_list[j];
const diff_x = pos_i.x -% pos_j.x;
const diff_y = pos_i.y -% pos_j.y;
const antinode1_x = pos_i.x +% diff_x;
const antinode1_y = pos_i.y +% diff_y;
if (antinode1_x < x_size and antinode1_y < y_size) {
antinodes[antinode1_x][antinode1_y] = true;
}
const antinode2_x = pos_j.x -% diff_x;
const antinode2_y = pos_j.y -% diff_y;
if (antinode2_x < x_size and antinode2_y < y_size) {
antinodes[antinode2_x][antinode2_y] = true;
}
}
}
}
var antinode_count: u64 = 0;
for (antinodes) |line| {
for (line) |b| {
if (b) {
antinode_count += 1;
}
}
}
return antinode_count;
}
fn part2(alloc: std.mem.Allocator, reader: anytype) !u64 {
const Pos = struct {
x: usize,
y: usize,
};
var arena = std.heap.ArenaAllocator.init(alloc);
defer arena.deinit();
var freq_pos = std.AutoHashMap(u8, std.ArrayList(Pos)).init(arena.allocator());
defer freq_pos.deinit();
var line_reader = utils.lineReader(alloc, reader);
defer line_reader.deinit();
var x_size: usize = 0;
var y_size: usize = 0;
while (try line_reader.next()) |line| : (x_size += 1) {
if (line.len == 0) {
break;
}
if (y_size == 0) {
y_size = line.len;
} else {
std.debug.assert(y_size == line.len);
}
for (0.., line) |y, c| {
if (c != '.') {
const entry = try freq_pos.getOrPut(c);
const pos_list = entry.value_ptr;
if (!entry.found_existing) {
pos_list.* = std.ArrayList(Pos).init(arena.allocator());
}
try pos_list.append(.{ .x = x_size, .y = y });
}
}
}
const antinodes = try utils.allocGridInit(bool, alloc, x_size, y_size, false);
defer utils.deinitGrid(bool, alloc, antinodes);
for (antinodes) |line| {
for (line) |b| {
std.debug.assert(b == false);
}
}
var it = freq_pos.iterator();
while (it.next()) |entry| {
const pos_list = entry.value_ptr.items;
const n = pos_list.len;
for (0..n) |i| {
const pos_i = pos_list[i];
for (0..i) |j| {
const pos_j = pos_list[j];
const i_minus_j_x = pos_i.x -% pos_j.x;
const i_minus_j_y = pos_i.y -% pos_j.y;
{
var antinode_x = pos_i.x;
var antinode_y = pos_i.y;
while (antinode_x < x_size and antinode_y < y_size) {
antinodes[antinode_x][antinode_y] = true;
antinode_x +%= i_minus_j_x;
antinode_y +%= i_minus_j_y;
}
}
{
var antinode_x = pos_j.x;
var antinode_y = pos_j.y;
while (antinode_x < x_size and antinode_y < y_size) {
antinodes[antinode_x][antinode_y] = true;
antinode_x -%= i_minus_j_x;
antinode_y -%= i_minus_j_y;
}
}
}
}
}
var antinode_count: u64 = 0;
for (antinodes) |line| {
for (line) |b| {
if (b) {
antinode_count += 1;
}
}
}
return antinode_count;
}
test "part1 example" {
const alloc = std.testing.allocator;
const example =
\\............
\\........0...
\\.....0......
\\.......0....
\\....0.......
\\......A.....
\\............
\\............
\\........A...
\\.........A..
\\............
\\............
;
var stream = std.io.fixedBufferStream(example);
const result = try part1(alloc, stream.reader());
try std.testing.expectEqual(14, result);
}
test "part1 input" {
const alloc = std.testing.allocator;
const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit();
const result = try part1(alloc, file_reader.reader());
try std.testing.expectEqual(381, result);
}
test "part2 example" {
const alloc = std.testing.allocator;
const example =
\\............
\\........0...
\\.....0......
\\.......0....
\\....0.......
\\......A.....
\\............
\\............
\\........A...
\\.........A..
\\............
\\............
;
var stream = std.io.fixedBufferStream(example);
const result = try part2(alloc, stream.reader());
try std.testing.expectEqual(34, result);
}
test "part2 input" {
const alloc = std.testing.allocator;
const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit();
const result = try part2(alloc, file_reader.reader());
try std.testing.expectEqual(1184, result);
}

329
day9.zig Normal file
View file

@ -0,0 +1,329 @@
const std = @import("std");
const utils = @import("lib/utils.zig");
const spice = @import("lib/spice.zig");
const filename = "inputs/day9.txt";
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const alloc = gpa.allocator();
{
const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit();
const result = try part1(alloc, file_reader.reader());
try std.io.getStdOut().writer().print("Day 8, part 1: {}\n", .{result});
}
{
const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit();
const result = try part2(alloc, file_reader.reader());
try std.io.getStdOut().writer().print("Day 8, part 2: {}\n", .{result});
}
}
fn part1(alloc: std.mem.Allocator, reader: anytype) !u64 {
const FsEntry = enum(u32) {
empty = std.math.maxInt(u32),
_,
};
const input = try reader.readAllAlloc(alloc, std.math.maxInt(usize));
defer alloc.free(input);
var filesystem_list = std.ArrayList(FsEntry).init(alloc);
defer filesystem_list.deinit();
var next_id: u16 = 0;
var is_gap = false;
for (input) |byte| {
if (byte == '\n') {
break;
}
std.debug.assert('0' <= byte and byte <= '9');
const len: usize = @as(usize, byte - '0');
if (is_gap) {
try filesystem_list.appendNTimes(.empty, len);
} else {
try filesystem_list.appendNTimes(@enumFromInt(next_id), len);
next_id += 1;
std.debug.assert(next_id != @intFromEnum(FsEntry.empty));
}
is_gap = !is_gap;
}
const filesystem = filesystem_list.items;
// for (filesystem) |x| {
// switch (x) {
// .empty => std.debug.print(".", .{}),
// else => std.debug.print("{}", .{@intFromEnum(x)}),
// }
// }
// std.debug.print("\n", .{});
var start_idx: usize = 0;
var end_idx: usize = filesystem.len - 1;
while (start_idx < end_idx) {
// for start_idx: seek next empty position
if (filesystem[start_idx] != .empty) {
start_idx += 1;
continue;
}
// for end_idx: seek next full position
if (filesystem[end_idx] == .empty) {
end_idx -= 1;
continue;
}
std.debug.assert(filesystem[start_idx] == .empty and filesystem[end_idx] != .empty);
filesystem[start_idx] = filesystem[end_idx];
filesystem[end_idx] = .empty;
}
const first_empty_idx = std.mem.indexOfScalar(FsEntry, filesystem, .empty).?;
for (filesystem[first_empty_idx..]) |id| {
std.debug.assert(id == .empty);
}
var checksum: u64 = 0;
for (0.., filesystem) |idx, id| {
if (id == .empty) {
break;
}
checksum += @as(u64, idx) * @intFromEnum(id);
}
return checksum;
}
fn part2(alloc: std.mem.Allocator, reader: anytype) !u64 {
const FsId = enum(u16) {
empty = std.math.maxInt(u16),
_,
};
const FsEntry = struct {
len: u16,
id: FsId,
};
const input = try reader.readAllAlloc(alloc, std.math.maxInt(usize));
defer alloc.free(input);
var filesystem = std.ArrayList(FsEntry).init(alloc);
defer filesystem.deinit();
var max_id: u16 = undefined;
{
var next_id: u16 = 0;
var is_gap = false;
for (input) |byte| {
if (byte == '\n') {
break;
}
std.debug.assert('0' <= byte and byte <= '9');
const len: u16 = @as(u16, byte - '0');
if (is_gap) {
try filesystem.append(.{ .len = len, .id = .empty });
} else {
try filesystem.append(.{ .len = len, .id = @enumFromInt(next_id) });
next_id += 1;
std.debug.assert(next_id != @intFromEnum(FsId.empty));
}
is_gap = !is_gap;
}
max_id = next_id - 1;
}
// for (filesystem.items) |entry| {
// for (0..entry.len) |_| {
// switch (entry.id) {
// .empty => std.debug.print(".", .{}),
// else => std.debug.print("{}", .{@intFromEnum(entry.id)}),
// }
// }
// }
// std.debug.print("\n", .{});
{
// next ID to check; neccessary for checking all files once
// since file entries are sorted in increasing order, we can simply decrease this with each
// checked file
var next_id = max_id;
// index of next filesystem entry to check
var idx: usize = filesystem.items.len - 1;
while (idx > 0) : (idx -= 1) {
const entry = filesystem.items[idx];
// we should never see and ID smaller than the one we're looking for
// if we do that means we would skip that file
std.debug.assert(entry.id == .empty or @intFromEnum(entry.id) >= next_id);
if (@intFromEnum(entry.id) != next_id) {
continue;
}
// found next entry to move
// decrease next ID to find
next_id -= 1;
const len = entry.len;
for (0..idx) |jdx| {
const target_entry = filesystem.items[jdx];
// find the first entry with is empty and at least as large as our entry
if (target_entry.id != .empty or target_entry.len < len) {
continue;
}
// found target entry
if (target_entry.len == entry.len) {
// exact hit! no need to split entry, just swap IDs
std.mem.swap(FsId, &filesystem.items[idx].id, &filesystem.items[jdx].id);
} else {
// empty space is larger than our entry; need to split
// warning! this (potentially) invalidates all pointers into filesystem.items
// and we need to adjust indices (i.e. increase idx by one) since everything after
// jdx shift back by one index
_ = try filesystem.addManyAt(jdx + 1, 1);
idx += 1;
// jdx + 1 becomes empty entry of len target_entry.len - entry.len
filesystem.items[jdx + 1].id = .empty;
filesystem.items[jdx + 1].len = target_entry.len - entry.len;
// jdx becomes entry
filesystem.items[jdx].len = entry.len;
filesystem.items[jdx].id = entry.id;
// idx becomes empty
filesystem.items[idx].id = .empty;
}
break;
}
}
}
// for (filesystem.items) |entry| {
// for (0..entry.len) |_| {
// switch (entry.id) {
// .empty => std.debug.print(".", .{}),
// else => std.debug.print("{}", .{@intFromEnum(entry.id)}),
// }
// }
// }
// std.debug.print("\n", .{});
var checksum: u64 = 0;
{
var idx: usize = 0;
for (filesystem.items) |entry| {
const len = entry.len;
// skip over empty entry
if (entry.id == .empty) {
idx += len;
continue;
}
const id = @intFromEnum(entry.id);
for (0..len) |_| {
checksum += id * idx;
idx += 1;
}
}
}
return checksum;
}
test "part1 example" {
const alloc = std.testing.allocator;
const example = "2333133121414131402";
var stream = std.io.fixedBufferStream(example);
const result = try part1(alloc, stream.reader());
try std.testing.expectEqual(1928, result);
}
test "part1 input" {
const alloc = std.testing.allocator;
const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit();
const result = try part1(alloc, file_reader.reader());
try std.testing.expectEqual(6307275788409, result);
}
test "part2 example" {
const alloc = std.testing.allocator;
const example = "2333133121414131402";
var stream = std.io.fixedBufferStream(example);
const result = try part2(alloc, stream.reader());
try std.testing.expectEqual(2858, result);
}
test "part2 input" {
const alloc = std.testing.allocator;
const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit();
const result = try part2(alloc, file_reader.reader());
try std.testing.expectEqual(6327174563252, result);
}

50
inputs/day8.txt Normal file
View file

@ -0,0 +1,50 @@
......................D....B...h..................
..............................h...................
.............D...3.....X..................9.......
...........C........X....2.hB......v........b.....
....................................O.............
......u.....3.........p...........................
....u......................v....6.................
......................y..D.....Ov.2..............b
.....u..........X...........o........y............
.........................y...B.f...........s......
.7....................C.2.....Bsyp..........t...q.
.u.7...........X............................Oe..t.
...........V........3......6v.s........o....h....t
..E........L.................6..........o......9..
........E......m.2.P.......O...9...8....b.........
..m..........3.......p..........M8................
..1.....................K.p....................b.e
5...............L...........s.6..........S.M......
....5..1.......E.........k.f.........M............
.E..Y..V......l.......T...D.......9....Q..........
..............................M...................
.....5....P................m...x..q......F......e.
................f...c......................x..F...
..V.C...........7.......a....o....8.........F.....
.......4....L.a..g..P.....8......Q....7d..........
...1......4..a............k......t...d............
..........V..........L....m........K....Q........S
..................1....k.....T....................
..........l......a...............F................
...........P...4.......l......x...................
.............c....g........T......................
.....g............c...Q.......................S...
...............l..................A.d.T.U.........
..........................f...0.............d.....
..........G..................A............e.S...x.
.........Y.......q........g....K..................
.....................q.H4...0.................j...
....................HA..............J.............
..Y..........................0...J.......j........
.......................G.JA...................U...
.......5..........................................
...........c..............G.........K.............
...............................G..................
...........................0.j....................
............................H.......k..........U..
.........................H........................
...................................Y....J.........
..................................j...............
..................................................
..................................................

1
inputs/day9.txt Normal file

File diff suppressed because one or more lines are too long

View file

@ -100,6 +100,38 @@ pub fn numberParserWithDelimiter(comptime T: type, input: []const u8, delimiter:
return NumberParser(T){ .token_it = std.mem.tokenizeScalar(u8, input, delimiter) };
}
pub fn allocGrid(comptime T: type, alloc: std.mem.Allocator, n: usize, m: usize) ![][]T {
const grid = try alloc.alloc([]T, n);
for (grid) |*line| {
line.* = try alloc.alloc(T, m);
}
return grid;
}
pub fn allocGridInit(comptime T: type, alloc: std.mem.Allocator, n: usize, m: usize, init: T) ![][]T {
const grid = try alloc.alloc([]T, n);
for (grid) |*line| {
line.* = try alloc.alloc(T, m);
for (line.*) |*x| {
x.* = init;
}
}
return grid;
}
pub fn deinitGrid(comptime T: type, alloc: std.mem.Allocator, grid: [][]T) void {
for (grid) |line| {
alloc.free(line);
}
alloc.free(grid);
}
pub fn rangeComptime(comptime n: usize) [n]usize {
var array: [n]usize = undefined;