From 71c5d3ad4dee3bddddcddba7e013212bb8b5fc8c Mon Sep 17 00:00:00 2001 From: Guido Schmidt Date: Tue, 2 Dec 2025 09:50:32 +0100 Subject: [PATCH 01/18] refactor(modules/aoc): move advent of code framework into its own module started fetching puzzle input in the build system (see https://ziggit.dev/t/unable-to-make-https-calls-from-build-zig/13297/8) --- 2025/.gitignore | 2 + 2025/SUMMARY.md | 18 +++- 2025/build.zig | 71 ++++++++++++++ 2025/build.zig.zon | 16 ++++ 2025/src/day01.zig | 73 ++++++++++++++ modules/aoc/build.zig | 36 +++++++ modules/aoc/build.zig.zon | 19 ++++ modules/aoc/src/common.zig | 50 ++++++++++ modules/aoc/src/fetch-puzzle-input.zig | 33 +++++++ modules/aoc/src/input.zig | 128 +++++++++++++++++++++++++ modules/aoc/src/root.zig | 9 ++ modules/aoc/src/stopwatch.zig | 16 ++++ modules/aoc/src/template.zig | 29 ++++++ modules/aoc/src/types.zig | 6 ++ modules/aoc/src/utils.zig | 32 +++++++ 15 files changed, 535 insertions(+), 3 deletions(-) create mode 100644 2025/.gitignore create mode 100644 2025/build.zig create mode 100644 2025/build.zig.zon create mode 100644 2025/src/day01.zig create mode 100644 modules/aoc/build.zig create mode 100644 modules/aoc/build.zig.zon create mode 100644 modules/aoc/src/common.zig create mode 100644 modules/aoc/src/fetch-puzzle-input.zig create mode 100644 modules/aoc/src/input.zig create mode 100644 modules/aoc/src/root.zig create mode 100644 modules/aoc/src/stopwatch.zig create mode 100644 modules/aoc/src/template.zig create mode 100644 modules/aoc/src/types.zig create mode 100644 modules/aoc/src/utils.zig diff --git a/2025/.gitignore b/2025/.gitignore new file mode 100644 index 0000000..b08e799 --- /dev/null +++ b/2025/.gitignore @@ -0,0 +1,2 @@ +# Avoid commiting puzzle inputs +**/*.txt diff --git a/2025/SUMMARY.md b/2025/SUMMARY.md index 00a8d0b..cb3edf4 100644 --- a/2025/SUMMARY.md +++ b/2025/SUMMARY.md @@ -1,4 +1,16 @@ -# AoC 2025 -### Notes & Comments +# Advent of Code 2025 +### Summary -## Day 1 + +# Day 01 +### Part 1 +Easy start. Some tipps: +- Parse `L` and `R` into `-1` and `1` +- Use `@mod` for the rotational arithmetic of the dial +- Don't forget to initialise the dial to `50`! + +### Part 2 +Somehow tried to be smart and calculate the amount of rotations first with: +```zig +const rotation_count = @abs(@divTrunc(dial, change)); +``` diff --git a/2025/build.zig b/2025/build.zig new file mode 100644 index 0000000..7f0b157 --- /dev/null +++ b/2025/build.zig @@ -0,0 +1,71 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const YEAR = 2025; +var DAY: u5 = 1; + +pub fn build(b: *std.Build) !void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + if (b.args) |args| { + DAY = std.fmt.parseInt(u5, args[0], 10) catch 1; + std.debug.print("šŸŽ…šŸ¼ Running day {d:0>2}\n", .{DAY}); + } else { + // @TODO + // try aoc.getToday(); + } + + // const cwd = try std.fs.cwd().realpathAlloc(b.allocator, "."); + // try aoc.fetchInputData(b, target, optimize, cwd, YEAR, DAY); + + const dep_aoc = b.dependency("aoc", .{}); + const aoc_puzzleinput = dep_aoc.artifact("puzzle-input-helper"); + std.debug.print("{any}\n", .{@TypeOf(aoc_puzzleinput)}); + + const cmd = b.addRunArtifact(aoc_puzzleinput); + const arg_year = try std.fmt.allocPrint(b.allocator, "{d}", .{YEAR}); + defer b.allocator.free(arg_year); + const arg_day = try std.fmt.allocPrint(b.allocator, "{d}", .{DAY}); + defer b.allocator.free(arg_day); + cmd.addArg(arg_year); + cmd.addArg(arg_day); + const captured_output = cmd.captureStdOut(); // use this as anonymous import + + const src_name = try std.fmt.allocPrint(b.allocator, "day{d:0>2}.zig", .{DAY}); + defer b.allocator.free(src_name); + const src_path = try std.fs.path.join(b.allocator, &.{ "src", src_name }); + defer b.allocator.free(src_path); + const exe_name = try std.fmt.allocPrint(b.allocator, "aoc-{d}-day{d:0>2}", .{ YEAR, DAY }); + defer b.allocator.free(exe_name); + + _ = std.fs.cwd().openFile(src_path, .{}) catch { + std.debug.print("{s} not found. Creating from template...\n", .{src_path}); + const contents = try aoc.createTemplate(b.allocator, YEAR, DAY); + defer b.allocator.free(contents); + std.debug.print("{s}\n", .{contents}); + const src_file = try std.fs.cwd().createFile(src_path, .{}); + defer src_file.close(); + try src_file.writeAll(contents); + }; + + const exe = b.addExecutable(.{ + .name = exe_name, + .root_module = b.createModule(.{ + .root_source_file = b.path(src_path), + .optimize = optimize, + .target = target, + }), + }); + b.installArtifact(exe); + + exe.root_module.addAnonymousImport("puzzle", .{ .root_source_file = captured_output }); + + exe.root_module.addImport("aoc", dep_aoc.module("aoc")); + + const run_cmd = b.addRunArtifact(exe); + run_cmd.step.dependOn(b.getInstallStep()); + + const run_step = b.step("run", "Start the program"); + run_step.dependOn(&run_cmd.step); +} diff --git a/2025/build.zig.zon b/2025/build.zig.zon new file mode 100644 index 0000000..347fa36 --- /dev/null +++ b/2025/build.zig.zon @@ -0,0 +1,16 @@ +.{ + .name = .advent_of_code2025, + .fingerprint = 0x775c1acd7871bd1a, + .version = "0.0.1", + .minimum_zig_version = "0.15.2", + .dependencies = .{ + .aoc = .{ .path = "../modules/aoc" }, + }, + .paths = .{ + "README.md", + "build.zig", + "build.zig.zon", + "src", + "modules", + }, +} diff --git a/2025/src/day01.zig b/2025/src/day01.zig new file mode 100644 index 0000000..bc70121 --- /dev/null +++ b/2025/src/day01.zig @@ -0,0 +1,73 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const YEAR: u12 = 2025; +const DAY: u5 = 1; + +const Allocator = std.mem.Allocator; +const log = std.log; + +fn part1(allocator: Allocator, input: []const u8) anyerror!void { + _ = allocator; + var row_it = std.mem.tokenizeSequence(u8, input, "\n"); + const max = 100; + var i: u32 = 0; + var dial: i32 = 50; + var result: u32 = 0; + while (row_it.next()) |row| : (i += 1) { + // std.debug.print("{d}\n", .{dial}); + const sign: i32 = if (row[0] == 'L') -1 else 1; + const number = try std.fmt.parseInt(i32, row[1..], 10); + dial += sign * number; + dial = @mod(dial, max); + if (dial == 0) result += 1; + } + std.debug.print("\nResult: {d}\n", .{result}); +} + +fn part2(allocator: Allocator, input: []const u8) anyerror!void { + _ = allocator; + var row_it = std.mem.tokenizeSequence(u8, input, "\n"); + const max = 100; + var i: u32 = 0; + var dial: i32 = 50; + var result: u32 = 0; + var rotation_count: i32 = 0; + while (row_it.next()) |row| : (i += 1) { + const sign: i32 = if (row[0] == 'L') -1 else 1; + const number = try std.fmt.parseInt(i32, row[1..], 10); + var change = sign * number; + const rotations = @as(f32, @floatFromInt(dial)) / @as(f32, @floatFromInt(change)); + rotation_count += @as(i32, @intFromFloat(rotations)); + std.debug.print("{d} → {d} → {d} [#rotations: {d}]\n", .{ + dial, + change, + @mod(dial + change, max), + rotation_count, + }); + + // dial += sign * number; + // dial = @mod(dial, max); + // if (rotation_count == 0 and dial == 0) smart_result += 1; + + while (change != 0) : (change += -1 * sign) { + dial += sign; + dial = @mod(dial, max); + if (dial == 0) result += 1; + } + } + std.debug.print("\nResult: {d}", .{result}); + // std.debug.print("\nOther Result: {d}\n", .{smart_result}); +} + +pub fn main() !void { + // var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + // defer arena.deinit(); + // const allocator = arena.allocator(); + + std.debug.print("{s}\n", .{@embedFile("puzzle")}); + + // try aoc.runPart(allocator, DAY, .PUZZLE, part1); + // try aoc.runPart(allocator, DAY, .EXAMPLE, part2); + // try aoc.runPart(allocator, DAY, .PUZZLE, part2); +} diff --git a/modules/aoc/build.zig b/modules/aoc/build.zig new file mode 100644 index 0000000..8ded910 --- /dev/null +++ b/modules/aoc/build.zig @@ -0,0 +1,36 @@ +const std = @import("std"); +const input = @import("src/input.zig"); +const types = @import("src/types.zig"); +const utils = @import("src/utils.zig"); + +pub fn createTemplate(allocator: std.mem.Allocator, year: types.Year, day: types.Day) ![]const u8 { + var template_str = utils.getTemplate(); + const day_str = try std.fmt.allocPrint(allocator, "{d}", .{day}); + defer allocator.free(day_str); + const year_str = try std.fmt.allocPrint(allocator, "{d}", .{year}); + defer allocator.free(year_str); + template_str = try std.mem.replaceOwned(u8, allocator, template_str, "\"$DAY\"", day_str); + template_str = try std.mem.replaceOwned(u8, allocator, template_str, "\"$YEAR\"", year_str); + return template_str; +} + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + _ = b.addModule("aoc", .{ + .root_source_file = b.path("src/root.zig"), + .target = target, + .optimize = optimize, + }); + + const exe_puzzlehelper = b.addExecutable(.{ + .name = "puzzle-input-helper", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/fetch-puzzle-input.zig"), + .target = target, + .optimize = optimize, + }), + }); + b.installArtifact(exe_puzzlehelper); +} diff --git a/modules/aoc/build.zig.zon b/modules/aoc/build.zig.zon new file mode 100644 index 0000000..c748f3e --- /dev/null +++ b/modules/aoc/build.zig.zon @@ -0,0 +1,19 @@ +.{ + .name = .aoc, + .fingerprint = 0x808a3f8f28be9351, + .version = "0.0.2", + .minimum_zig_version = "0.15.2", + .dependencies = .{ + .zeit = .{ + .url = "git+https://github.com/rockorager/zeit#b01d40568edc0b1c1a2ec0692619449a2a39c718", + .hash = "zeit-0.6.0-5I6bk05_AgAUb00ViMy0zAkEf4N38JCSUUrjCW4s-haJ", + }, + }, + .paths = .{ + "README.md", + "build.zig", + "build.zig.zon", + "src", + "modules", + }, +} diff --git a/modules/aoc/src/common.zig b/modules/aoc/src/common.zig new file mode 100644 index 0000000..2ef5f19 --- /dev/null +++ b/modules/aoc/src/common.zig @@ -0,0 +1,50 @@ +const std = @import("std"); +const types = @import("types.zig"); +const puzzle_input = @import("input.zig"); +const stopwatch = @import("stopwatch.zig"); + +const Allocator = std.mem.Allocator; + +pub fn printPart1() void { + std.debug.print("\n########## Part 1 ##########", .{}); +} + +pub fn printPart2() void { + std.debug.print("\n########## Part 2 ##########", .{}); +} + +pub fn printTime(time: u64) void { + const ns = time; + const us: f64 = @floatFromInt(time / std.time.ns_per_us); + const ms: f64 = @floatFromInt(time / std.time.ns_per_ms); + std.debug.print("\n— ā² Running time: {d:3} ms / {d:3} μs / {d} ns\n", .{ ms, us, ns }); +} + +pub fn runPart( + allocator: std.mem.Allocator, + comptime day: types.Day, + input_type: types.PuzzleInput, + comptime part_fn: fn (allocator: Allocator, input: []const u8) anyerror!void, +) !void { + _ = day; + const input = switch (input_type) { + .PUZZLE => "", //try puzzle_input.getPuzzleInput(allocator, cwd, day), + .EXAMPLE => "", //try puzzle_input.getExampleInput(allocator, cwd, day), + }; + stopwatch.start(); + try part_fn(allocator, input); + const time = stopwatch.stop(); + printTime(time); +} + +pub fn runDay( + allocator: std.mem.Allocator, + comptime year: types.Year, + comptime day: types.Day, + input_type: types.PuzzleInput, + comptime part1: fn (allocator: Allocator, input: []const u8) anyerror!void, + comptime part2: fn (allocator: Allocator, input: []const u8) anyerror!void, +) !void { + try runPart(allocator, year, day, input_type, part1); + try runPart(allocator, year, day, input_type, part2); +} diff --git a/modules/aoc/src/fetch-puzzle-input.zig b/modules/aoc/src/fetch-puzzle-input.zig new file mode 100644 index 0000000..ea66384 --- /dev/null +++ b/modules/aoc/src/fetch-puzzle-input.zig @@ -0,0 +1,33 @@ +/// Small helper executable that allows to fetch the puzzle input from +/// https://adventofcode.com/$YEAR/day/$DAY/input +/// CLI arguments are +/// - year +/// - day +/// - Optional file path to store the puzzle input to disk. This is sometimes useful, +/// e.g. if you want to implement a solution in another language or just need to +/// peek into the data +const std = @import("std"); +const types = @import("types.zig"); +const input = @import("input.zig"); + +pub fn main() !void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + + var args = try std.process.argsWithAllocator(allocator); + defer args.deinit(); + _ = args.next(); // binary name + + if (args.inner.count < 3) { + _ = try std.fs.File.stdout().write("Please provide 2 arguments: YEAR DAY, e.g. 2025 7"); + return; + } + + const YEAR = try std.fmt.parseInt(types.Year, args.next().?, 10); + const DAY = try std.fmt.parseInt(types.Day, args.next().?, 10); + const puzzle_file_path = args.next(); + + const response = try input.getPuzzleInputFromServer(allocator, YEAR, DAY, puzzle_file_path); + try std.fs.File.stdout().writeAll(response); +} diff --git a/modules/aoc/src/input.zig b/modules/aoc/src/input.zig new file mode 100644 index 0000000..fa38c3d --- /dev/null +++ b/modules/aoc/src/input.zig @@ -0,0 +1,128 @@ +const std = @import("std"); +const types = @import("types.zig"); + +const fs = std.fs; +const http = std.http; + +fn getAdventOfCodeCookieFromEnv(allocator: std.mem.Allocator) !?[]const u8 { + const env_map = try allocator.create(std.process.EnvMap); + env_map.* = try std.process.getEnvMap(allocator); + return env_map.get("AOC_COOKIE"); +} + +pub fn getPuzzleInputFromServer( + allocator: std.mem.Allocator, + year: types.Year, + day: types.Day, + puzzle_file_path: ?[]const u8, +) ![]const u8 { + std.debug.print("šŸŽ„ Fetching puzzle input ...\n", .{}); + + // Get AOC_COOKIE from environment + const cookie = try getAdventOfCodeCookieFromEnv(allocator); + if (cookie == null) { + const msg = "⚠ Please set AOC_COOKIE env variable1"; + std.log.err(msg, .{}); + return msg; + } + std.debug.print("šŸŖ AOC_COOKIE:\n{s}\n", .{cookie.?}); + + // Build URL string + var buf: [128]u8 = undefined; + const url = try std.fmt.bufPrint(&buf, "https://adventofcode.com/{d}/day/{d}/input", .{ + year, + day, + }); + + // Init HTT client + var client = std.http.Client{ + .allocator = allocator, + }; + defer client.deinit(); + + // Body writer + var response: std.Io.Writer.Allocating = .init(allocator); + defer response.deinit(); + + // Perform HTTP call + _ = try client.fetch(.{ + .location = .{ .url = url }, + .extra_headers = &.{.{ + .name = "cookie", + .value = cookie.?, + }}, + .method = .GET, + .response_writer = &response.writer, + }); + + const puzzle_input = try response.toOwnedSlice(); + + // Write to file + if (puzzle_file_path) |filepath| { + var puzzle_file = try std.fs.cwd().createFile(filepath, .{}); + defer puzzle_file.close(); + try puzzle_file.writeAll(puzzle_input); + } + + return puzzle_input; +} + +pub fn getPuzzleInput( + allocator: std.mem.Allocator, + cwd: []const u8, + day: types.Day, +) ![]const u8 { + var day_buf: [16]u8 = undefined; + + const file_path = try std.fs.path.join(allocator, &.{ + cwd, + "src", + "input", + "puzzle", + try std.fmt.bufPrint(&day_buf, "day{d:0>2}.txt", .{day}), + }); + std.debug.print("{s}\n", .{file_path}); + + const file = try fs.cwd().openFile(file_path, .{}); + // catch { + // // try getPuzzleInputFromServer(allocator, year, day, file_path); + // // const f = try fs.cwd().openFile(file_path, .{}); + // // const s = try f.stat(); + // // var buffer: [1]u8 = undefined; + // // var reader = f.reader(&buffer); + // // return reader.interface.readAlloc(allocator, s.size); + // }; + const stat = try file.stat(); + std.debug.print("File size: {d}", .{stat.size}); + if (stat.size == 0) { + // try getPuzzleInputFromServer(allocator, year, day, file_path); + } + + var buffer: [1]u8 = undefined; + var reader = file.reader(&buffer); + return reader.interface.readAlloc(allocator, stat.size); +} + +pub fn getExampleInput( + allocator: std.mem.Allocator, + cwd: []const u8, + day: types.Day, +) ![]const u8 { + var day_buf: [16]u8 = undefined; + const file_path = try std.fs.path.join(allocator, &.{ + cwd, + "src", + "input", + "example", + // try std.fmt.bufPrint(&year_buf, "{d}", .{year}), + // "examples", + try std.fmt.bufPrint(&day_buf, "day{d:0>2}.txt", .{day}), + }); + errdefer allocator.free(file_path); + const file = try fs.cwd().openFile(file_path, .{}); + defer file.close(); + const stat = try file.stat(); + var buffer: [1]u8 = undefined; + var reader = file.reader(&buffer); + return reader.interface.readAlloc(allocator, stat.size); +} diff --git a/modules/aoc/src/root.zig b/modules/aoc/src/root.zig new file mode 100644 index 0000000..b590a9d --- /dev/null +++ b/modules/aoc/src/root.zig @@ -0,0 +1,9 @@ +const std = @import("std"); +const common = @import("common.zig"); + +pub const utils = @import("utils.zig"); + +pub const runPart = common.runPart; +pub const runDay = common.runDay; + +pub const blockAskForNext = utils.blockAskForNext; diff --git a/modules/aoc/src/stopwatch.zig b/modules/aoc/src/stopwatch.zig new file mode 100644 index 0000000..56db3ae --- /dev/null +++ b/modules/aoc/src/stopwatch.zig @@ -0,0 +1,16 @@ +const std = @import("std"); +const time = std.time; +const Timer = time.Timer; + +var timer: Timer = undefined; + +pub fn start() void { + timer = Timer.start() catch { + unreachable; + }; +} + +pub fn stop() u64 { + const elapsed: u64 = timer.read(); + return elapsed; +} diff --git a/modules/aoc/src/template.zig b/modules/aoc/src/template.zig new file mode 100644 index 0000000..dbe53e3 --- /dev/null +++ b/modules/aoc/src/template.zig @@ -0,0 +1,29 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const YEAR: u12 = "$YEAR"; +const DAY: u5 = "$DAY"; + +const Allocator = std.mem.Allocator; +const log = std.log; + +fn part1(allocator: Allocator, input: []const u8) anyerror!void { + _ = allocator; + _ = input; + std.debug.print("--- INPUT---\n{s}\n------------\n", .{@embedFile("puzzle")}); +} + +fn part2(allocator: Allocator, input: []const u8) anyerror!void { + _ = allocator; + _ = input; + std.debug.print("--- INPUT---\n{s}\n------------\n", .{@embedFile("puzzle")}); +} + +pub fn main() !void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + + try aoc.runPart(allocator, DAY, .EXAMPLE, part1); + try aoc.runPart(allocator, DAY, .EXAMPLE, part2); +} diff --git a/modules/aoc/src/types.zig b/modules/aoc/src/types.zig new file mode 100644 index 0000000..caac1dc --- /dev/null +++ b/modules/aoc/src/types.zig @@ -0,0 +1,6 @@ +pub const Year = u12; +pub const Day = u5; +pub const PuzzleInput = enum { + EXAMPLE, + PUZZLE, +}; diff --git a/modules/aoc/src/utils.zig b/modules/aoc/src/utils.zig new file mode 100644 index 0000000..e4341d5 --- /dev/null +++ b/modules/aoc/src/utils.zig @@ -0,0 +1,32 @@ +const std = @import("std"); +const zeit = @import("zeit"); + +pub fn blockAskForNext() void { + step: { + var reader_buffer: [10]u8 = undefined; + var reader = std.fs.File.stdin().readerStreaming(&reader_buffer); + std.debug.print("\n\n→ Step: [Enter]", .{}); + var writter_buffer: [10]u8 = undefined; + var writer = std.fs.File.stdout().writerStreaming(&writter_buffer); + _ = reader.interface.streamDelimiter(&writer.interface, '\n') catch return; + _ = reader.interface.takeByte() catch return; + break :step; + } +} + +pub fn getToday() !void { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + const allocator = gpa.allocator(); + + var env = try std.process.getEnvMap(allocator); + defer env.deinit(); + const local = try zeit.local(allocator, &env); + const now = try zeit.instant(.{}); + const now_local = now.in(&local); + const dt = now_local.time(); + std.debug.print("{}", .{dt}); +} + +pub fn getTemplate() []const u8 { + return @embedFile("template.zig"); +} From 2d193aa1329b8a17e0c42c610d89859f576c7ee9 Mon Sep 17 00:00:00 2001 From: Guido Schmidt Date: Thu, 4 Dec 2025 23:27:43 +0100 Subject: [PATCH 02/18] feat(2025/day04): solve part 1 + part 2 --- 2025/src/day04.zig | 97 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 2025/src/day04.zig diff --git a/2025/src/day04.zig b/2025/src/day04.zig new file mode 100644 index 0000000..3040008 --- /dev/null +++ b/2025/src/day04.zig @@ -0,0 +1,97 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const YEAR: u12 = 2025; +const DAY: u5 = 4; + +const Allocator = std.mem.Allocator; +const log = std.log; + +fn findAccessibleRolls(map: []u8, rows: usize, cols: usize) !usize { + var result: usize = 0; + + for (0..cols) |i| { + for (0..rows) |j| { + const x: isize = @intCast(j); + const y: isize = @intCast(i); + var rolls_count: usize = 0; + + for (0..3) |yy| { + inner: for (0..3) |xx| { + const xo = x + @as(isize, @intCast(xx)) - 1; + const yo = y + @as(isize, @intCast(yy)) - 1; + if (xo < 0 or + yo < 0 or + xo > rows - 1 or + yo > cols - 1 or + (xo == x and yo == y)) continue :inner; + const map_value = map[@intCast((xo * @as(isize, @intCast(rows))) + yo)]; + if (map_value == '@' or map_value == 'x') { + rolls_count += 1; + } + } + } + + if (map[@intCast((x * @as(isize, @intCast(rows))) + y)] != '.' and rolls_count < 4) { + map[@intCast((x * @as(isize, @intCast(rows))) + y)] = '.'; + result += 1; + } + } + } + + // --- Print the map to visualise what's going on + // for (0..cols) |_x| { + // for (0..rows) |_y| { + // std.debug.print("{c} ", .{map[(_x * rows) + _y]}); + // } + // std.debug.print("\n", .{}); + // } + + return result; +} + +fn part1(allocator: Allocator, input: []const u8) anyerror!void { + _ = input; + const input_embed = std.mem.trimEnd(u8, @embedFile("puzzle-04"), "\n"); + + var it = std.mem.tokenizeSequence(u8, input_embed, "\n"); + const cols = it.peek().?.len; + const rows = input_embed.len / cols; + std.debug.print("Map size: {d} x {d}\n", .{ cols, rows }); + + const map = try std.mem.replaceOwned(u8, allocator, input_embed, "\n", ""); + defer allocator.free(map); + + const result = try findAccessibleRolls(map, rows, cols); + std.debug.print("Result: {d}\n", .{result}); +} + +fn part2(allocator: Allocator, input: []const u8) anyerror!void { + _ = input; + const input_embed = std.mem.trimEnd(u8, @embedFile("puzzle-04"), "\n"); + + var it = std.mem.tokenizeSequence(u8, input_embed, "\n"); + const cols = it.peek().?.len; + const rows = input_embed.len / cols; + std.debug.print("Map size: {d} x {d}\n", .{ cols, rows }); + + const map = try std.mem.replaceOwned(u8, allocator, input_embed, "\n", ""); + defer allocator.free(map); + + var step_result = try findAccessibleRolls(map, rows, cols); + var summed_result: usize = step_result; + while (step_result > 0) { + step_result = try findAccessibleRolls(map, rows, cols); + summed_result += step_result; + } + std.debug.print("Result: {d}\n", .{summed_result}); +} + +pub fn main() !void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + + try aoc.runPart(allocator, DAY, .EXAMPLE, part1); + try aoc.runPart(allocator, DAY, .EXAMPLE, part2); +} From b46648d2262e2c023317ae274e4a9c1902e55ca1 Mon Sep 17 00:00:00 2001 From: Guido Schmidt Date: Thu, 4 Dec 2025 23:31:38 +0100 Subject: [PATCH 03/18] feat(modules/libs): helper libraries module --- modules/aoc/build.zig.zon | 8 +-- modules/aoc/src/template.zig | 4 +- modules/libs/build.zig | 12 ++++ modules/libs/build.zig.zon | 13 ++++ modules/libs/src/datastructures.zig | 0 modules/libs/src/datastructures/VectorSet.zig | 59 +++++++++++++++++++ modules/libs/src/math.zig | 13 ++++ modules/libs/src/ppm.zig | 36 +++++++++++ modules/libs/src/root.zig | 5 ++ modules/libs/src/svg.zig | 50 ++++++++++++++++ modules/libs/src/term.zig | 25 ++++++++ 11 files changed, 216 insertions(+), 9 deletions(-) create mode 100644 modules/libs/build.zig create mode 100644 modules/libs/build.zig.zon create mode 100644 modules/libs/src/datastructures.zig create mode 100644 modules/libs/src/datastructures/VectorSet.zig create mode 100644 modules/libs/src/math.zig create mode 100644 modules/libs/src/ppm.zig create mode 100644 modules/libs/src/root.zig create mode 100644 modules/libs/src/svg.zig create mode 100644 modules/libs/src/term.zig diff --git a/modules/aoc/build.zig.zon b/modules/aoc/build.zig.zon index c748f3e..32f9a48 100644 --- a/modules/aoc/build.zig.zon +++ b/modules/aoc/build.zig.zon @@ -3,17 +3,11 @@ .fingerprint = 0x808a3f8f28be9351, .version = "0.0.2", .minimum_zig_version = "0.15.2", - .dependencies = .{ - .zeit = .{ - .url = "git+https://github.com/rockorager/zeit#b01d40568edc0b1c1a2ec0692619449a2a39c718", - .hash = "zeit-0.6.0-5I6bk05_AgAUb00ViMy0zAkEf4N38JCSUUrjCW4s-haJ", - }, - }, + .dependencies = .{}, .paths = .{ "README.md", "build.zig", "build.zig.zon", "src", - "modules", }, } diff --git a/modules/aoc/src/template.zig b/modules/aoc/src/template.zig index dbe53e3..92d7aea 100644 --- a/modules/aoc/src/template.zig +++ b/modules/aoc/src/template.zig @@ -10,13 +10,13 @@ const log = std.log; fn part1(allocator: Allocator, input: []const u8) anyerror!void { _ = allocator; _ = input; - std.debug.print("--- INPUT---\n{s}\n------------\n", .{@embedFile("puzzle")}); + std.debug.print("--- INPUT---\n{s}\n------------\n", .{@embedFile("puzzle-$DAY")}); } fn part2(allocator: Allocator, input: []const u8) anyerror!void { _ = allocator; _ = input; - std.debug.print("--- INPUT---\n{s}\n------------\n", .{@embedFile("puzzle")}); + std.debug.print("--- INPUT---\n{s}\n------------\n", .{@embedFile("puzzle-$DAY")}); } pub fn main() !void { diff --git a/modules/libs/build.zig b/modules/libs/build.zig new file mode 100644 index 0000000..132030e --- /dev/null +++ b/modules/libs/build.zig @@ -0,0 +1,12 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + _ = b.addModule("libs", .{ + .root_source_file = b.path("src/root.zig"), + .target = target, + .optimize = optimize, + }); +} diff --git a/modules/libs/build.zig.zon b/modules/libs/build.zig.zon new file mode 100644 index 0000000..75f0117 --- /dev/null +++ b/modules/libs/build.zig.zon @@ -0,0 +1,13 @@ +.{ + .name = .aoc_libs, + .fingerprint = 0x988ff719da89c27e, + .version = "0.0.2", + .minimum_zig_version = "0.15.2", + .dependencies = .{}, + .paths = .{ + "README.md", + "build.zig", + "build.zig.zon", + "src", + }, +} diff --git a/modules/libs/src/datastructures.zig b/modules/libs/src/datastructures.zig new file mode 100644 index 0000000..e69de29 diff --git a/modules/libs/src/datastructures/VectorSet.zig b/modules/libs/src/datastructures/VectorSet.zig new file mode 100644 index 0000000..a283c5f --- /dev/null +++ b/modules/libs/src/datastructures/VectorSet.zig @@ -0,0 +1,59 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const AutoHashMap = std.AutoHashMap; + +pub fn VectorSet(comptime size: usize, comptime T: type) type { + return struct { + const VectorSetHashMap = AutoHashMap(@Vector(size, T), void); + const Iterator = VectorSetHashMap.KeyIterator; + + const Self = @This(); + + hash_map: VectorSetHashMap, + + pub fn init(a: Allocator) VectorSet(size, T) { + return .{ .hash_map = VectorSetHashMap.init(a) }; + } + + pub fn deinit(self: *VectorSet) void { + var it = self.hash_map.keyIterator(); + while (it.next()) |key_ptr| { + self.hash_map.allocator.free(key_ptr.*); + } + } + + pub fn insert(self: *VectorSet(size, T), value: @Vector(size, T)) !void { + const gop = try self.hash_map.getOrPut(value); + if (!gop.found_existing) { + var new_vec: @Vector(size, T) = @splat(0); + inline for (0..size) |i| new_vec[i] = value[i]; + gop.key_ptr.* = new_vec; + } + } + + pub fn remove(self: *VectorSet(size, T), value: @Vector(size, T)) bool { + return self.hash_map.remove(value); + } + + pub fn contains(self: VectorSet(size, T), value: @Vector(size, T)) bool { + return self.hash_map.contains(value); + } + + pub fn getIndex(self: VectorSet(size, T), idx: usize) @Vector(size, T) { + var it = self.hash_map.keyIterator(); + if (idx == 0) return it.next().?.*; + for (0..idx) |_| { + _ = it.next().?; + } + return it.next().?.*; + } + + pub fn count(self: *const VectorSet(size, T)) usize { + return self.hash_map.count(); + } + + pub fn iterator(self: *const VectorSet(size, T)) Iterator { + return self.hash_map.keyIterator(); + } + }; +} diff --git a/modules/libs/src/math.zig b/modules/libs/src/math.zig new file mode 100644 index 0000000..8162675 --- /dev/null +++ b/modules/libs/src/math.zig @@ -0,0 +1,13 @@ +pub fn lcm(comptime T: type, nums: []const T) T { + var r: T = nums[0]; + for(1..nums.len) |i| { + r = nums[i] * r / gcd(T, nums[i], r); + } + return r; +} + +pub fn gcd(comptime T: type, a: T, b: T) T { + if (b == 0) + return a; + return gcd(T, b, @mod(a, b)); +} diff --git a/modules/libs/src/ppm.zig b/modules/libs/src/ppm.zig new file mode 100644 index 0000000..5822203 --- /dev/null +++ b/modules/libs/src/ppm.zig @@ -0,0 +1,36 @@ +const std = @import("std"); +const fs = std.fs; + +var file: fs.File = undefined; +var buf: [32]u8 = undefined; + +pub fn init(file_path: []const u8, width: usize, height: usize) !void { + file = try fs.cwd().createFile(file_path, .{}); + _ = try file.write("P2\n"); + + const ppm_size = try std.fmt.bufPrint(&buf, "{d} {d}\n255", .{ height, width }); + _ = try file.write(ppm_size); +} + +pub fn writeValue(value: []const u8) !void { + _ = try file.write(value); +} + +pub fn nextRow() !void { + _ = try file.write("\n"); +} + +pub fn write(map: *[][]u8) !void { + for(0..map.len) |x| { + const row = map.*[x]; + _ = try file.write("\n"); + for(0..row.len) |y| { + const v = map.*[x][y]; + const g: u8 = if (v == '.') 1 else 255; + const o = try std.fmt.bufPrint(&buf, "{d} ", .{ g }); + _ = try file.write(o); + } + } + try file.sync(); + file.close(); +} diff --git a/modules/libs/src/root.zig b/modules/libs/src/root.zig new file mode 100644 index 0000000..62d28e9 --- /dev/null +++ b/modules/libs/src/root.zig @@ -0,0 +1,5 @@ +pub const VectorSet = @import("datastructures/VectorSet.zig").VectorSet; +pub const math = @import("math.zig"); +pub const ppm = @import("ppm.zig"); +pub const svg = @import("svg.zig"); +pub const term = @import("term.zig"); diff --git a/modules/libs/src/svg.zig b/modules/libs/src/svg.zig new file mode 100644 index 0000000..7b323a3 --- /dev/null +++ b/modules/libs/src/svg.zig @@ -0,0 +1,50 @@ +const std = @import("std"); +const fs = std.fs; + +var file: fs.File = undefined; +var buf: [128]u8 = undefined; + +pub fn init(file_path: []const u8, width: i128, height: i128, min_x: i128, max_x: i128, min_y: i128, max_y: i128) !void { + _ = max_y; + _ = min_y; + _ = max_x; + _ = min_x; + file = try fs.cwd().createFile(file_path, .{}); + const margin_x: i128 = @intFromFloat(@as(f32, @floatFromInt(width)) * 0.1); + _ = margin_x; + const margin_y: i128 = @intFromFloat(@as(f32, @floatFromInt(height)) * 0.1); + _ = margin_y; + // const svg_el = try std.fmt.bufPrint(&buf, "", + // .{ -margin_x, -margin_y, width + 2 * margin_x, height + 2 * margin_y, width, height }); + const svg_el = try std.fmt.bufPrint(&buf, "", + .{ width, height }); + _ = try file.write(svg_el); +} + +pub fn addLine(x1: i128, y1: i128, x2: i128, y2: i128) !void { + const line_el = try std.fmt.bufPrint(&buf, "", .{ x1, y1, x2, y2 }); + _ = try file.write(line_el); +} + +pub fn addCirc(x: i128, y: i128) !void { + const line_el = try std.fmt.bufPrint(&buf, "", .{ x, y, 10 }); + _ = try file.write(line_el); +} + +pub fn startPolygon() !void { + _ = try file.write(""); +} + +pub fn close() !void { + _ = try file.write(""); + file.close(); +} diff --git a/modules/libs/src/term.zig b/modules/libs/src/term.zig new file mode 100644 index 0000000..bb45ab0 --- /dev/null +++ b/modules/libs/src/term.zig @@ -0,0 +1,25 @@ +pub const clear = "\x1B[0m"; +pub const red = "\x1B[31m"; +pub const bg_red = "\x1B[41m"; +pub const green = "\x1B[32m"; +pub const bg_green = "\x1B[42m"; +pub const yellow = "\x1B[33m"; +pub const bg_yellow = "\x1B[43m"; +pub const blue = "\x1B[34m"; +pub const white = "\x1B[37m"; +pub const black = "\x1B[30m"; +pub const bg_white = "\x1B[47m"; +pub const light_blue = "\x1B[36m"; +pub const gray = "\x1B[37m"; +pub const magenta = "\x1B[95m"; +pub const bg_magenta = "\x1B[105m"; +pub const cyan = "\x1B[36m"; +pub const bg_cyan = "\x1B[46m"; +pub const dark_gray = "\x1B[90m"; +pub const idk = "\x1B[38m"; + +pub const clear_screen = "\x1B[2J"; +pub const hide_cursor = "\x1B[?25l"; +pub const yx = "\x1B[{d};{d}H"; + +pub const bg_color = "\x1B[48:5:{d}m"; From 2b9941b03efebb57dfbb390f076d83b45643e026 Mon Sep 17 00:00:00 2001 From: Guido Schmidt Date: Thu, 4 Dec 2025 23:32:52 +0100 Subject: [PATCH 04/18] refactor: delete old aoc and libs folder, moved to modules/ --- .zigversion | 1 - aoc/common.zig | 64 --------------- aoc/input.zig | 109 ------------------------- aoc/root.zig | 5 -- aoc/stopwatch.zig | 16 ---- aoc/template.zig | 25 ------ build.zig | 128 ------------------------------ build.zig.zon | 7 -- libs/datastructures.zig | 1 - libs/datastructures/VectorSet.zig | 59 -------------- libs/math.zig | 13 --- libs/ppm.zig | 36 --------- libs/svg.zig | 50 ------------ libs/term.zig | 25 ------ 14 files changed, 539 deletions(-) delete mode 100644 .zigversion delete mode 100644 aoc/common.zig delete mode 100644 aoc/input.zig delete mode 100644 aoc/root.zig delete mode 100644 aoc/stopwatch.zig delete mode 100644 aoc/template.zig delete mode 100644 build.zig delete mode 100644 build.zig.zon delete mode 100644 libs/datastructures.zig delete mode 100644 libs/datastructures/VectorSet.zig delete mode 100644 libs/math.zig delete mode 100644 libs/ppm.zig delete mode 100644 libs/svg.zig delete mode 100644 libs/term.zig diff --git a/.zigversion b/.zigversion deleted file mode 100644 index 4312e0d..0000000 --- a/.zigversion +++ /dev/null @@ -1 +0,0 @@ -0.15.2 diff --git a/aoc/common.zig b/aoc/common.zig deleted file mode 100644 index fc628ee..0000000 --- a/aoc/common.zig +++ /dev/null @@ -1,64 +0,0 @@ -const std = @import("std"); -const puzzle_input = @import("./input.zig"); -const stopwatch = @import("./stopwatch.zig"); - -const Allocator = std.mem.Allocator; - -pub fn printPart1() void { - std.debug.print("\n########## Part 1 ##########", .{}); -} - -pub fn printPart2() void { - std.debug.print("\n########## Part 2 ##########", .{}); -} - -pub fn printTime(time: u64) void { - const ns = time; - const us: f64 = @floatFromInt(time / std.time.ns_per_us); - const ms: f64 = @floatFromInt(time / std.time.ns_per_ms); - std.debug.print("\n— ā² Running time: {d:3} ms / {d:3} μs / {d} ns\n", .{ ms, us, ns }); -} - -pub const PuzzleInput = enum { EXAMPLE, PUZZLE }; - -pub fn runPart( - allocator: std.mem.Allocator, - comptime year: u16, - comptime day: u8, - input_type: PuzzleInput, - comptime part_fn: fn (allocator: Allocator, input: []const u8) anyerror!void, -) !void { - const input = switch (input_type) { - .PUZZLE => try puzzle_input.getPuzzleInput(allocator, day, year), - .EXAMPLE => try puzzle_input.getExampleInput(allocator, day, year), - }; - stopwatch.start(); - try part_fn(allocator, input); - const time = stopwatch.stop(); - printTime(time); -} - -pub fn runDay( - allocator: std.mem.Allocator, - comptime year: u16, - comptime day: u8, - input_type: PuzzleInput, - comptime part1: fn (allocator: Allocator, input: []const u8) anyerror!void, - comptime part2: fn (allocator: Allocator, input: []const u8) anyerror!void, -) !void { - try runPart(allocator, year, day, input_type, part1); - try runPart(allocator, year, day, input_type, part2); -} - -pub fn blockAskForNext() void { - step: { - var reader_buffer: [10]u8 = undefined; - var reader = std.fs.File.stdin().readerStreaming(&reader_buffer); - std.debug.print("\n\n→ Step: [Enter]", .{}); - var writter_buffer: [10]u8 = undefined; - var writer = std.fs.File.stdout().writerStreaming(&writter_buffer); - _ = reader.interface.streamDelimiter(&writer.interface, '\n') catch return; - _ = reader.interface.takeByte() catch return; - break :step; - } -} diff --git a/aoc/input.zig b/aoc/input.zig deleted file mode 100644 index 1eaa57f..0000000 --- a/aoc/input.zig +++ /dev/null @@ -1,109 +0,0 @@ -const std = @import("std"); -const fs = std.fs; -const http = std.http; - -fn getAdventOfCodeCookieFromEnv(allocator: std.mem.Allocator) !?[]const u8 { - const env_map = try allocator.create(std.process.EnvMap); - env_map.* = try std.process.getEnvMap(allocator); - return env_map.get("AOC_COOKIE"); -} - -pub fn getPuzzleInputFromServer( - allocator: std.mem.Allocator, - year: u16, - day: usize, - file_path: []const u8, -) !void { - if (std.fs.path.dirname(file_path)) |basepath| { - fs.cwd().makeDir(basepath) catch { - std.debug.print("\n{s} already exists. Continue...", .{basepath}); - }; - } - - // Get AOC_COOKIE from environment - const cookie_from_env = try getAdventOfCodeCookieFromEnv(allocator); - if (cookie_from_env == null) { - std.log.err("\nPlease set AOC_COOKIE env variable", .{}); - } - std.debug.print("\nAOC_COOKIE: {s}", .{cookie_from_env.?}); - - var buf: [128]u8 = undefined; - const url = try std.fmt.bufPrint(&buf, "https://adventofcode.com/{d}/day/{d}/input", .{ - year, - day, - }); - - var client = http.Client{ .allocator = allocator }; - defer client.deinit(); - - const output_file = try fs.cwd().createFile(file_path, .{}); - defer output_file.close(); - var buffer: [1]u8 = undefined; - var body_writer = output_file.writer(&buffer); - - var body = std.Io.Writer.Allocating.init(allocator); - defer body.deinit(); - try body.ensureUnusedCapacity(256); - - const req = try client.fetch(.{ - .location = .{ - .url = url, - }, - .extra_headers = &.{ - .{ - .name = "cookie", - .value = cookie_from_env.?, - }, - }, - .response_writer = &body_writer.interface, - }); - - if (req.status == .ok) std.debug.print("Success", .{}); - std.debug.print("\n{s}", .{file_path}); - std.debug.print("\nInput: {s}\n\n", .{try body.toOwnedSlice()}); -} - -pub fn getPuzzleInput(allocator: std.mem.Allocator, day: u8, year: u16) ![]const u8 { - var year_buf: [16]u8 = undefined; - var day_buf: [16]u8 = undefined; - const file_path = try std.fs.path.join(allocator, &.{ - "aoc", - "input", - try std.fmt.bufPrint(&year_buf, "{d}", .{year}), - try std.fmt.bufPrint(&day_buf, "day{d}.txt", .{day}), - }); - - const file = fs.cwd().openFile(file_path, .{}) catch { - try getPuzzleInputFromServer(allocator, year, day, file_path); - const f = try fs.cwd().openFile(file_path, .{}); - const s = try f.stat(); - var buffer: [1]u8 = undefined; - var reader = f.reader(&buffer); - return reader.interface.readAlloc(allocator, s.size); - }; - const stat = try file.stat(); - if (stat.size == 0) { - try getPuzzleInputFromServer(allocator, year, day, file_path); - } - - var buffer: [1]u8 = undefined; - var reader = file.reader(&buffer); - return reader.interface.readAlloc(allocator, stat.size); -} - -pub fn getExampleInput(allocator: std.mem.Allocator, day: u8, year: u16) ![]const u8 { - var day_buf: [16]u8 = undefined; - var year_buf: [16]u8 = undefined; - const file_path = try std.fs.path.join(allocator, &.{ - "aoc", - "input", - try std.fmt.bufPrint(&year_buf, "{d}", .{year}), - "examples", - try std.fmt.bufPrint(&day_buf, "day{d}.txt", .{day}), - }); - const file = try fs.cwd().openFile(file_path, .{}); - const stat = try file.stat(); - var buffer: [1]u8 = undefined; - var reader = file.reader(&buffer); - return reader.interface.readAlloc(allocator, stat.size); -} diff --git a/aoc/root.zig b/aoc/root.zig deleted file mode 100644 index 0faa777..0000000 --- a/aoc/root.zig +++ /dev/null @@ -1,5 +0,0 @@ -const aoc = @import("./common.zig"); - -pub const blockAskForNext = aoc.blockAskForNext; -pub const runPart = aoc.runPart; -pub const runDay = aoc.runDay; diff --git a/aoc/stopwatch.zig b/aoc/stopwatch.zig deleted file mode 100644 index 56db3ae..0000000 --- a/aoc/stopwatch.zig +++ /dev/null @@ -1,16 +0,0 @@ -const std = @import("std"); -const time = std.time; -const Timer = time.Timer; - -var timer: Timer = undefined; - -pub fn start() void { - timer = Timer.start() catch { - unreachable; - }; -} - -pub fn stop() u64 { - const elapsed: u64 = timer.read(); - return elapsed; -} diff --git a/aoc/template.zig b/aoc/template.zig deleted file mode 100644 index f7538da..0000000 --- a/aoc/template.zig +++ /dev/null @@ -1,25 +0,0 @@ -const std = @import("std"); -const aoc = @import("aoc"); - -const DAY: u8 = -1; // @TODO -const Allocator = std.mem.Allocator; -const log = std.log; - -fn part1(allocator: Allocator, input: []const u8) anyerror!void { - _ = allocator; - _ = input; -} - -fn part2(allocator: Allocator, input: []const u8) anyerror!void { - _ = allocator; - _ = input; -} - -pub fn main() !void { - var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - defer arena.deinit(); - const allocator = arena.allocator(); - - try aoc.runPart(allocator, 2024, DAY, .EXAMPLE, part1); - try aoc.runPart(allocator, 2024, DAY, .EXAMPLE, part2); -} diff --git a/build.zig b/build.zig deleted file mode 100644 index 2a868e1..0000000 --- a/build.zig +++ /dev/null @@ -1,128 +0,0 @@ -const std = @import("std"); -const fs = std.fs; -const aoc_input = @import("aoc/input.zig"); - -const YEAR: usize = 2024; - -const BuildTargetResults = struct { - exe: *std.Build.Step.Compile, - tests: *std.Build.Step.Compile, -}; - -fn createBuildTarget( - b: *std.Build, - target: std.Build.ResolvedTarget, - optimize: std.builtin.OptimizeMode, - aoc_module: *std.Build.Module, - libs: *std.StringHashMap(*std.Build.Module), - year: u16, - day: usize, -) !BuildTargetResults { - var src_buf: [32]u8 = undefined; - const source_file = try std.fmt.bufPrint(&src_buf, "{d}/day{d}.zig", .{ year, day }); - - // Check if source file actually exists - _ = std.fs.cwd().openFile(source_file, .{}) catch |err| { - std.debug.print("\n{s} does not exist!", .{source_file}); - return err; - }; - - var name_buf: [32]u8 = undefined; - const name = try std.fmt.bufPrint(&name_buf, "{d}-{d}", .{ year, day }); - const exe = b.addExecutable( - .{ - .name = name, - .root_module = b.createModule( - .{ - .root_source_file = b.path(source_file), - .target = target, - .optimize = optimize, - }, - ), - }, - ); - exe.root_module.addImport("aoc", aoc_module); - - var libs_it = libs.iterator(); - while (libs_it.next()) |lib| { - exe.root_module.addImport(lib.key_ptr.*, lib.value_ptr.*); - } - - const unit_tests = b.addTest(.{ - .root_module = b.createModule( - .{ - .root_source_file = b.path(source_file), - .target = target, - .optimize = optimize, - }, - ), - }); - - b.installArtifact(exe); - - return .{ - .exe = exe, - .tests = unit_tests, - }; -} - -pub fn build(b: *std.Build) !void { - const target = b.standardTargetOptions(.{}); - const optimize = b.standardOptimizeOption(.{}); - - const aoc_module = b.addModule("aoc", .{ .root_source_file = b.path("./aoc/root.zig"), .optimize = optimize, .target = target }); - - var libs = std.StringHashMap(*std.Build.Module).init(b.allocator); - - const lib_names = .{ "ppm", "math", "svg", "term", "datastructures" }; - inline for (0..lib_names.len) |i| { - const lib_name = lib_names[i]; - const module = b.addModule(lib_name, .{ - .root_source_file = b.path("./libs/" ++ lib_name ++ ".zig"), - .optimize = optimize, - .target = target, - }); - try libs.put(lib_name, module); - } - - // This allows the user to pass arguments to the application in the build - // command itself, like this: `zig build run -- arg1 arg2 etc` - if (b.args) |args| { - const day = std.fmt.parseInt(u8, args[0], 10) catch 1; - const res = createBuildTarget(b, target, optimize, aoc_module, &libs, YEAR, day) catch return; - - const run_cmd = b.addRunArtifact(res.exe); - run_cmd.step.dependOn(b.getInstallStep()); - - const run_step = b.step(try std.fmt.allocPrint(b.allocator, "run", .{}), try std.fmt.allocPrint(b.allocator, "Run {d} of {d}", .{ day, YEAR })); - run_step.dependOn(&run_cmd.step); - - // Testing - const run_unit_tests = b.addRunArtifact(res.tests); - const test_step = b.step(try std.fmt.allocPrint(b.allocator, "test", .{}), try std.fmt.allocPrint(b.allocator, "Run test cases for {d} of {d}", .{ day, YEAR })); - test_step.dependOn(&run_unit_tests.step); - return; - } - - for (1..25) |day| { - _ = createBuildTarget(b, target, optimize, aoc_module, &libs, YEAR, day) catch continue; - } - - // Interactive prompt - if (false) { - // // If no argument was given, ask the user which day should be build/run - // std.debug.print("\nWhich day should be build/run [1 - 24]? ", .{}); - // // Ideally we would want to issue more than one read - // // otherwise there is no point in buffering. - // var msg_buf: [4096]u8 = undefined; - // const input = r.readUntilDelimiterOrEof(&msg_buf, '\n') catch ""; - // if (input) |input_txt| { - // const day = std.fmt.parseInt(u8, input_txt, 10) catch { - // std.debug.print("\nPlease give a number between 1 and 24\n\n", .{}); - // return; - // }; - // std.debug.print("Selected day {d}\n~ Compiling...\n", .{day}); - // try createBuildTarget(b, target, optimize, aoc_module, &libs, YEAR, day); - // } - } -} diff --git a/build.zig.zon b/build.zig.zon deleted file mode 100644 index 54b8a26..0000000 --- a/build.zig.zon +++ /dev/null @@ -1,7 +0,0 @@ -.{ - .name = .aoc, - .fingerprint = 0x808a3f8f28be9351, - .version = "0.0.1", - .dependencies = .{}, - .paths = .{"./aoc"}, -} diff --git a/libs/datastructures.zig b/libs/datastructures.zig deleted file mode 100644 index 457e115..0000000 --- a/libs/datastructures.zig +++ /dev/null @@ -1 +0,0 @@ -pub const VectorSet = @import("./datastructures/VectorSet.zig").VectorSet; diff --git a/libs/datastructures/VectorSet.zig b/libs/datastructures/VectorSet.zig deleted file mode 100644 index a283c5f..0000000 --- a/libs/datastructures/VectorSet.zig +++ /dev/null @@ -1,59 +0,0 @@ -const std = @import("std"); -const Allocator = std.mem.Allocator; -const AutoHashMap = std.AutoHashMap; - -pub fn VectorSet(comptime size: usize, comptime T: type) type { - return struct { - const VectorSetHashMap = AutoHashMap(@Vector(size, T), void); - const Iterator = VectorSetHashMap.KeyIterator; - - const Self = @This(); - - hash_map: VectorSetHashMap, - - pub fn init(a: Allocator) VectorSet(size, T) { - return .{ .hash_map = VectorSetHashMap.init(a) }; - } - - pub fn deinit(self: *VectorSet) void { - var it = self.hash_map.keyIterator(); - while (it.next()) |key_ptr| { - self.hash_map.allocator.free(key_ptr.*); - } - } - - pub fn insert(self: *VectorSet(size, T), value: @Vector(size, T)) !void { - const gop = try self.hash_map.getOrPut(value); - if (!gop.found_existing) { - var new_vec: @Vector(size, T) = @splat(0); - inline for (0..size) |i| new_vec[i] = value[i]; - gop.key_ptr.* = new_vec; - } - } - - pub fn remove(self: *VectorSet(size, T), value: @Vector(size, T)) bool { - return self.hash_map.remove(value); - } - - pub fn contains(self: VectorSet(size, T), value: @Vector(size, T)) bool { - return self.hash_map.contains(value); - } - - pub fn getIndex(self: VectorSet(size, T), idx: usize) @Vector(size, T) { - var it = self.hash_map.keyIterator(); - if (idx == 0) return it.next().?.*; - for (0..idx) |_| { - _ = it.next().?; - } - return it.next().?.*; - } - - pub fn count(self: *const VectorSet(size, T)) usize { - return self.hash_map.count(); - } - - pub fn iterator(self: *const VectorSet(size, T)) Iterator { - return self.hash_map.keyIterator(); - } - }; -} diff --git a/libs/math.zig b/libs/math.zig deleted file mode 100644 index 8162675..0000000 --- a/libs/math.zig +++ /dev/null @@ -1,13 +0,0 @@ -pub fn lcm(comptime T: type, nums: []const T) T { - var r: T = nums[0]; - for(1..nums.len) |i| { - r = nums[i] * r / gcd(T, nums[i], r); - } - return r; -} - -pub fn gcd(comptime T: type, a: T, b: T) T { - if (b == 0) - return a; - return gcd(T, b, @mod(a, b)); -} diff --git a/libs/ppm.zig b/libs/ppm.zig deleted file mode 100644 index 5822203..0000000 --- a/libs/ppm.zig +++ /dev/null @@ -1,36 +0,0 @@ -const std = @import("std"); -const fs = std.fs; - -var file: fs.File = undefined; -var buf: [32]u8 = undefined; - -pub fn init(file_path: []const u8, width: usize, height: usize) !void { - file = try fs.cwd().createFile(file_path, .{}); - _ = try file.write("P2\n"); - - const ppm_size = try std.fmt.bufPrint(&buf, "{d} {d}\n255", .{ height, width }); - _ = try file.write(ppm_size); -} - -pub fn writeValue(value: []const u8) !void { - _ = try file.write(value); -} - -pub fn nextRow() !void { - _ = try file.write("\n"); -} - -pub fn write(map: *[][]u8) !void { - for(0..map.len) |x| { - const row = map.*[x]; - _ = try file.write("\n"); - for(0..row.len) |y| { - const v = map.*[x][y]; - const g: u8 = if (v == '.') 1 else 255; - const o = try std.fmt.bufPrint(&buf, "{d} ", .{ g }); - _ = try file.write(o); - } - } - try file.sync(); - file.close(); -} diff --git a/libs/svg.zig b/libs/svg.zig deleted file mode 100644 index 7b323a3..0000000 --- a/libs/svg.zig +++ /dev/null @@ -1,50 +0,0 @@ -const std = @import("std"); -const fs = std.fs; - -var file: fs.File = undefined; -var buf: [128]u8 = undefined; - -pub fn init(file_path: []const u8, width: i128, height: i128, min_x: i128, max_x: i128, min_y: i128, max_y: i128) !void { - _ = max_y; - _ = min_y; - _ = max_x; - _ = min_x; - file = try fs.cwd().createFile(file_path, .{}); - const margin_x: i128 = @intFromFloat(@as(f32, @floatFromInt(width)) * 0.1); - _ = margin_x; - const margin_y: i128 = @intFromFloat(@as(f32, @floatFromInt(height)) * 0.1); - _ = margin_y; - // const svg_el = try std.fmt.bufPrint(&buf, "", - // .{ -margin_x, -margin_y, width + 2 * margin_x, height + 2 * margin_y, width, height }); - const svg_el = try std.fmt.bufPrint(&buf, "", - .{ width, height }); - _ = try file.write(svg_el); -} - -pub fn addLine(x1: i128, y1: i128, x2: i128, y2: i128) !void { - const line_el = try std.fmt.bufPrint(&buf, "", .{ x1, y1, x2, y2 }); - _ = try file.write(line_el); -} - -pub fn addCirc(x: i128, y: i128) !void { - const line_el = try std.fmt.bufPrint(&buf, "", .{ x, y, 10 }); - _ = try file.write(line_el); -} - -pub fn startPolygon() !void { - _ = try file.write(""); -} - -pub fn close() !void { - _ = try file.write(""); - file.close(); -} diff --git a/libs/term.zig b/libs/term.zig deleted file mode 100644 index bb45ab0..0000000 --- a/libs/term.zig +++ /dev/null @@ -1,25 +0,0 @@ -pub const clear = "\x1B[0m"; -pub const red = "\x1B[31m"; -pub const bg_red = "\x1B[41m"; -pub const green = "\x1B[32m"; -pub const bg_green = "\x1B[42m"; -pub const yellow = "\x1B[33m"; -pub const bg_yellow = "\x1B[43m"; -pub const blue = "\x1B[34m"; -pub const white = "\x1B[37m"; -pub const black = "\x1B[30m"; -pub const bg_white = "\x1B[47m"; -pub const light_blue = "\x1B[36m"; -pub const gray = "\x1B[37m"; -pub const magenta = "\x1B[95m"; -pub const bg_magenta = "\x1B[105m"; -pub const cyan = "\x1B[36m"; -pub const bg_cyan = "\x1B[46m"; -pub const dark_gray = "\x1B[90m"; -pub const idk = "\x1B[38m"; - -pub const clear_screen = "\x1B[2J"; -pub const hide_cursor = "\x1B[?25l"; -pub const yx = "\x1B[{d};{d}H"; - -pub const bg_color = "\x1B[48:5:{d}m"; From 2e93aea0c3f9fcef9dfd92b439a08329c398b0c3 Mon Sep 17 00:00:00 2001 From: Guido Schmidt Date: Thu, 4 Dec 2025 23:35:08 +0100 Subject: [PATCH 05/18] refactor(2023|2024): move src files of recent years --- 2023/{SUMMARY.md => README.md} | 0 2023/{ => src}/day1.zig | 0 2023/{ => src}/day10.zig | 0 2023/{ => src}/day11.zig | 0 2023/{ => src}/day12.zig | 0 2023/{ => src}/day12_old.zig | 0 2023/{ => src}/day13.zig | 0 2023/{ => src}/day14.zig | 0 2023/{ => src}/day15.zig | 0 2023/{ => src}/day16.zig | 0 2023/{ => src}/day17.zig | 0 2023/{ => src}/day18.zig | 0 2023/{ => src}/day19.zig | 0 2023/{ => src}/day2.zig | 0 2023/{ => src}/day20.zig | 0 2023/{ => src}/day21.zig | 0 2023/{ => src}/day22.zig | 0 2023/{ => src}/day23.zig | 0 2023/{ => src}/day24.zig | 0 2023/{ => src}/day25.zig | 0 2023/{ => src}/day3.zig | 0 2023/{ => src}/day4.zig | 0 2023/{ => src}/day5.zig | 0 2023/{ => src}/day6.zig | 0 2023/{ => src}/day7.zig | 0 2023/{ => src}/day8.zig | 0 2023/{ => src}/day9.zig | 0 2024/{SUMMARY.md => README.md} | 0 2024/{ => src}/Graph.zig | 0 2024/{ => src}/day1.zig | 0 2024/{ => src}/day10.zig | 0 2024/{ => src}/day11.py | 0 2024/{ => src}/day11.zig | 0 2024/{ => src}/day12-ext.py | 0 2024/{ => src}/day12.py | 0 2024/{ => src}/day12.zig | 0 2024/{ => src}/day13.py | 0 2024/{ => src}/day13.zig | 0 2024/{ => src}/day14.zig | 0 2024/{ => src}/day15.zig | 0 2024/{ => src}/day16.zig | 0 2024/{ => src}/day17.zig | 0 2024/{ => src}/day18.zig | 0 2024/{ => src}/day19.zig | 0 2024/{ => src}/day2.zig | 0 2024/{ => src}/day20.zig | 0 2024/{ => src}/day21.old.zig | 0 2024/{ => src}/day21.zig | 0 2024/{ => src}/day22.zig | 0 2024/{ => src}/day23.py | 0 2024/{ => src}/day23.zig | 0 2024/{ => src}/day24.zig | 0 2024/{ => src}/day25.zig | 0 2024/{ => src}/day3.zig | 0 2024/{ => src}/day4.zig | 0 2024/{ => src}/day5.zig | 0 2024/{ => src}/day6.zig | 0 2024/{ => src}/day7.zig | 0 2024/{ => src}/day8.zig | 0 2024/{ => src}/day9.py | 0 2024/{ => src}/day9.zig | 0 61 files changed, 0 insertions(+), 0 deletions(-) rename 2023/{SUMMARY.md => README.md} (100%) rename 2023/{ => src}/day1.zig (100%) rename 2023/{ => src}/day10.zig (100%) rename 2023/{ => src}/day11.zig (100%) rename 2023/{ => src}/day12.zig (100%) rename 2023/{ => src}/day12_old.zig (100%) rename 2023/{ => src}/day13.zig (100%) rename 2023/{ => src}/day14.zig (100%) rename 2023/{ => src}/day15.zig (100%) rename 2023/{ => src}/day16.zig (100%) rename 2023/{ => src}/day17.zig (100%) rename 2023/{ => src}/day18.zig (100%) rename 2023/{ => src}/day19.zig (100%) rename 2023/{ => src}/day2.zig (100%) rename 2023/{ => src}/day20.zig (100%) rename 2023/{ => src}/day21.zig (100%) rename 2023/{ => src}/day22.zig (100%) rename 2023/{ => src}/day23.zig (100%) rename 2023/{ => src}/day24.zig (100%) rename 2023/{ => src}/day25.zig (100%) rename 2023/{ => src}/day3.zig (100%) rename 2023/{ => src}/day4.zig (100%) rename 2023/{ => src}/day5.zig (100%) rename 2023/{ => src}/day6.zig (100%) rename 2023/{ => src}/day7.zig (100%) rename 2023/{ => src}/day8.zig (100%) rename 2023/{ => src}/day9.zig (100%) rename 2024/{SUMMARY.md => README.md} (100%) rename 2024/{ => src}/Graph.zig (100%) rename 2024/{ => src}/day1.zig (100%) rename 2024/{ => src}/day10.zig (100%) rename 2024/{ => src}/day11.py (100%) rename 2024/{ => src}/day11.zig (100%) rename 2024/{ => src}/day12-ext.py (100%) rename 2024/{ => src}/day12.py (100%) rename 2024/{ => src}/day12.zig (100%) rename 2024/{ => src}/day13.py (100%) rename 2024/{ => src}/day13.zig (100%) rename 2024/{ => src}/day14.zig (100%) rename 2024/{ => src}/day15.zig (100%) rename 2024/{ => src}/day16.zig (100%) rename 2024/{ => src}/day17.zig (100%) rename 2024/{ => src}/day18.zig (100%) rename 2024/{ => src}/day19.zig (100%) rename 2024/{ => src}/day2.zig (100%) rename 2024/{ => src}/day20.zig (100%) rename 2024/{ => src}/day21.old.zig (100%) rename 2024/{ => src}/day21.zig (100%) rename 2024/{ => src}/day22.zig (100%) rename 2024/{ => src}/day23.py (100%) rename 2024/{ => src}/day23.zig (100%) rename 2024/{ => src}/day24.zig (100%) rename 2024/{ => src}/day25.zig (100%) rename 2024/{ => src}/day3.zig (100%) rename 2024/{ => src}/day4.zig (100%) rename 2024/{ => src}/day5.zig (100%) rename 2024/{ => src}/day6.zig (100%) rename 2024/{ => src}/day7.zig (100%) rename 2024/{ => src}/day8.zig (100%) rename 2024/{ => src}/day9.py (100%) rename 2024/{ => src}/day9.zig (100%) diff --git a/2023/SUMMARY.md b/2023/README.md similarity index 100% rename from 2023/SUMMARY.md rename to 2023/README.md diff --git a/2023/day1.zig b/2023/src/day1.zig similarity index 100% rename from 2023/day1.zig rename to 2023/src/day1.zig diff --git a/2023/day10.zig b/2023/src/day10.zig similarity index 100% rename from 2023/day10.zig rename to 2023/src/day10.zig diff --git a/2023/day11.zig b/2023/src/day11.zig similarity index 100% rename from 2023/day11.zig rename to 2023/src/day11.zig diff --git a/2023/day12.zig b/2023/src/day12.zig similarity index 100% rename from 2023/day12.zig rename to 2023/src/day12.zig diff --git a/2023/day12_old.zig b/2023/src/day12_old.zig similarity index 100% rename from 2023/day12_old.zig rename to 2023/src/day12_old.zig diff --git a/2023/day13.zig b/2023/src/day13.zig similarity index 100% rename from 2023/day13.zig rename to 2023/src/day13.zig diff --git a/2023/day14.zig b/2023/src/day14.zig similarity index 100% rename from 2023/day14.zig rename to 2023/src/day14.zig diff --git a/2023/day15.zig b/2023/src/day15.zig similarity index 100% rename from 2023/day15.zig rename to 2023/src/day15.zig diff --git a/2023/day16.zig b/2023/src/day16.zig similarity index 100% rename from 2023/day16.zig rename to 2023/src/day16.zig diff --git a/2023/day17.zig b/2023/src/day17.zig similarity index 100% rename from 2023/day17.zig rename to 2023/src/day17.zig diff --git a/2023/day18.zig b/2023/src/day18.zig similarity index 100% rename from 2023/day18.zig rename to 2023/src/day18.zig diff --git a/2023/day19.zig b/2023/src/day19.zig similarity index 100% rename from 2023/day19.zig rename to 2023/src/day19.zig diff --git a/2023/day2.zig b/2023/src/day2.zig similarity index 100% rename from 2023/day2.zig rename to 2023/src/day2.zig diff --git a/2023/day20.zig b/2023/src/day20.zig similarity index 100% rename from 2023/day20.zig rename to 2023/src/day20.zig diff --git a/2023/day21.zig b/2023/src/day21.zig similarity index 100% rename from 2023/day21.zig rename to 2023/src/day21.zig diff --git a/2023/day22.zig b/2023/src/day22.zig similarity index 100% rename from 2023/day22.zig rename to 2023/src/day22.zig diff --git a/2023/day23.zig b/2023/src/day23.zig similarity index 100% rename from 2023/day23.zig rename to 2023/src/day23.zig diff --git a/2023/day24.zig b/2023/src/day24.zig similarity index 100% rename from 2023/day24.zig rename to 2023/src/day24.zig diff --git a/2023/day25.zig b/2023/src/day25.zig similarity index 100% rename from 2023/day25.zig rename to 2023/src/day25.zig diff --git a/2023/day3.zig b/2023/src/day3.zig similarity index 100% rename from 2023/day3.zig rename to 2023/src/day3.zig diff --git a/2023/day4.zig b/2023/src/day4.zig similarity index 100% rename from 2023/day4.zig rename to 2023/src/day4.zig diff --git a/2023/day5.zig b/2023/src/day5.zig similarity index 100% rename from 2023/day5.zig rename to 2023/src/day5.zig diff --git a/2023/day6.zig b/2023/src/day6.zig similarity index 100% rename from 2023/day6.zig rename to 2023/src/day6.zig diff --git a/2023/day7.zig b/2023/src/day7.zig similarity index 100% rename from 2023/day7.zig rename to 2023/src/day7.zig diff --git a/2023/day8.zig b/2023/src/day8.zig similarity index 100% rename from 2023/day8.zig rename to 2023/src/day8.zig diff --git a/2023/day9.zig b/2023/src/day9.zig similarity index 100% rename from 2023/day9.zig rename to 2023/src/day9.zig diff --git a/2024/SUMMARY.md b/2024/README.md similarity index 100% rename from 2024/SUMMARY.md rename to 2024/README.md diff --git a/2024/Graph.zig b/2024/src/Graph.zig similarity index 100% rename from 2024/Graph.zig rename to 2024/src/Graph.zig diff --git a/2024/day1.zig b/2024/src/day1.zig similarity index 100% rename from 2024/day1.zig rename to 2024/src/day1.zig diff --git a/2024/day10.zig b/2024/src/day10.zig similarity index 100% rename from 2024/day10.zig rename to 2024/src/day10.zig diff --git a/2024/day11.py b/2024/src/day11.py similarity index 100% rename from 2024/day11.py rename to 2024/src/day11.py diff --git a/2024/day11.zig b/2024/src/day11.zig similarity index 100% rename from 2024/day11.zig rename to 2024/src/day11.zig diff --git a/2024/day12-ext.py b/2024/src/day12-ext.py similarity index 100% rename from 2024/day12-ext.py rename to 2024/src/day12-ext.py diff --git a/2024/day12.py b/2024/src/day12.py similarity index 100% rename from 2024/day12.py rename to 2024/src/day12.py diff --git a/2024/day12.zig b/2024/src/day12.zig similarity index 100% rename from 2024/day12.zig rename to 2024/src/day12.zig diff --git a/2024/day13.py b/2024/src/day13.py similarity index 100% rename from 2024/day13.py rename to 2024/src/day13.py diff --git a/2024/day13.zig b/2024/src/day13.zig similarity index 100% rename from 2024/day13.zig rename to 2024/src/day13.zig diff --git a/2024/day14.zig b/2024/src/day14.zig similarity index 100% rename from 2024/day14.zig rename to 2024/src/day14.zig diff --git a/2024/day15.zig b/2024/src/day15.zig similarity index 100% rename from 2024/day15.zig rename to 2024/src/day15.zig diff --git a/2024/day16.zig b/2024/src/day16.zig similarity index 100% rename from 2024/day16.zig rename to 2024/src/day16.zig diff --git a/2024/day17.zig b/2024/src/day17.zig similarity index 100% rename from 2024/day17.zig rename to 2024/src/day17.zig diff --git a/2024/day18.zig b/2024/src/day18.zig similarity index 100% rename from 2024/day18.zig rename to 2024/src/day18.zig diff --git a/2024/day19.zig b/2024/src/day19.zig similarity index 100% rename from 2024/day19.zig rename to 2024/src/day19.zig diff --git a/2024/day2.zig b/2024/src/day2.zig similarity index 100% rename from 2024/day2.zig rename to 2024/src/day2.zig diff --git a/2024/day20.zig b/2024/src/day20.zig similarity index 100% rename from 2024/day20.zig rename to 2024/src/day20.zig diff --git a/2024/day21.old.zig b/2024/src/day21.old.zig similarity index 100% rename from 2024/day21.old.zig rename to 2024/src/day21.old.zig diff --git a/2024/day21.zig b/2024/src/day21.zig similarity index 100% rename from 2024/day21.zig rename to 2024/src/day21.zig diff --git a/2024/day22.zig b/2024/src/day22.zig similarity index 100% rename from 2024/day22.zig rename to 2024/src/day22.zig diff --git a/2024/day23.py b/2024/src/day23.py similarity index 100% rename from 2024/day23.py rename to 2024/src/day23.py diff --git a/2024/day23.zig b/2024/src/day23.zig similarity index 100% rename from 2024/day23.zig rename to 2024/src/day23.zig diff --git a/2024/day24.zig b/2024/src/day24.zig similarity index 100% rename from 2024/day24.zig rename to 2024/src/day24.zig diff --git a/2024/day25.zig b/2024/src/day25.zig similarity index 100% rename from 2024/day25.zig rename to 2024/src/day25.zig diff --git a/2024/day3.zig b/2024/src/day3.zig similarity index 100% rename from 2024/day3.zig rename to 2024/src/day3.zig diff --git a/2024/day4.zig b/2024/src/day4.zig similarity index 100% rename from 2024/day4.zig rename to 2024/src/day4.zig diff --git a/2024/day5.zig b/2024/src/day5.zig similarity index 100% rename from 2024/day5.zig rename to 2024/src/day5.zig diff --git a/2024/day6.zig b/2024/src/day6.zig similarity index 100% rename from 2024/day6.zig rename to 2024/src/day6.zig diff --git a/2024/day7.zig b/2024/src/day7.zig similarity index 100% rename from 2024/day7.zig rename to 2024/src/day7.zig diff --git a/2024/day8.zig b/2024/src/day8.zig similarity index 100% rename from 2024/day8.zig rename to 2024/src/day8.zig diff --git a/2024/day9.py b/2024/src/day9.py similarity index 100% rename from 2024/day9.py rename to 2024/src/day9.py diff --git a/2024/day9.zig b/2024/src/day9.zig similarity index 100% rename from 2024/day9.zig rename to 2024/src/day9.zig From 72caaf31d733d1c420cf2b2df931dd922f5c4c81 Mon Sep 17 00:00:00 2001 From: Guido Schmidt Date: Thu, 4 Dec 2025 23:36:51 +0100 Subject: [PATCH 06/18] =?UTF-8?q?feat(2025):=20docs(SUMMARY=20=E2=86=92=20?= =?UTF-8?q?README)=20+=20add=20libs=20module?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 2025/{SUMMARY.md => README.md} | 13 +++++++++++ 2025/build.zig | 42 +++++++++++++++++++++++++++++++++- 2025/build.zig.zon | 1 + 2025/src/day04.zig | 18 ++++++++++----- 4 files changed, 67 insertions(+), 7 deletions(-) rename 2025/{SUMMARY.md => README.md} (67%) diff --git a/2025/SUMMARY.md b/2025/README.md similarity index 67% rename from 2025/SUMMARY.md rename to 2025/README.md index cb3edf4..cf83b4f 100644 --- a/2025/SUMMARY.md +++ b/2025/README.md @@ -14,3 +14,16 @@ Somehow tried to be smart and calculate the amount of rotations first with: ```zig const rotation_count = @abs(@divTrunc(dial, change)); ``` + + + +# Day 02 + + +# Day 03 +- Order is important + + +# Day 04 +- Find `rows` and `columns` size upfront. Ensure to trim! +- Index the one-dimensional buffer with index `(x * rows) + y` diff --git a/2025/build.zig b/2025/build.zig index 7f0b157..5da17e1 100644 --- a/2025/build.zig +++ b/2025/build.zig @@ -28,8 +28,17 @@ pub fn build(b: *std.Build) !void { defer b.allocator.free(arg_year); const arg_day = try std.fmt.allocPrint(b.allocator, "{d}", .{DAY}); defer b.allocator.free(arg_day); + const puzzle_input_filename = try std.fmt.allocPrint(b.allocator, "day{d:0>2}", .{DAY}); + defer b.allocator.free(puzzle_input_filename); + const arg_puzzle_path = try std.fs.path.join(b.allocator, &.{ + "input", + "puzzle", + puzzle_input_filename, + }); + defer b.allocator.free(arg_puzzle_path); cmd.addArg(arg_year); cmd.addArg(arg_day); + cmd.addArg(arg_puzzle_path); const captured_output = cmd.captureStdOut(); // use this as anonymous import const src_name = try std.fmt.allocPrint(b.allocator, "day{d:0>2}.zig", .{DAY}); @@ -59,13 +68,44 @@ pub fn build(b: *std.Build) !void { }); b.installArtifact(exe); - exe.root_module.addAnonymousImport("puzzle", .{ .root_source_file = captured_output }); + const puzzle_input_name = try std.fmt.allocPrint(b.allocator, "puzzle-{d:0>2}", .{DAY}); + defer b.allocator.free(puzzle_input_name); + exe.root_module.addAnonymousImport(puzzle_input_name, .{ + .root_source_file = captured_output, + }); + const expample_input_name = try std.fmt.allocPrint(b.allocator, "example-{d:0>2}", .{DAY}); + defer b.allocator.free(expample_input_name); + const expample_input_file_name = try std.fmt.allocPrint(b.allocator, "day{d:0>2}", .{DAY}); + defer b.allocator.free(expample_input_name); + const example_input_file_path = try std.fs.path.join(b.allocator, &.{ "input", "example", expample_input_file_name }); + defer b.allocator.free(example_input_file_path); + exe.root_module.addAnonymousImport(expample_input_name, .{ + .root_source_file = b.path(example_input_file_path), + }); exe.root_module.addImport("aoc", dep_aoc.module("aoc")); + const dep_libs = b.dependency("libs", .{}); + exe.root_module.addImport("libs", dep_libs.module("libs")); + const run_cmd = b.addRunArtifact(exe); run_cmd.step.dependOn(b.getInstallStep()); const run_step = b.step("run", "Start the program"); run_step.dependOn(&run_cmd.step); + + // Tests + const tests = b.addTest(.{ + .root_module = b.createModule(.{ + .root_source_file = b.path(src_path), + .target = target, + .optimize = optimize, + }), + }); + tests.root_module.addAnonymousImport(expample_input_name, .{ + .root_source_file = b.path(example_input_file_path), + }); + const run_tests = b.addRunArtifact(tests); + const test_step = b.step("test", "Run tests"); + test_step.dependOn(&run_tests.step); } diff --git a/2025/build.zig.zon b/2025/build.zig.zon index 347fa36..12ac06a 100644 --- a/2025/build.zig.zon +++ b/2025/build.zig.zon @@ -5,6 +5,7 @@ .minimum_zig_version = "0.15.2", .dependencies = .{ .aoc = .{ .path = "../modules/aoc" }, + .libs = .{ .path = "../modules/libs" }, }, .paths = .{ "README.md", diff --git a/2025/src/day04.zig b/2025/src/day04.zig index 3040008..816004e 100644 --- a/2025/src/day04.zig +++ b/2025/src/day04.zig @@ -1,5 +1,6 @@ const std = @import("std"); const aoc = @import("aoc"); +const term = @import("libs").term; const YEAR: u12 = 2025; const DAY: u5 = 4; @@ -40,12 +41,17 @@ fn findAccessibleRolls(map: []u8, rows: usize, cols: usize) !usize { } // --- Print the map to visualise what's going on - // for (0..cols) |_x| { - // for (0..rows) |_y| { - // std.debug.print("{c} ", .{map[(_x * rows) + _y]}); - // } - // std.debug.print("\n", .{}); - // } + for (0..cols) |_x| { + for (0..rows) |_y| { + const m = map[(_x * rows) + _y]; + std.debug.print("{s}{c}{s} ", .{ + if (m == '@') term.red else term.clear, + m, + term.clear, + }); + } + std.debug.print("\n", .{}); + } return result; } From efcfab6d539c604501e8ad79015078a2cfc3d146 Mon Sep 17 00:00:00 2001 From: Guido Schmidt Date: Sun, 7 Dec 2025 22:25:40 +0100 Subject: [PATCH 07/18] feat(2025/day03): solve part 1 + 2 --- 2025/src/day03.zig | 119 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 2025/src/day03.zig diff --git a/2025/src/day03.zig b/2025/src/day03.zig new file mode 100644 index 0000000..8118a4f --- /dev/null +++ b/2025/src/day03.zig @@ -0,0 +1,119 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const expect = std.testing.expect; + +const YEAR: u12 = 2025; +const DAY: u5 = 3; + +const Allocator = std.mem.Allocator; +const log = std.log; + +fn part1(allocator: Allocator) anyerror!void { + _ = allocator; + const input_embed = @embedFile("puzzle-03"); + var sum: u32 = 0; + + var reader: std.Io.Reader = .fixed(input_embed); + while (try reader.takeDelimiter('\n')) |line| { + var largest: u8 = 0; + + for (0..line.len) |i| { + const start_digit = try std.fmt.charToDigit(line[i], 10); + for (i + 1..line.len) |j| { + const end_digit = try std.fmt.charToDigit(line[j], 10); + const combination = start_digit * 10 + end_digit; + if (combination > largest) largest = combination; + } + } + sum += largest; + } + std.debug.print("Result: {d}\n", .{sum}); +} + +fn part2(allocator: Allocator) anyerror!void { + _ = allocator; + var total_joltage: u128 = 0; + + const input = @embedFile("puzzle-03"); + var reader: std.Io.Reader = .fixed(input); + + const battery_count = 12; + while (try reader.takeDelimiter('\n')) |line| { + const contained_number = try findLargestContainedNumber( + @TypeOf(total_joltage), + line, + battery_count, + ); + total_joltage += contained_number; + } + + std.debug.print("Result: {d}\n", .{total_joltage}); +} + +fn findLargestContainedNumber(comptime T: type, line: []const u8, battery_count: usize) !T { + var largest_contained_number: T = 0; + var start_idx: usize = 0; + var search_space = line[start_idx .. line.len - (battery_count - 1)]; + var found_digit_count: usize = 0; + for (0..battery_count) |e| { + var max: u8 = try std.fmt.charToDigit(search_space[0], 10); + var max_idx: usize = 0; + for (search_space[0..], 0..) |c, i| { + if (try std.fmt.charToDigit(c, 10) > max) { + max = c - '0'; + max_idx = i; + } + } + + largest_contained_number += max * std.math.pow(T, 10, (battery_count - 1) - e); + + found_digit_count += 1; + start_idx += max_idx + 1; + const upper_bound = @min( + line.len, + @max( + (line.len - (battery_count -| 1 -| found_digit_count)), + start_idx + 1, + ), + ); + search_space = line[start_idx..upper_bound]; + // std.debug.print(" > max: {d} [{d}]\n", .{ max, max_idx }); + } + return largest_contained_number; +} + +pub fn main() !void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + + try aoc.runPart(allocator, part1); + try aoc.runPart(allocator, part2); +} + +test "example-part2" { + try expect(987654321111 == try findLargestContainedNumber(u32, "987654321111111", 12)); + try expect(811111111119 == try findLargestContainedNumber(u32, "811111111111119", 12)); + try expect(434234234278 == try findLargestContainedNumber(u32, "234234234234278", 12)); + try expect(888911112111 == try findLargestContainedNumber(u32, "818181911112111", 12)); + + try expect(919 == try findLargestContainedNumber(u32, "9119", 3)); + try expect(123 == try findLargestContainedNumber(u32, "111123", 3)); + try expect(923 == try findLargestContainedNumber(u32, "234789123", 3)); + try expect(11145 == try findLargestContainedNumber(u32, "1111145", 5)); + try expect(99919 == try findLargestContainedNumber(u32, "19191919", 5)); + + try expect(99919 == try findLargestContainedNumber(u32, "19191919", 5)); + + const input_example = @embedFile("example-03"); + var result: u128 = 0; + var reader: std.Io.Reader = .fixed(input_example); + while (try reader.takeDelimiter('\n')) |line| { + const number = try findLargestContainedNumber(u32, line, 12); + result += number; + } + try expect(result == 3121910778619); + + _ = try findLargestContainedNumber(u128, "2753445676625843555534776876555247667428557664243735457776754553427876646616644267454232337424744677", 12); +} From 40e7c2b528f0ec36230ccbf21782bd882125a9de Mon Sep 17 00:00:00 2001 From: Guido Schmidt Date: Tue, 9 Dec 2025 12:20:52 +0100 Subject: [PATCH 08/18] feat(2025): solve part 6 + starting point for, 7, 8, 9 --- 2025/.gitignore | 2 +- 2025/build.zig | 62 +++++--- 2025/src/day01.zig | 41 ++--- 2025/src/day02.zig | 95 ++++++++++++ 2025/src/day05.zig | 79 ++++++++++ 2025/src/day06.zig | 206 +++++++++++++++++++++++++ 2025/src/day07.zig | 77 +++++++++ 2025/src/day08.zig | 104 +++++++++++++ 2025/src/day09.zig | 28 ++++ modules/aoc/build.zig | 24 ++- modules/aoc/src/common.zig | 22 +-- modules/aoc/src/fetch-puzzle-input.zig | 51 +++++- modules/aoc/src/root.zig | 1 + modules/aoc/src/template.zig | 15 +- modules/aoc/src/types.zig | 6 +- modules/aoc/src/utils.zig | 15 +- 16 files changed, 722 insertions(+), 106 deletions(-) create mode 100644 2025/src/day02.zig create mode 100644 2025/src/day05.zig create mode 100644 2025/src/day06.zig create mode 100644 2025/src/day07.zig create mode 100644 2025/src/day08.zig create mode 100644 2025/src/day09.zig diff --git a/2025/.gitignore b/2025/.gitignore index b08e799..49ac128 100644 --- a/2025/.gitignore +++ b/2025/.gitignore @@ -1,2 +1,2 @@ # Avoid commiting puzzle inputs -**/*.txt +input/**/* diff --git a/2025/build.zig b/2025/build.zig index 5da17e1..23d2fc3 100644 --- a/2025/build.zig +++ b/2025/build.zig @@ -21,25 +21,40 @@ pub fn build(b: *std.Build) !void { const dep_aoc = b.dependency("aoc", .{}); const aoc_puzzleinput = dep_aoc.artifact("puzzle-input-helper"); - std.debug.print("{any}\n", .{@TypeOf(aoc_puzzleinput)}); - const cmd = b.addRunArtifact(aoc_puzzleinput); const arg_year = try std.fmt.allocPrint(b.allocator, "{d}", .{YEAR}); defer b.allocator.free(arg_year); + const arg_day = try std.fmt.allocPrint(b.allocator, "{d}", .{DAY}); defer b.allocator.free(arg_day); - const puzzle_input_filename = try std.fmt.allocPrint(b.allocator, "day{d:0>2}", .{DAY}); - defer b.allocator.free(puzzle_input_filename); - const arg_puzzle_path = try std.fs.path.join(b.allocator, &.{ + + const arg_input_type_puzzle = try std.fmt.allocPrint(b.allocator, "{d}", .{ + @intFromEnum(aoc.types.PuzzleInput.PUZZLE), + }); + defer b.allocator.free(arg_input_type_puzzle); + + const arg_input_type_example = try std.fmt.allocPrint(b.allocator, "{d}", .{ + @intFromEnum(aoc.types.PuzzleInput.EXAMPLE), + }); + defer b.allocator.free(arg_input_type_puzzle); + + const cmd_puzzle_input = b.addRunArtifact(aoc_puzzleinput); + cmd_puzzle_input.addArgs(&.{ + arg_input_type_puzzle, + arg_year, + arg_day, "input", - "puzzle", - puzzle_input_filename, }); - defer b.allocator.free(arg_puzzle_path); - cmd.addArg(arg_year); - cmd.addArg(arg_day); - cmd.addArg(arg_puzzle_path); - const captured_output = cmd.captureStdOut(); // use this as anonymous import + const captured_output_puzzle = cmd_puzzle_input.captureStdOut(); // use this as anonymous import + + const cmd_example_input = b.addRunArtifact(aoc_puzzleinput); + cmd_example_input.addArgs(&.{ + arg_input_type_example, + arg_year, + arg_day, + "input", + }); + const captured_output_example = cmd_example_input.captureStdOut(); // use this as anonymous import const src_name = try std.fmt.allocPrint(b.allocator, "day{d:0>2}.zig", .{DAY}); defer b.allocator.free(src_name); @@ -50,7 +65,7 @@ pub fn build(b: *std.Build) !void { _ = std.fs.cwd().openFile(src_path, .{}) catch { std.debug.print("{s} not found. Creating from template...\n", .{src_path}); - const contents = try aoc.createTemplate(b.allocator, YEAR, DAY); + const contents = try aoc.createTemplate(b.allocator, DAY); defer b.allocator.free(contents); std.debug.print("{s}\n", .{contents}); const src_file = try std.fs.cwd().createFile(src_path, .{}); @@ -71,16 +86,13 @@ pub fn build(b: *std.Build) !void { const puzzle_input_name = try std.fmt.allocPrint(b.allocator, "puzzle-{d:0>2}", .{DAY}); defer b.allocator.free(puzzle_input_name); exe.root_module.addAnonymousImport(puzzle_input_name, .{ - .root_source_file = captured_output, + .root_source_file = captured_output_puzzle, }); - const expample_input_name = try std.fmt.allocPrint(b.allocator, "example-{d:0>2}", .{DAY}); - defer b.allocator.free(expample_input_name); - const expample_input_file_name = try std.fmt.allocPrint(b.allocator, "day{d:0>2}", .{DAY}); - defer b.allocator.free(expample_input_name); - const example_input_file_path = try std.fs.path.join(b.allocator, &.{ "input", "example", expample_input_file_name }); - defer b.allocator.free(example_input_file_path); - exe.root_module.addAnonymousImport(expample_input_name, .{ - .root_source_file = b.path(example_input_file_path), + + const example_input_name = try std.fmt.allocPrint(b.allocator, "example-{d:0>2}", .{DAY}); + defer b.allocator.free(puzzle_input_name); + exe.root_module.addAnonymousImport(example_input_name, .{ + .root_source_file = captured_output_example, }); exe.root_module.addImport("aoc", dep_aoc.module("aoc")); @@ -102,9 +114,9 @@ pub fn build(b: *std.Build) !void { .optimize = optimize, }), }); - tests.root_module.addAnonymousImport(expample_input_name, .{ - .root_source_file = b.path(example_input_file_path), - }); + // tests.root_module.addAnonymousImport(example_input_name, .{ + // .root_source_file = b.path(example_input_name), + // }); const run_tests = b.addRunArtifact(tests); const test_step = b.step("test", "Run tests"); test_step.dependOn(&run_tests.step); diff --git a/2025/src/day01.zig b/2025/src/day01.zig index bc70121..f1733a8 100644 --- a/2025/src/day01.zig +++ b/2025/src/day01.zig @@ -7,9 +7,11 @@ const DAY: u5 = 1; const Allocator = std.mem.Allocator; const log = std.log; -fn part1(allocator: Allocator, input: []const u8) anyerror!void { +fn unifiedSolution() anyerror!void {} + +fn part1(allocator: Allocator) anyerror!void { _ = allocator; - var row_it = std.mem.tokenizeSequence(u8, input, "\n"); + var row_it = std.mem.tokenizeSequence(u8, @embedFile("puzzle-01"), "\n"); const max = 100; var i: u32 = 0; var dial: i32 = 50; @@ -25,31 +27,16 @@ fn part1(allocator: Allocator, input: []const u8) anyerror!void { std.debug.print("\nResult: {d}\n", .{result}); } -fn part2(allocator: Allocator, input: []const u8) anyerror!void { +fn part2(allocator: Allocator) anyerror!void { _ = allocator; - var row_it = std.mem.tokenizeSequence(u8, input, "\n"); + var row_it = std.mem.tokenizeSequence(u8, @embedFile("puzzle-01"), "\n"); const max = 100; - var i: u32 = 0; var dial: i32 = 50; var result: u32 = 0; - var rotation_count: i32 = 0; - while (row_it.next()) |row| : (i += 1) { + while (row_it.next()) |row| { const sign: i32 = if (row[0] == 'L') -1 else 1; const number = try std.fmt.parseInt(i32, row[1..], 10); var change = sign * number; - const rotations = @as(f32, @floatFromInt(dial)) / @as(f32, @floatFromInt(change)); - rotation_count += @as(i32, @intFromFloat(rotations)); - std.debug.print("{d} → {d} → {d} [#rotations: {d}]\n", .{ - dial, - change, - @mod(dial + change, max), - rotation_count, - }); - - // dial += sign * number; - // dial = @mod(dial, max); - // if (rotation_count == 0 and dial == 0) smart_result += 1; - while (change != 0) : (change += -1 * sign) { dial += sign; dial = @mod(dial, max); @@ -57,17 +44,13 @@ fn part2(allocator: Allocator, input: []const u8) anyerror!void { } } std.debug.print("\nResult: {d}", .{result}); - // std.debug.print("\nOther Result: {d}\n", .{smart_result}); } pub fn main() !void { - // var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - // defer arena.deinit(); - // const allocator = arena.allocator(); - - std.debug.print("{s}\n", .{@embedFile("puzzle")}); + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); - // try aoc.runPart(allocator, DAY, .PUZZLE, part1); - // try aoc.runPart(allocator, DAY, .EXAMPLE, part2); - // try aoc.runPart(allocator, DAY, .PUZZLE, part2); + try aoc.runPart(allocator, part1); + try aoc.runPart(allocator, part2); } diff --git a/2025/src/day02.zig b/2025/src/day02.zig new file mode 100644 index 0000000..27d5384 --- /dev/null +++ b/2025/src/day02.zig @@ -0,0 +1,95 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const YEAR: u12 = 2025; +const DAY: u5 = 2; + +const Allocator = std.mem.Allocator; +const log = std.log; + +fn countDigits(number: usize) usize { + var r: usize = 0; + var n = number; + while (n > 0) : (r += 1) { + n /= 10; + } + return r; +} + +fn part1(allocator: Allocator) anyerror!void { + const input_embed = @embedFile("puzzle-02"); + std.debug.print("--- INPUT---\n{s}\n------------\n", .{input_embed}); + + var result: usize = 0; + + var reader: std.Io.Reader = .fixed(std.mem.trimEnd(u8, input_embed, "\n")); + while (try reader.takeDelimiter(',')) |line| { + var ranges = std.mem.splitSequence(u8, line, "-"); + const lhs = ranges.next().?; + const rhs = ranges.next().?; + const lower = try std.fmt.parseInt(u64, lhs, 10); + const upper = try std.fmt.parseInt(u64, rhs, 10); + for (lower..upper + 1) |id| { + const id_str = try std.fmt.allocPrint(allocator, "{d}", .{id}); + if (try std.math.rem(usize, countDigits(id), 2) == 0) { + const left = id_str[0 .. id_str.len / 2]; + const right = id_str[id_str.len / 2 ..]; + defer allocator.free(id_str); + if (std.mem.eql(u8, left, right)) result += id; + } + } + } + + std.debug.print("Result: {d}\n", .{result}); +} + +fn part2(allocator: Allocator) anyerror!void { + const input_embed = @embedFile("example-02"); + std.debug.print("--- INPUT---\n{s}\n------------\n", .{input_embed}); + + var result: usize = 0; + + var reader: std.Io.Reader = .fixed(std.mem.trimEnd(u8, input_embed, "\n")); + while (try reader.takeDelimiter(',')) |line| { + var ranges = std.mem.splitSequence(u8, line, "-"); + const lhs = ranges.next().?; + const rhs = ranges.next().?; + const lower = try std.fmt.parseInt(u64, lhs, 10); + const upper = try std.fmt.parseInt(u64, rhs, 10); + for (lower..upper + 1) |id| { + const id_str = try std.fmt.allocPrint(allocator, "{d}", .{id}); + defer allocator.free(id_str); + var invalid = false; + + for (1..id_str.len) |x| { + // const remainder = std.math.rem(usize, countDigits(id), x); + + var window_it = std.mem.window(u8, id_str, x, x); + while (window_it.next()) |seq| { + if (std.mem.containsAtLeast(u8, id_str, 2, seq)) { + std.debug.print("{s} ?? {s}\n", .{ id_str, seq }); + invalid = true; + break; + } + } + } + result += id; + // const left = id_str[0 .. id_str.len / 2]; + // const right = id_str[id_str.len / 2 ..]; + // defer allocator.free(id_str); + // if (std.mem.eql(u8, left, right)) result += id; + // } + } + } + + std.debug.print("Result: {d}\n", .{result}); +} + +pub fn main() !void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + + // try aoc.runPart(allocator, part1); + try aoc.runPart(allocator, part2); +} diff --git a/2025/src/day05.zig b/2025/src/day05.zig new file mode 100644 index 0000000..43f74a2 --- /dev/null +++ b/2025/src/day05.zig @@ -0,0 +1,79 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const YEAR: u12 = 2025; +const DAY: u5 = 5; + +const Allocator = std.mem.Allocator; +const log = std.log; + +fn part1(allocator: Allocator, input: []const u8) anyerror!void { + _ = input; + + var fresh_ingredients: usize = 0; + + const input_embed = @embedFile("puzzle-05"); + const split_at = std.mem.indexOf(u8, input_embed, "\n\n").?; + const ranges = std.mem.trim(u8, input_embed[0..split_at], "\n"); + const ingredients_ids = std.mem.trim(u8, input_embed[split_at..], "\n"); + + var ranges_list: std.array_list.Managed(@Vector(2, usize)) = .init(allocator); + defer ranges_list.deinit(); + var ranges_reader: std.Io.Reader = .fixed(ranges); + while (try ranges_reader.takeDelimiter('\n')) |range| { + const sep = std.mem.indexOf(u8, range, "-").?; + try ranges_list.append(.{ + try std.fmt.parseInt(usize, range[0..sep], 10), + try std.fmt.parseInt(usize, range[sep + 1 ..], 10), + }); + } + + var instructions_reader: std.Io.Reader = .fixed(ingredients_ids); + while (try instructions_reader.takeDelimiter('\n')) |line| { + const num = try std.fmt.parseInt(usize, line, 10); + var is_fresh = false; + for (ranges_list.items) |range| { + is_fresh = (num >= range[0] and num <= range[1]); + if (is_fresh) break; + } + if (is_fresh) fresh_ingredients += 1; + } + + std.debug.print("Result: {d}\n", .{fresh_ingredients}); +} + +fn part2(allocator: Allocator, input: []const u8) anyerror!void { + _ = input; + // _ = allocator; + + // var fresh_ingredients: usize = 0; + + const input_embed = @embedFile("example-05"); + const split_at = std.mem.indexOf(u8, input_embed, "\n\n").?; + const ranges = std.mem.trim(u8, input_embed[0..split_at], "\n"); + + var ranges_list: std.array_list.Managed(@Vector(2, usize)) = .init(allocator); + defer ranges_list.deinit(); + + var sum: usize = 0; + var ranges_reader: std.Io.Reader = .fixed(ranges); + while (try ranges_reader.takeDelimiter('\n')) |range| { + const sep = std.mem.indexOf(u8, range, "-").?; + const lower = try std.fmt.parseInt(usize, range[0..sep], 10); + const upper = try std.fmt.parseInt(usize, range[sep + 1 ..], 10); + try ranges_list.append(.{ lower, upper }); + std.debug.print("\n{d}", .{upper - lower}); + sum += upper - lower; + } + + std.debug.print("Result: {d}\n", .{sum}); +} + +pub fn main() !void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + + // try aoc.runPart(allocator, DAY, .EXAMPLE, part1); + try aoc.runPart(allocator, DAY, .EXAMPLE, part2); +} diff --git a/2025/src/day06.zig b/2025/src/day06.zig new file mode 100644 index 0000000..59877b8 --- /dev/null +++ b/2025/src/day06.zig @@ -0,0 +1,206 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const DAY: u5 = 6; + +const Allocator = std.mem.Allocator; +const log = std.log; + +const Cell = struct { + value: []const u8 = " ", + + pub fn format(self: Cell, writer: *std.Io.Writer) std.Io.Writer.Error!void { + try writer.print("{s}", .{self.value}); + } +}; + +fn part1(allocator: Allocator) anyerror!void { + const input = std.mem.trimEnd(u8, @embedFile("puzzle-06"), "\n"); + + const eol = std.mem.indexOf(u8, input, "\n").?; + var first_line_reader: std.Io.Reader = .fixed(input[0..eol]); + var column_count: usize = 1; + var c = try first_line_reader.peekByte(); + while (true) { + const next = first_line_reader.takeByte() catch break; + if (c != ' ' and next == ' ') { + column_count += 1; + } + c = next; + } + std.debug.print("Column count: {d}\n", .{column_count}); + + var line_it = std.mem.splitSequence(u8, input, "\n"); + var row_count: usize = 0; + while (line_it.next()) |_| { + row_count += 1; + } + std.debug.print("{d} x {d}\n", .{ row_count, column_count }); + + var ops: std.array_list.Managed(u8) = .init(allocator); + defer ops.deinit(); + + var problems = try allocator.alloc(std.array_list.Managed(u32), column_count); + for (0..problems.len) |i| { + problems[i] = .init(allocator); + } + defer allocator.free(problems); + defer for (problems) |p| p.deinit(); + + var lines_it: std.Io.Reader = .fixed(input); + while (try lines_it.takeDelimiter('\n')) |line| { + if (line.len == 0) continue; + const line_trimmed = std.mem.trim(u8, line, "\n"); + var reader: std.Io.Reader = .fixed(line_trimmed); + + if (std.mem.containsAtLeast(u8, line_trimmed, 1, "+")) { + var o: u8 = ' '; + while (true) { + o = reader.takeByte() catch break; + switch (o) { + '*', '+' => try ops.append(o), + ' ' => continue, + else => unreachable, + } + } + break; + } + + var it = std.mem.splitScalar(u8, line, ' '); + var i: usize = 0; + while (it.next()) |next| { + if (next.len == 0) continue; + const v = try std.fmt.parseInt(u32, next, 10); + try problems[i].append(v); + i += 1; + } + } + + var sum: u64 = 0; + for (0..problems.len) |p| { + std.debug.print("({c} ", .{ops.items[p]}); + for (problems[p].items) |v| std.debug.print("{d} ", .{v}); + var result: usize = 0; + switch (ops.items[p]) { + '+' => { + for (problems[p].items) |v| result += v; + }, + '*' => { + result = 1; + for (problems[p].items) |v| result *= v; + }, + ' ' => break, + else => unreachable, + } + std.debug.print(" = {d}", .{result}); + std.debug.print(")\n", .{}); + sum += result; + } + std.debug.print("\nResult: {d}\n", .{sum}); +} + +fn part2(allocator: Allocator) anyerror!void { + const input = @embedFile("puzzle-06"); + var rows: usize = 0; + var cols: usize = 0; + var ops: std.array_list.Managed(u8) = .init(allocator); + var splits: std.array_list.Managed([2]usize) = .init(allocator); + + // Find number of rows + var line_it = std.mem.splitScalar(u8, input, '\n'); + var line: []const u8 = ""; + while (line_it.next()) |l| : (rows += 1) { + if (l.len == 0) break; + line = l; + } + + // Find number of columns + var ops_reader: std.Io.Reader = .fixed(line); + var previous: u8 = try ops_reader.takeByte(); + var s: usize = 0; + var i: usize = 0; + var last_seen_op: u8 = previous; + while (true) : (i += 1) { + const c = ops_reader.takeByte() catch { + // last split, intentionally create a larger end index + cols += 1; + try ops.append(last_seen_op); + try splits.append(.{ s, s + 5 }); + break; + }; + if (previous == ' ' and c != ' ') { + try ops.append(last_seen_op); + // store the last seen op to be available + // when iterator has no more data + last_seen_op = c; + try splits.append(.{ s, i }); + s = i; + } + if (c == ' ') { + previous = c; + continue; + } + cols += 1; + } + + std.debug.print("{d} x {d}\n", .{ rows, cols }); + + // Process input + var sum: usize = 0; + for (ops.items, 0..cols) |op, x| { + var sum_column: usize = 0; + var line_idx: usize = 0; + + for (0..rows) |r| { + var number: usize = 0; + + var digit_count: usize = 0; + line_it.reset(); + while (line_it.next()) |l| { + if (l.len == 0 or std.mem.containsAtLeast(u8, l, 1, "*")) break; + const lower = splits.items[x][0]; + const upper = @min(splits.items[x][1], l.len); + const slice = l[lower..upper]; + if (r >= slice.len) break; + if (slice[r] != ' ') digit_count += 1; + } + + line_it.reset(); + var pow: usize = std.math.pow(usize, 10, digit_count -| 1); + while (line_it.next()) |l| : (line_idx += 1) { + if (l.len == 0 or std.mem.containsAtLeast(u8, l, 1, "*")) break; + const lower = splits.items[x][0]; + const upper = @min(splits.items[x][1], l.len); + const slice = l[lower..upper]; + if (r >= slice.len) break; + + switch (slice[r]) { + '0'...'9' => |c| { + const digit = c - '0'; + number += digit * pow; + pow /= 10; + }, + else => continue, + } + } + + switch (op) { + '*' => sum_column = @max(1, sum_column) * @max(1, number), + '+' => sum_column += number, + else => unreachable, + } + } + + sum += sum_column; + } + std.debug.print("Result: {d}\n", .{sum}); +} + +pub fn main() !void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + + try aoc.runPart(allocator, part1); + try aoc.runPart(allocator, part2); +} diff --git a/2025/src/day07.zig b/2025/src/day07.zig new file mode 100644 index 0000000..9d9a1a0 --- /dev/null +++ b/2025/src/day07.zig @@ -0,0 +1,77 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const DAY: u5 = 7; + +const Allocator = std.mem.Allocator; +const log = std.log; + +const Map = struct { + rows: usize, + cols: usize, + buffer: []u8, + + pub fn init(allocator: Allocator, buffer: []const u8) !Map { + if (std.mem.indexOf(u8, buffer, "\n")) |line_end| { + return .{ + .buffer = try std.mem.replaceOwned(u8, allocator, buffer, "\n", ""), + .cols = line_end, + .rows = (buffer.len - 1) / line_end, + }; + } + @panic("Failed to split input buffer!"); + } + + pub fn get(self: *const Map, x: usize, y: usize) u8 { + return self.buffer[(y * self.cols) + x]; + } + + pub fn format(self: Map, writer: *std.Io.Writer) std.Io.Writer.Error!void { + try writer.print("Map {d} x {d}\n", .{ self.rows, self.cols }); + for (0..self.rows) |y| { + for (0..self.cols) |x| { + try writer.print("{c}", .{self.get(x, y)}); + } + try writer.print("\n", .{}); + } + } + + pub fn deinit(self: Map, allocator: Allocator) void { + allocator.free(self.buffer); + } +}; + +fn part1(allocator: Allocator) anyerror!void { + const input = @embedFile("example-07"); + std.debug.print("--- INPUT---\n{s}\n------------\n", .{input}); + + const map: Map = try .init(allocator, input); + + var start: @Vector(2, usize) = @splat(0); + var splitters: std.array_list.Managed(@Vector(2, usize)) = .init(allocator); + + for (0..map.rows) |y| { + for (0..map.cols) |x| { + switch (map.get(x, y)) { + 'S' => start = .{ x, y }, + '^' => try splitters.append(.{ x, y }), + else => continue, + } + } + } +} + +fn part2(allocator: Allocator) anyerror!void { + _ = allocator; + const input = @embedFile("puzzle-07"); + std.debug.print("--- INPUT---\n{s}\n------------\n", .{input}); +} + +pub fn main() !void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + + try aoc.runPart(allocator, part1); + // try aoc.runPart(allocator, part2); +} diff --git a/2025/src/day08.zig b/2025/src/day08.zig new file mode 100644 index 0000000..1e1f92f --- /dev/null +++ b/2025/src/day08.zig @@ -0,0 +1,104 @@ +const std = @import("std"); +const aoc = @import("aoc"); +const VectorSet = @import("libs").VectorSet; + +const DAY: u5 = 8; + +const Allocator = std.mem.Allocator; +const log = std.log; + +fn distance(comptime T: type, a: @Vector(3, T), b: @Vector(3, T)) T { + const v = @abs(b - a); + return @sqrt(@reduce(.Add, v * v)); +} + +fn part1(allocator: Allocator) anyerror!void { + const input = @embedFile("example-08"); + + var reader: std.Io.Reader = .fixed(input); + + const T: type = f32; + const Box = struct { + id: usize, + pos: @Vector(3, T), + + pub fn format(self: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void { + try writer.print("[{d: >4}] {d: >6}", .{ self.id, self.pos }); + } + }; + + var junction_boxes: std.array_list.Managed(Box) = .init(allocator); + defer junction_boxes.deinit(); + + var idx: usize = 0; + while (try reader.takeDelimiter('\n')) |line| { + var coords_it = std.mem.splitScalar(u8, line, ','); + var coord: @Vector(3, T) = @splat(0); + var i: usize = 0; + while (coords_it.next()) |str| : (i += 1) { + const v = try std.fmt.parseFloat(T, str); + std.debug.assert(v != 0); + coord[i] = v; + } + std.debug.assert(i <= 3); + try junction_boxes.append(.{ + .id = idx, + .pos = coord, + }); + idx += 1; + } + + const Connection = struct { + a: Box, + b: Box, + d: T, + + pub fn format(self: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void { + try writer.print("{f}---{f} [distance: {d: >10.3}]", .{ self.a, self.b, self.d }); + } + }; + + var connections: std.array_list.Managed(Connection) = .init(allocator); + defer connections.deinit(); + + for (0..junction_boxes.items.len) |i| { + for (i..junction_boxes.items.len) |j| { + const a = junction_boxes.items[i]; + const b = junction_boxes.items[j]; + const d = distance(T, a.pos, b.pos); + if (d == 0) continue; + try connections.append(.{ + .a = a, + .b = b, + .d = d, + }); + } + } + + std.mem.sort(Connection, connections.items, {}, comptime struct { + pub fn f(_: void, a: Connection, b: Connection) bool { + return a.d > b.d; + } + }.f); + + while (connections.pop()) |next| { + std.debug.print("{f}\n", .{next}); + } + + // std.debug.print("--- INPUT---\n{s}\n------------\n", .{input}); +} + +fn part2(allocator: Allocator) anyerror!void { + _ = allocator; + // const input = @embedFile("example-08"); + // std.debug.print("--- INPUT---\n{s}\n------------\n", .{@embedFile()}); +} + +pub fn main() !void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + + try aoc.runPart(allocator, part1); + // try aoc.runPart(allocator, part2); +} diff --git a/2025/src/day09.zig b/2025/src/day09.zig new file mode 100644 index 0000000..8850d8f --- /dev/null +++ b/2025/src/day09.zig @@ -0,0 +1,28 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const DAY: u5 = 9; + +const Allocator = std.mem.Allocator; +const log = std.log; + +fn part1(allocator: Allocator) anyerror!void { + _ = allocator; + const input = @embedFile("example-09"); + std.debug.print("--- INPUT---\n{s}\n------------\n", .{input}); +} + +fn part2(allocator: Allocator) anyerror!void { + _ = allocator; + const input = @embedFile("example-09"); + std.debug.print("--- INPUT---\n{s}\n------------\n", .{input}); +} + +pub fn main() !void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + + try aoc.runPart(allocator, part1); + // try aoc.runPart(allocator, part2); +} diff --git a/modules/aoc/build.zig b/modules/aoc/build.zig index 8ded910..d34d471 100644 --- a/modules/aoc/build.zig +++ b/modules/aoc/build.zig @@ -1,16 +1,28 @@ const std = @import("std"); const input = @import("src/input.zig"); -const types = @import("src/types.zig"); +pub const types = @import("src/types.zig"); const utils = @import("src/utils.zig"); -pub fn createTemplate(allocator: std.mem.Allocator, year: types.Year, day: types.Day) ![]const u8 { +pub fn createTemplate(allocator: std.mem.Allocator, day: types.Day) ![]const u8 { var template_str = utils.getTemplate(); + std.debug.print("{any}\n", .{@TypeOf(template_str)}); + const search = "\"$DAY\""; const day_str = try std.fmt.allocPrint(allocator, "{d}", .{day}); defer allocator.free(day_str); - const year_str = try std.fmt.allocPrint(allocator, "{d}", .{year}); - defer allocator.free(year_str); - template_str = try std.mem.replaceOwned(u8, allocator, template_str, "\"$DAY\"", day_str); - template_str = try std.mem.replaceOwned(u8, allocator, template_str, "\"$YEAR\"", year_str); + template_str = try std.mem.replaceOwned(u8, allocator, template_str, search, day_str); + std.debug.print("{s}\n", .{template_str}); + // while (std.mem.indexOf( + // u8, + // template_str, + // )) |found| { + // var output = try allocator.alloc(u8, search.len); + // } + // std.debug.print("{any}: ", .{found}); + // const found_slice = template_str[found .. found + "\"$DAY\"".len]; + // std.debug.print("{s}\n", .{found_slice}); + // break; + // // found_slice = day_str; + // } return template_str; } diff --git a/modules/aoc/src/common.zig b/modules/aoc/src/common.zig index 2ef5f19..b6904d0 100644 --- a/modules/aoc/src/common.zig +++ b/modules/aoc/src/common.zig @@ -22,29 +22,19 @@ pub fn printTime(time: u64) void { pub fn runPart( allocator: std.mem.Allocator, - comptime day: types.Day, - input_type: types.PuzzleInput, - comptime part_fn: fn (allocator: Allocator, input: []const u8) anyerror!void, + comptime part_fn: fn (allocator: Allocator) anyerror!void, ) !void { - _ = day; - const input = switch (input_type) { - .PUZZLE => "", //try puzzle_input.getPuzzleInput(allocator, cwd, day), - .EXAMPLE => "", //try puzzle_input.getExampleInput(allocator, cwd, day), - }; stopwatch.start(); - try part_fn(allocator, input); + try part_fn(allocator); const time = stopwatch.stop(); printTime(time); } pub fn runDay( allocator: std.mem.Allocator, - comptime year: types.Year, - comptime day: types.Day, - input_type: types.PuzzleInput, - comptime part1: fn (allocator: Allocator, input: []const u8) anyerror!void, - comptime part2: fn (allocator: Allocator, input: []const u8) anyerror!void, + comptime part1: fn (allocator: Allocator) anyerror!void, + comptime part2: fn (allocator: Allocator) anyerror!void, ) !void { - try runPart(allocator, year, day, input_type, part1); - try runPart(allocator, year, day, input_type, part2); + try runPart(allocator, part1); + try runPart(allocator, part2); } diff --git a/modules/aoc/src/fetch-puzzle-input.zig b/modules/aoc/src/fetch-puzzle-input.zig index ea66384..2360cf3 100644 --- a/modules/aoc/src/fetch-puzzle-input.zig +++ b/modules/aoc/src/fetch-puzzle-input.zig @@ -19,15 +19,60 @@ pub fn main() !void { defer args.deinit(); _ = args.next(); // binary name - if (args.inner.count < 3) { + if (args.inner.count < 4) { _ = try std.fs.File.stdout().write("Please provide 2 arguments: YEAR DAY, e.g. 2025 7"); return; } + const INPUT_TYPE: types.PuzzleInput = @enumFromInt(try std.fmt.parseInt( + @typeInfo(types.PuzzleInput).@"enum".tag_type, + args.next().?, + 10, + )); const YEAR = try std.fmt.parseInt(types.Year, args.next().?, 10); const DAY = try std.fmt.parseInt(types.Day, args.next().?, 10); const puzzle_file_path = args.next(); - const response = try input.getPuzzleInputFromServer(allocator, YEAR, DAY, puzzle_file_path); - try std.fs.File.stdout().writeAll(response); + std.debug.print("{any}: {d}-{d}\n", .{ YEAR, DAY, INPUT_TYPE }); + + const sub_folder = switch (INPUT_TYPE) { + .EXAMPLE => "example", + .PUZZLE => "puzzle", + }; + const file_name = try std.fmt.allocPrint(allocator, "day{d:0>2.}", .{DAY}); + const file_path = try std.fs.path.join(allocator, &.{ ".", puzzle_file_path.?, sub_folder, file_name }); + const file = std.fs.cwd().openFile(file_path, .{}) catch |err| { + switch (err) { + error.FileNotFound => { + _ = switch (INPUT_TYPE) { + .PUZZLE => { + const response = try input.getPuzzleInputFromServer( + allocator, + YEAR, + DAY, + file_path, + ); + try std.fs.File.stdout().writeAll(response); + return; + }, + .EXAMPLE => { + _ = try std.fs.cwd().createFile(file_path, .{}); + std.debug.print("šŸŽ Created empty {s}. Make sure to feed in data.\n", .{ + file_path, + }); + return; + }, + }; + }, + else => { + @panic("Error not handled"); + }, + } + }; + + // Read existing file + const end = try file.getEndPos(); + const result = try file.readToEndAlloc(allocator, end); + + try std.fs.File.stdout().writeAll(result); } diff --git a/modules/aoc/src/root.zig b/modules/aoc/src/root.zig index b590a9d..eb5a651 100644 --- a/modules/aoc/src/root.zig +++ b/modules/aoc/src/root.zig @@ -1,6 +1,7 @@ const std = @import("std"); const common = @import("common.zig"); +pub const types = @import("types.zig"); pub const utils = @import("utils.zig"); pub const runPart = common.runPart; diff --git a/modules/aoc/src/template.zig b/modules/aoc/src/template.zig index 92d7aea..3f7b2b7 100644 --- a/modules/aoc/src/template.zig +++ b/modules/aoc/src/template.zig @@ -1,22 +1,19 @@ const std = @import("std"); const aoc = @import("aoc"); -const YEAR: u12 = "$YEAR"; const DAY: u5 = "$DAY"; const Allocator = std.mem.Allocator; const log = std.log; -fn part1(allocator: Allocator, input: []const u8) anyerror!void { +fn part1(allocator: Allocator) anyerror!void { _ = allocator; - _ = input; - std.debug.print("--- INPUT---\n{s}\n------------\n", .{@embedFile("puzzle-$DAY")}); + // std.debug.print("--- INPUT---\n{s}\n------------\n", .{input}); } -fn part2(allocator: Allocator, input: []const u8) anyerror!void { +fn part2(allocator: Allocator) anyerror!void { _ = allocator; - _ = input; - std.debug.print("--- INPUT---\n{s}\n------------\n", .{@embedFile("puzzle-$DAY")}); + // std.debug.print("--- INPUT---\n{s}\n------------\n", .{@embedFile()}); } pub fn main() !void { @@ -24,6 +21,6 @@ pub fn main() !void { defer arena.deinit(); const allocator = arena.allocator(); - try aoc.runPart(allocator, DAY, .EXAMPLE, part1); - try aoc.runPart(allocator, DAY, .EXAMPLE, part2); + try aoc.runPart(allocator, part1); + try aoc.runPart(allocator, part2); } diff --git a/modules/aoc/src/types.zig b/modules/aoc/src/types.zig index caac1dc..b36a889 100644 --- a/modules/aoc/src/types.zig +++ b/modules/aoc/src/types.zig @@ -1,6 +1,6 @@ pub const Year = u12; pub const Day = u5; -pub const PuzzleInput = enum { - EXAMPLE, - PUZZLE, +pub const PuzzleInput = enum(u1) { + EXAMPLE = 0, + PUZZLE = 1, }; diff --git a/modules/aoc/src/utils.zig b/modules/aoc/src/utils.zig index e4341d5..936db5b 100644 --- a/modules/aoc/src/utils.zig +++ b/modules/aoc/src/utils.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const zeit = @import("zeit"); +const types = @import("types.zig"); pub fn blockAskForNext() void { step: { @@ -14,19 +14,6 @@ pub fn blockAskForNext() void { } } -pub fn getToday() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - const allocator = gpa.allocator(); - - var env = try std.process.getEnvMap(allocator); - defer env.deinit(); - const local = try zeit.local(allocator, &env); - const now = try zeit.instant(.{}); - const now_local = now.in(&local); - const dt = now_local.time(); - std.debug.print("{}", .{dt}); -} - pub fn getTemplate() []const u8 { return @embedFile("template.zig"); } From 0e113c544aa0ed8f6ceb686cce0bedb3d7e38f3c Mon Sep 17 00:00:00 2001 From: Guido Schmidt Date: Thu, 11 Dec 2025 12:37:00 +0100 Subject: [PATCH 09/18] feat(2025/day05): solve part 1 and part 2 --- 2025/src/day05.zig | 56 +++++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/2025/src/day05.zig b/2025/src/day05.zig index 43f74a2..fd6d909 100644 --- a/2025/src/day05.zig +++ b/2025/src/day05.zig @@ -7,9 +7,7 @@ const DAY: u5 = 5; const Allocator = std.mem.Allocator; const log = std.log; -fn part1(allocator: Allocator, input: []const u8) anyerror!void { - _ = input; - +fn part1(allocator: Allocator) anyerror!void { var fresh_ingredients: usize = 0; const input_embed = @embedFile("puzzle-05"); @@ -42,28 +40,56 @@ fn part1(allocator: Allocator, input: []const u8) anyerror!void { std.debug.print("Result: {d}\n", .{fresh_ingredients}); } -fn part2(allocator: Allocator, input: []const u8) anyerror!void { - _ = input; - // _ = allocator; - - // var fresh_ingredients: usize = 0; - - const input_embed = @embedFile("example-05"); +fn part2(allocator: Allocator) anyerror!void { + const input_embed = @embedFile("puzzle-05"); const split_at = std.mem.indexOf(u8, input_embed, "\n\n").?; const ranges = std.mem.trim(u8, input_embed[0..split_at], "\n"); var ranges_list: std.array_list.Managed(@Vector(2, usize)) = .init(allocator); defer ranges_list.deinit(); - var sum: usize = 0; var ranges_reader: std.Io.Reader = .fixed(ranges); while (try ranges_reader.takeDelimiter('\n')) |range| { const sep = std.mem.indexOf(u8, range, "-").?; const lower = try std.fmt.parseInt(usize, range[0..sep], 10); const upper = try std.fmt.parseInt(usize, range[sep + 1 ..], 10); + try ranges_list.append(.{ lower, upper }); - std.debug.print("\n{d}", .{upper - lower}); - sum += upper - lower; + } + + std.mem.sort(@Vector(2, usize), ranges_list.items, {}, comptime struct { + pub fn f(_: void, a: @Vector(2, usize), b: @Vector(2, usize)) bool { + return a[0] < b[0]; + } + }.f); + + for (0..ranges_list.items.len - 1) |r| { + const curr = ranges_list.items[r]; + const next = ranges_list.items[r + 1]; + + // Current range overlaps next range on lower bound of next + if (curr[1] >= next[0]) { + + // Extend next range on lower end + ranges_list.items[r + 1][0] = curr[0]; + + // Current range overlaps next range on upper bound of next + if (curr[1] > next[1]) { + // Extend next range on upper end + ranges_list.items[r + 1][1] = curr[1]; + } + + // Dismiss current range + // (as the next range has been expanded and ids shouldn't be + // counted twice) + ranges_list.items[r] = .{ 0, 0 }; + } + } + + var sum: usize = 0; + for (ranges_list.items) |r| { + if (r[0] == 0 or r[1] == 0) continue; + sum += r[1] - r[0] + 1; } std.debug.print("Result: {d}\n", .{sum}); @@ -74,6 +100,6 @@ pub fn main() !void { defer arena.deinit(); const allocator = arena.allocator(); - // try aoc.runPart(allocator, DAY, .EXAMPLE, part1); - try aoc.runPart(allocator, DAY, .EXAMPLE, part2); + try aoc.runPart(allocator, part1); + try aoc.runPart(allocator, part2); } From 22ccf97a5413675ff4182309149cf4986b336852 Mon Sep 17 00:00:00 2001 From: Guido Schmidt Date: Thu, 11 Dec 2025 12:40:11 +0100 Subject: [PATCH 10/18] refactor(modules/aoc): improve string replacement in template --- modules/aoc/build.zig | 27 ++++++++++++--------------- modules/aoc/src/common.zig | 10 +--------- modules/aoc/src/template.zig | 6 ++++-- 3 files changed, 17 insertions(+), 26 deletions(-) diff --git a/modules/aoc/build.zig b/modules/aoc/build.zig index d34d471..6660b68 100644 --- a/modules/aoc/build.zig +++ b/modules/aoc/build.zig @@ -6,23 +6,20 @@ const utils = @import("src/utils.zig"); pub fn createTemplate(allocator: std.mem.Allocator, day: types.Day) ![]const u8 { var template_str = utils.getTemplate(); std.debug.print("{any}\n", .{@TypeOf(template_str)}); - const search = "\"$DAY\""; + const search_DAY = "\"$DAY\""; const day_str = try std.fmt.allocPrint(allocator, "{d}", .{day}); defer allocator.free(day_str); - template_str = try std.mem.replaceOwned(u8, allocator, template_str, search, day_str); - std.debug.print("{s}\n", .{template_str}); - // while (std.mem.indexOf( - // u8, - // template_str, - // )) |found| { - // var output = try allocator.alloc(u8, search.len); - // } - // std.debug.print("{any}: ", .{found}); - // const found_slice = template_str[found .. found + "\"$DAY\"".len]; - // std.debug.print("{s}\n", .{found_slice}); - // break; - // // found_slice = day_str; - // } + template_str = try std.mem.replaceOwned(u8, allocator, template_str, search_DAY, day_str); + const search_DAY_embedFile = "$DAY"; + for (0..2) |_| { + template_str = try std.mem.replaceOwned( + u8, + allocator, + template_str, + search_DAY_embedFile, + day_str, + ); + } return template_str; } diff --git a/modules/aoc/src/common.zig b/modules/aoc/src/common.zig index b6904d0..3b8f8d1 100644 --- a/modules/aoc/src/common.zig +++ b/modules/aoc/src/common.zig @@ -5,19 +5,11 @@ const stopwatch = @import("stopwatch.zig"); const Allocator = std.mem.Allocator; -pub fn printPart1() void { - std.debug.print("\n########## Part 1 ##########", .{}); -} - -pub fn printPart2() void { - std.debug.print("\n########## Part 2 ##########", .{}); -} - pub fn printTime(time: u64) void { const ns = time; const us: f64 = @floatFromInt(time / std.time.ns_per_us); const ms: f64 = @floatFromInt(time / std.time.ns_per_ms); - std.debug.print("\n— ā² Running time: {d:3} ms / {d:3} μs / {d} ns\n", .{ ms, us, ns }); + std.debug.print("\n— āŒ›ļø Running time: {d:3} ms / {d:3} μs / {d} ns\n", .{ ms, us, ns }); } pub fn runPart( diff --git a/modules/aoc/src/template.zig b/modules/aoc/src/template.zig index 3f7b2b7..6840ee7 100644 --- a/modules/aoc/src/template.zig +++ b/modules/aoc/src/template.zig @@ -8,12 +8,14 @@ const log = std.log; fn part1(allocator: Allocator) anyerror!void { _ = allocator; - // std.debug.print("--- INPUT---\n{s}\n------------\n", .{input}); + const input = @embedFile("example-$DAY"); + std.debug.print("--- INPUT---\n{s}\n------------\n", .{input}); } fn part2(allocator: Allocator) anyerror!void { _ = allocator; - // std.debug.print("--- INPUT---\n{s}\n------------\n", .{@embedFile()}); + const input = @embedFile("example-$DAY"); + std.debug.print("--- INPUT---\n{s}\n------------\n", .{input}); } pub fn main() !void { From a33d842bb89b18ccf802e0bc9488c86966e6d1d1 Mon Sep 17 00:00:00 2001 From: Guido Schmidt Date: Thu, 11 Dec 2025 13:18:08 +0100 Subject: [PATCH 11/18] feat(2025/day04): solve part 1 and 2 --- 2025/src/day04.zig | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/2025/src/day04.zig b/2025/src/day04.zig index 816004e..7754e70 100644 --- a/2025/src/day04.zig +++ b/2025/src/day04.zig @@ -56,8 +56,7 @@ fn findAccessibleRolls(map: []u8, rows: usize, cols: usize) !usize { return result; } -fn part1(allocator: Allocator, input: []const u8) anyerror!void { - _ = input; +fn part1(allocator: Allocator) anyerror!void { const input_embed = std.mem.trimEnd(u8, @embedFile("puzzle-04"), "\n"); var it = std.mem.tokenizeSequence(u8, input_embed, "\n"); @@ -72,8 +71,7 @@ fn part1(allocator: Allocator, input: []const u8) anyerror!void { std.debug.print("Result: {d}\n", .{result}); } -fn part2(allocator: Allocator, input: []const u8) anyerror!void { - _ = input; +fn part2(allocator: Allocator) anyerror!void { const input_embed = std.mem.trimEnd(u8, @embedFile("puzzle-04"), "\n"); var it = std.mem.tokenizeSequence(u8, input_embed, "\n"); @@ -98,6 +96,6 @@ pub fn main() !void { defer arena.deinit(); const allocator = arena.allocator(); - try aoc.runPart(allocator, DAY, .EXAMPLE, part1); - try aoc.runPart(allocator, DAY, .EXAMPLE, part2); + try aoc.runPart(allocator, part1); + try aoc.runPart(allocator, part2); } From d17ba472cfe0cdf45e8c51e46e39c4c0896f4f44 Mon Sep 17 00:00:00 2001 From: Guido Schmidt Date: Thu, 11 Dec 2025 14:07:19 +0100 Subject: [PATCH 12/18] feat(2025/day02): solve part 1 + part 2 --- 2025/src/day02.zig | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/2025/src/day02.zig b/2025/src/day02.zig index 27d5384..80ca329 100644 --- a/2025/src/day02.zig +++ b/2025/src/day02.zig @@ -44,7 +44,7 @@ fn part1(allocator: Allocator) anyerror!void { } fn part2(allocator: Allocator) anyerror!void { - const input_embed = @embedFile("example-02"); + const input_embed = @embedFile("puzzle-02"); std.debug.print("--- INPUT---\n{s}\n------------\n", .{input_embed}); var result: usize = 0; @@ -59,26 +59,26 @@ fn part2(allocator: Allocator) anyerror!void { for (lower..upper + 1) |id| { const id_str = try std.fmt.allocPrint(allocator, "{d}", .{id}); defer allocator.free(id_str); + var invalid = false; - for (1..id_str.len) |x| { - // const remainder = std.math.rem(usize, countDigits(id), x); + const digit_count = countDigits(id); + check: for (1..digit_count) |x| { + const w = digit_count - x; + const remainder = try std.math.rem(usize, digit_count, w); + if (remainder > 0) continue; - var window_it = std.mem.window(u8, id_str, x, x); + var window_it = std.mem.window(u8, id_str, w, w); + const search = window_it.next().?; while (window_it.next()) |seq| { - if (std.mem.containsAtLeast(u8, id_str, 2, seq)) { - std.debug.print("{s} ?? {s}\n", .{ id_str, seq }); - invalid = true; - break; + if (!std.mem.eql(u8, search, seq)) { + continue :check; } } + invalid = true; + break :check; } - result += id; - // const left = id_str[0 .. id_str.len / 2]; - // const right = id_str[id_str.len / 2 ..]; - // defer allocator.free(id_str); - // if (std.mem.eql(u8, left, right)) result += id; - // } + if (invalid) result += id; } } From 97ac2f8f85f4e9e13bfcbb669b0081551f6ad2f9 Mon Sep 17 00:00:00 2001 From: Guido Schmidt Date: Mon, 15 Dec 2025 22:06:25 +0100 Subject: [PATCH 13/18] feat(2025): solve day 09, part 1 + 2 --- 2025/src/day09.zig | 291 +++++++++++++++++++++++- modules/libs/src/datastructures.zig | 0 modules/libs/src/datastructures/Map.zig | 74 ++++++ modules/libs/src/root.zig | 1 + modules/libs/src/svg.zig | 27 ++- 5 files changed, 373 insertions(+), 20 deletions(-) delete mode 100644 modules/libs/src/datastructures.zig create mode 100644 modules/libs/src/datastructures/Map.zig diff --git a/2025/src/day09.zig b/2025/src/day09.zig index 8850d8f..b3e8e43 100644 --- a/2025/src/day09.zig +++ b/2025/src/day09.zig @@ -1,21 +1,283 @@ const std = @import("std"); const aoc = @import("aoc"); +const t = @import("libs").term; +const svg = @import("libs").svg; +const Map = @import("libs").Map; const DAY: u5 = 9; const Allocator = std.mem.Allocator; const log = std.log; +fn calcArea(x0: u64, x1: u64, y0: u64, y1: u64) u64 { + const vec_a: u64 = @abs(@as(i64, @intCast(x0)) - @as(i64, @intCast(x1))) + 1; + const vec_b: u64 = @abs(@as(i64, @intCast(y0)) - @as(i64, @intCast(y1))) + 1; + return vec_a * vec_b; +} + +fn distance(a: @Vector(2, u64), b: @Vector(2, u64)) f64 { + const vec: @Vector(2, f64) = @as(@Vector(2, f64), @floatFromInt(a)) - @as(@Vector(2, f64), @floatFromInt(b)); + return @sqrt(@exp2(vec))[0]; +} + +fn findLeftMostLowest(list: *std.array_list.Managed(@Vector(2, u64))) @Vector(2, u64) { + std.debug.assert(list.items.len > 2); + var result = list.items[0]; + for (list.items) |p| { + if (p[0] < result[0]) { + result = p; + if (p[1] < result[1]) { + result = p; + } + } + } + return result; +} + +/// Cast a ray from point p to the right +/// Count how many times it intersects the path +/// count is odd => inside, count is even => outside +fn insidePath(path: *std.array_list.Managed(@Vector(2, u64)), p: @Vector(2, u64)) bool { + var inside = false; + for (0..path.items.len) |i| { + const a = path.items[i]; + const b = path.items[(i + 1) % path.items.len]; + + // Convert to signed integers for comparison + const ax = @as(i64, @intCast(a[0])); + const ay = @as(i64, @intCast(a[1])); + const bx = @as(i64, @intCast(b[0])); + const by = @as(i64, @intCast(b[1])); + const px = @as(i64, @intCast(p[0])); + const py = @as(i64, @intCast(p[1])); + + // Check if edge crosses the horizontal ray from point p to the right + // Edge must straddle the horizontal line at py + if ((ay >= py) != (by >= py)) { + // Calculate x-coordinate of intersection point + // Using: x = ax + (py - ay) * (bx - ax) / (by - ay) + const slope_num = (py - ay) * (bx - ax); + const slope_den = by - ay; + const intersect_x = ax + @divTrunc(slope_num, slope_den); + + // If intersection is to the right of point p, toggle inside + if (px + 1 < intersect_x) { + inside = !inside; + } + } + } + + return inside; +} + fn part1(allocator: Allocator) anyerror!void { - _ = allocator; - const input = @embedFile("example-09"); + const input = @embedFile("puzzle-09"); std.debug.print("--- INPUT---\n{s}\n------------\n", .{input}); + + var it: std.Io.Reader = .fixed(input); + var tiles: std.array_list.Managed(@Vector(2, u64)) = .init(allocator); + defer tiles.deinit(); + + var last_area: u64 = 0; + while (try it.takeDelimiter('\n')) |line| { + if (std.mem.indexOf(u8, line, ",")) |sep| { + const x = try std.fmt.parseInt(u64, line[0..sep], 10); + const y = try std.fmt.parseInt(u64, line[sep + 1 ..], 10); + for (tiles.items) |_| { + const area = calcArea(x, t[0], y, t[1]); + last_area = @max(area, last_area); + } + try tiles.append(.{ x, y }); + } + } + std.debug.print("Largest area: {d}\n", .{last_area}); } fn part2(allocator: Allocator) anyerror!void { - _ = allocator; - const input = @embedFile("example-09"); + const input = @embedFile("puzzle-09"); std.debug.print("--- INPUT---\n{s}\n------------\n", .{input}); + + var it: std.Io.Reader = .fixed(input); + var tiles: std.array_list.Managed(@Vector(2, u64)) = .init(allocator); + defer tiles.deinit(); + + var largest: @Vector(2, usize) = .{ 0, 0 }; + while (try it.takeDelimiter('\n')) |line| { + if (std.mem.indexOf(u8, line, ",")) |sep| { + const x = try std.fmt.parseInt(u64, line[0..sep], 10); + const y = try std.fmt.parseInt(u64, line[sep + 1 ..], 10); + // map.set(x, y, '#'); + if (x > largest[0]) { + largest[0] = x; + } + if (y > largest[1]) { + largest[1] = y; + } + try tiles.append(.{ x, y }); + } + } + + std.debug.print("{d} x {d}\n", .{ largest[0], largest[1] }); + + try svg.init( + "2025-day09-part2.svg", + largest[0], + largest[1], + 0, + largest[0], + 0, + largest[1], + ); + + try svg.startPolygon(); + for (tiles.items) |tile| { + try svg.addPolygonPoint(tile[0], tile[1]); + } + try svg.endPolygon("green"); + + var path: std.array_list.Managed(@Vector(2, u64)) = .init(allocator); + defer path.deinit(); + + const start = findLeftMostLowest(&tiles); + try path.append(start); + + // const map: Map = try .initEmpty(allocator, 14, 8, '.'); + // defer map.deinit(); + + std.debug.print("Start: {d}\n", .{start}); + // map.set(start[0], start[1], 'S'); + + const tile_count = tiles.items.len; + var current = start; + var idx: u64 = 1; + while (true) { + if (idx >= tile_count) break; + var closest_idx: usize = 0; + var closest = tiles.items[closest_idx]; + var d: f64 = 100.0; + for (1..tiles.items.len) |i| { + const tile = tiles.items[i]; + if (tile[0] == current[0] and tile[1] == current[1]) continue; + if (current[0] != tile[0] and current[1] != tile[1]) continue; + const closest_d = distance(current, tile); + if (closest_d < d) { + d = closest_d; + closest = tile; + closest_idx = i; + } + } + // map.set(closest[0], closest[1], idx + '0'); + idx += 1; + current = closest; + try path.append(tiles.swapRemove(closest_idx)); + } + + // std.debug.print("Path:\n{any}\n", .{path.items}); + // for (path.items) |p| { + // std.debug.print("{any}\n", .{p}); + // } + + // for (0..path.items.len) |p| { + // const s = path.items[@mod(p, path.items.len)]; + // const n = path.items[@mod(p + 1, path.items.len)]; + // const x_from = @min(s[0], n[0]); + // const x_to = @max(s[0], n[0]); + // const y_from = @min(s[1], n[1]); + // const y_to = @max(s[1], n[1]); + // if (x_from == x_to) { + // for (y_from + 1..y_to) |y| { + // // map.set(x_from, y, 'x'); + // } + // } + // if (y_from == y_to) { + // std.debug.print("{d} -- {d}\n", .{ x_from, x_to }); + // for (x_from + 1..x_to) |x| { + // // map.set(x, y_from, 'x'); + // } + // } + // } + + // const test_points = [_]@Vector(2, u64){ + // .{ 4, 4 }, + // .{ 1, 1 }, + // .{ 5, 7 }, + // .{ 2, 5 }, + // .{ 2, 1 }, + // }; + // for (test_points) |p| { + // const orig = map.get(p[0], p[1]); + // map.set(p[0], p[1], '?'); + // const is_inside = insidePath(&path, p); + // std.debug.print("Is Inside? --> {any}\n", .{is_inside}); + // std.debug.print("{f}\n", .{map}); + // map.set(p[0], p[1], orig); + // } + + var largest_area: u64 = 0; + var a: @Vector(2, u64) = .{ 0, 0 }; + var b: @Vector(2, u64) = .{ 0, 0 }; + for (0..path.items.len) |i| { + const p = path.items[i]; + outer: for (0..path.items.len) |j| { + const q = path.items[j]; + if (p[0] == q[0] and p[1] == q[1]) continue; + const area = calcArea(p[0], q[0], p[1], q[1]); + const opposite_a_inside = insidePath(&path, .{ p[0], q[1] }); + const opposite_b_inside = insidePath(&path, .{ q[0], p[1] }); + // const p_orig = map.get(p[0], p[1]); + // const q_orig = map.get(q[0], q[1]); + // const u_orig = map.get(p[0], q[1]); + // const v_orig = map.get(q[0], p[1]); + // map.set(p[0], p[1], 'O'); + // map.set(q[0], q[1], 'O'); + // map.set(p[0], q[1], 'O'); + // map.set(q[0], p[1], 'O'); + // std.debug.print("Area: {d} [{d} -- {d}] - Outside: {any}, {any}?\n{f}\n", .{ + // area, + // i, + // j, + // opposite_a_inside, + // opposite_b_inside, + // map, + // }); + // map.set(p[0], p[1], p_orig); + // map.set(q[0], q[1], q_orig); + // map.set(p[0], q[1], u_orig); + // map.set(q[0], p[1], v_orig); + if (opposite_a_inside and opposite_b_inside and area > largest_area) { + var all_inside = true; + if (p[1] < q[1]) { + for (p[1]..q[1]) |y| { + const inside = insidePath(&path, .{ p[0], y }); + all_inside = all_inside and inside; + if (!all_inside) continue :outer; + } + } + if (q[1] < p[1]) { + for (q[1]..p[1]) |y| { + const inside = insidePath(&path, .{ p[0], y }); + all_inside = all_inside and inside; + if (!all_inside) continue :outer; + } + } + if (all_inside) { + largest_area = area; + a = p; + b = q; + } + } + } + } + try svg.startPolygon(); + try svg.addPolygonPoint(a[0], a[1]); + try svg.addPolygonPoint(a[0], b[1]); + try svg.addPolygonPoint(b[0], b[1]); + try svg.addPolygonPoint(b[0], a[1]); + try svg.endPolygon("red"); + + try svg.close(); + + std.debug.print("Area: {d}\n", .{largest_area}); } pub fn main() !void { @@ -23,6 +285,23 @@ pub fn main() !void { defer arena.deinit(); const allocator = arena.allocator(); - try aoc.runPart(allocator, part1); - // try aoc.runPart(allocator, part2); + // try aoc.runPart(allocator, part1); + try aoc.runPart(allocator, part2); +} + +test "simple test" { + var a: @Vector(2, u32) = .{ 7, 1 }; + var b: @Vector(2, u32) = .{ 11, 7 }; + try std.testing.expect(calcArea(a[0], b[0], a[1], b[1]) == 35); + try std.testing.expect(calcArea(b[0], a[0], b[1], a[1]) == 35); + + a = .{ 7, 3 }; + b = .{ 2, 3 }; + try std.testing.expect(calcArea(a[0], b[0], a[1], b[1]) == 6); + try std.testing.expect(calcArea(b[0], a[0], b[1], a[1]) == 6); + + a = .{ 2, 5 }; + b = .{ 11, 1 }; + try std.testing.expect(calcArea(a[0], b[0], a[1], b[1]) == 50); + try std.testing.expect(calcArea(b[0], a[0], b[1], a[1]) == 50); } diff --git a/modules/libs/src/datastructures.zig b/modules/libs/src/datastructures.zig deleted file mode 100644 index e69de29..0000000 diff --git a/modules/libs/src/datastructures/Map.zig b/modules/libs/src/datastructures/Map.zig new file mode 100644 index 0000000..a3ab1e4 --- /dev/null +++ b/modules/libs/src/datastructures/Map.zig @@ -0,0 +1,74 @@ +const std = @import("std"); +const t = @import("../term.zig"); + +const Allocator = std.mem.Allocator; + +pub const Map = struct { + rows: usize, + cols: usize, + buffer: []u8, + allocator: Allocator = undefined, + + pub fn init(allocator: Allocator, buffer: []const u8) !Map { + if (std.mem.indexOf(u8, buffer, "\n")) |line_end| { + const clean = std.mem.trimEnd(u8, buffer, "\n"); + return .{ + .allocator = allocator, + .buffer = try std.mem.replaceOwned(u8, allocator, clean, "\n", ""), + .cols = line_end, + .rows = (clean.len - 1) / line_end, + }; + } + @panic("Failed to split input buffer!"); + } + + pub fn initEmpty(allocator: Allocator, cols: usize, rows: usize, fill: u8) !Map { + const buffer = try allocator.alloc(u8, rows * cols); + for (0..(rows * cols)) |i| buffer[i] = fill; + return .{ + .allocator = allocator, + .buffer = buffer, + .cols = cols, + .rows = rows, + }; + } + + pub fn get(self: *const Map, x: usize, y: usize) u8 { + return self.buffer[(y * self.cols) + x]; + } + + pub fn set(self: *const Map, x: usize, y: usize, v: u8) void { + self.buffer[(y * self.cols) + x] = v; + } + + pub fn format(self: Map, writer: *std.Io.Writer) std.Io.Writer.Error!void { + try writer.print("Map {d} x {d}\n", .{ self.rows, self.cols }); + for (0..self.rows) |y| { + for (0..self.cols) |x| { + try writer.print("{c}", .{self.get(x, y)}); + } + try writer.print("\n", .{}); + } + } + + pub fn deinit(self: Map) void { + self.allocator.free(self.buffer); + } + + pub fn animate(self: Map) void { + std.debug.print(t.hide_cursor, .{}); + for (0..self.cols) |x| { + for (0..self.rows - 1) |y| { + std.debug.print(t.yx, .{ y, x }); + const v = self.get(x, y); + switch (v) { + '|' => std.debug.print("{s}{c}{s}", .{ t.yellow, v, t.clear }), + '^' => std.debug.print("{s}{c}{s}", .{ t.red, v, t.clear }), + '.' => std.debug.print("{s}{c}{s}", .{ t.dark_gray, v, t.clear }), + else => std.debug.print("{s}{c}{s}", .{ t.blue, v, t.clear }), + } + } + } + // std.Thread.sleep(std.time.ns_per_ms * 60); + } +}; diff --git a/modules/libs/src/root.zig b/modules/libs/src/root.zig index 62d28e9..a8bc577 100644 --- a/modules/libs/src/root.zig +++ b/modules/libs/src/root.zig @@ -1,4 +1,5 @@ pub const VectorSet = @import("datastructures/VectorSet.zig").VectorSet; +pub const Map = @import("datastructures/Map.zig").Map; pub const math = @import("math.zig"); pub const ppm = @import("ppm.zig"); pub const svg = @import("svg.zig"); diff --git a/modules/libs/src/svg.zig b/modules/libs/src/svg.zig index 7b323a3..4233dec 100644 --- a/modules/libs/src/svg.zig +++ b/modules/libs/src/svg.zig @@ -5,19 +5,17 @@ var file: fs.File = undefined; var buf: [128]u8 = undefined; pub fn init(file_path: []const u8, width: i128, height: i128, min_x: i128, max_x: i128, min_y: i128, max_y: i128) !void { - _ = max_y; - _ = min_y; - _ = max_x; - _ = min_x; file = try fs.cwd().createFile(file_path, .{}); - const margin_x: i128 = @intFromFloat(@as(f32, @floatFromInt(width)) * 0.1); - _ = margin_x; - const margin_y: i128 = @intFromFloat(@as(f32, @floatFromInt(height)) * 0.1); - _ = margin_y; - // const svg_el = try std.fmt.bufPrint(&buf, "", - // .{ -margin_x, -margin_y, width + 2 * margin_x, height + 2 * margin_y, width, height }); - const svg_el = try std.fmt.bufPrint(&buf, "", - .{ width, height }); + const svg_el = try std.fmt.bufPrint(&buf, "", .{ + min_x, + min_y, + max_x, + max_y, + width, + height, + }); + // const svg_el = try std.fmt.bufPrint(&buf, "", + // .{ width, height }); _ = try file.write(svg_el); } @@ -40,8 +38,9 @@ pub fn addPolygonPoint(x: i128, y: i128) !void { _ = try file.write(point); } -pub fn endPolygon() !void { - _ = try file.write("\" fill=\"black\" />"); +pub fn endPolygon(fill: []const u8) !void { + const end = try std.fmt.bufPrint(&buf, "\" fill=\"{s}\" />", .{fill}); + _ = try file.write(end); } pub fn close() !void { From a4d5d48d7fa67ccf32e6736c939d83e7ba1ceae3 Mon Sep 17 00:00:00 2001 From: Guido Schmidt Date: Thu, 18 Dec 2025 00:04:57 +0100 Subject: [PATCH 14/18] feat(2025.08): solve day 08 part 1 --- 2025/src/day08.zig | 180 ++++++++++++++++++++++++++++++------- modules/aoc/build.zig | 113 ++++++++++++++++++++++- modules/aoc/src/common.zig | 10 ++- 3 files changed, 270 insertions(+), 33 deletions(-) diff --git a/2025/src/day08.zig b/2025/src/day08.zig index 1e1f92f..962c050 100644 --- a/2025/src/day08.zig +++ b/2025/src/day08.zig @@ -7,36 +7,90 @@ const DAY: u5 = 8; const Allocator = std.mem.Allocator; const log = std.log; +fn Box(comptime T: type) type { + return struct { + id: usize, + pos: @Vector(3, T), + + pub fn format(self: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void { + try writer.print("[{d: >4}] {d: >6}", .{ self.id, self.pos }); + } + }; +} + +fn Connection(comptime T: type) type { + return struct { + a: Box(T), + b: Box(T), + d: T, + + pub fn format(self: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void { + try writer.print("{f}---{f} [distance: {d: >10.3}]", .{ self.a, self.b, self.d }); + } + }; +} + +const Circuit = struct { + ids: std.bit_set.DynamicBitSet, + + pub fn initEmpty(allocator: Allocator) !Circuit { + return Circuit{ + .ids = try std.bit_set.DynamicBitSet.initEmpty(allocator, 4096), + }; + } + + pub fn init(allocator: Allocator, start: Box(f32)) !Circuit { + var ids = try std.bit_set.DynamicBitSet.initEmpty(allocator, 4096); + ids.set(start.id); + return Circuit{ + .ids = ids, + }; + } + + pub fn contains(self: *const Circuit, b: Box(f32)) bool { + return self.ids.isSet(b.id); + } + + pub fn merge(self: *Circuit, c: Circuit) void { + var it = c.ids.iterator(.{}); + while (it.next()) |id| { + self.ids.set(id); + } + } + + pub fn format(self: Circuit, writer: *std.Io.Writer) std.Io.Writer.Error!void { + try writer.print("Circuit [{d}]\n", .{self.ids.count()}); + var it = self.ids.iterator(.{}); + while (it.next()) |c| { + std.debug.print(" + {d}\n", .{c}); + } + } + + pub fn deinit(self: *Circuit) void { + self.ids.deinit(); + } +}; + fn distance(comptime T: type, a: @Vector(3, T), b: @Vector(3, T)) T { const v = @abs(b - a); return @sqrt(@reduce(.Add, v * v)); } fn part1(allocator: Allocator) anyerror!void { - const input = @embedFile("example-08"); + const input = @embedFile("puzzle-08"); var reader: std.Io.Reader = .fixed(input); - const T: type = f32; - const Box = struct { - id: usize, - pos: @Vector(3, T), - - pub fn format(self: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void { - try writer.print("[{d: >4}] {d: >6}", .{ self.id, self.pos }); - } - }; - - var junction_boxes: std.array_list.Managed(Box) = .init(allocator); + var junction_boxes: std.array_list.Managed(Box(f32)) = .init(allocator); defer junction_boxes.deinit(); var idx: usize = 0; while (try reader.takeDelimiter('\n')) |line| { var coords_it = std.mem.splitScalar(u8, line, ','); - var coord: @Vector(3, T) = @splat(0); + var coord: @Vector(3, f32) = @splat(0); var i: usize = 0; while (coords_it.next()) |str| : (i += 1) { - const v = try std.fmt.parseFloat(T, str); + const v = try std.fmt.parseFloat(f32, str); std.debug.assert(v != 0); coord[i] = v; } @@ -48,24 +102,28 @@ fn part1(allocator: Allocator) anyerror!void { idx += 1; } - const Connection = struct { - a: Box, - b: Box, - d: T, - - pub fn format(self: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void { - try writer.print("{f}---{f} [distance: {d: >10.3}]", .{ self.a, self.b, self.d }); - } - }; - - var connections: std.array_list.Managed(Connection) = .init(allocator); + var connections: std.array_list.Managed(Connection(f32)) = .init(allocator); defer connections.deinit(); + var used_boxes: std.bit_set.DynamicBitSet = try .initEmpty(allocator, 4096); + defer used_boxes.deinit(); + var circuits: std.array_list.Managed(Circuit) = .init(allocator); + defer circuits.deinit(); + defer for (circuits.items) |*c| c.deinit(); + for (0..junction_boxes.items.len) |i| { for (i..junction_boxes.items.len) |j| { const a = junction_boxes.items[i]; const b = junction_boxes.items[j]; - const d = distance(T, a.pos, b.pos); + const d = distance(f32, a.pos, b.pos); + if (!used_boxes.isSet(a.id)) { + try circuits.append(try .init(allocator, a)); + used_boxes.set(a.id); + } + if (!used_boxes.isSet(b.id)) { + try circuits.append(try .init(allocator, b)); + used_boxes.set(b.id); + } if (d == 0) continue; try connections.append(.{ .a = a, @@ -75,17 +133,77 @@ fn part1(allocator: Allocator) anyerror!void { } } - std.mem.sort(Connection, connections.items, {}, comptime struct { - pub fn f(_: void, a: Connection, b: Connection) bool { + std.mem.sort(Connection(f32), connections.items, {}, comptime struct { + pub fn f(_: void, a: Connection(f32), b: Connection(f32)) bool { return a.d > b.d; } }.f); - while (connections.pop()) |next| { - std.debug.print("{f}\n", .{next}); + const limit: usize = 1000; + var i: usize = 0; + while (connections.pop()) |next| : (i += 1) { + if (i >= limit) break; + std.debug.print("[{d: >5}] {f}\n", .{ i, next }); + + var circuit_a: ?Circuit = null; + var count: usize = circuits.items.len; + for (0..count) |c| { + const circuit = circuits.items[c]; + if (circuit.contains(next.a) and circuit.contains(next.b)) continue; + if (circuit.contains(next.a)) { + circuit_a = circuits.swapRemove(c); + count = circuits.items.len; + break; + } + } + std.debug.print("Circuit A: {any}\n", .{circuit_a}); + + var circuit_b: ?Circuit = null; + for (0..circuits.items.len) |c| { + const circuit = circuits.items[c]; + if (circuit.contains(next.a) and circuit.contains(next.b)) continue; + if (circuit.contains(next.b)) { + circuit_b = circuits.swapRemove(c); + break; + } + } + std.debug.print("Circuit B: {any}\n", .{circuit_b}); + + if (circuit_a != null and circuit_b != null) { + std.debug.print(">>>>> Merge!\n", .{}); + defer circuit_a.?.deinit(); + defer circuit_b.?.deinit(); + var merged: Circuit = try .initEmpty(allocator); + merged.merge(circuit_a.?); + merged.merge(circuit_b.?); + try circuits.append(merged); + } + + // std.debug.print("\n------\n# Circuits: {d}\n", .{circuits.items.len}); + // for (0..circuits.items.len) |c| { + // std.debug.print("@{d} {f}\n", .{ c, circuits.items[c] }); + // } + // aoc.blockAskForNext(); + } + + std.mem.sort(Circuit, circuits.items, {}, comptime struct { + pub fn f(_: void, a: Circuit, b: Circuit) bool { + return a.ids.count() > b.ids.count(); + } + }.f); + + var result: usize = 1; + var total: usize = 0; + std.debug.print("\n------\n# Circuits: {d}\n", .{circuits.items.len}); + for (0..circuits.items.len) |c| { + if (c >= 3) break; + total += circuits.items[c].ids.count(); + result *= circuits.items[c].ids.count(); + std.debug.print("[{d: >3}]\n{f}\n", .{ c, circuits.items[c] }); } - // std.debug.print("--- INPUT---\n{s}\n------------\n", .{input}); + std.debug.print("Total connections: {d}\n", .{total}); + std.debug.print("Result: {d}\n", .{result}); } fn part2(allocator: Allocator) anyerror!void { diff --git a/modules/aoc/build.zig b/modules/aoc/build.zig index 6660b68..46652e3 100644 --- a/modules/aoc/build.zig +++ b/modules/aoc/build.zig @@ -3,6 +3,115 @@ const input = @import("src/input.zig"); pub const types = @import("src/types.zig"); const utils = @import("src/utils.zig"); +pub fn setupDay( + b: *std.Build, + target: std.Build.ResolvedTarget, + optimize: std.builtin.OptimizeMode, + YEAR: types.Year, + DAY: types.Day, +) !*std.Build.Step.Compile { + const dep_aoc = b.dependency("aoc", .{}); + const aoc_puzzleinput = dep_aoc.artifact("puzzle-input-helper"); + + const arg_year = try std.fmt.allocPrint(b.allocator, "{d}", .{YEAR}); + defer b.allocator.free(arg_year); + + const arg_day = try std.fmt.allocPrint(b.allocator, "{d}", .{DAY}); + defer b.allocator.free(arg_day); + + const arg_input_type_puzzle = try std.fmt.allocPrint(b.allocator, "{d}", .{ + @intFromEnum(types.PuzzleInput.PUZZLE), + }); + defer b.allocator.free(arg_input_type_puzzle); + + const arg_input_type_example = try std.fmt.allocPrint(b.allocator, "{d}", .{ + @intFromEnum(types.PuzzleInput.EXAMPLE), + }); + defer b.allocator.free(arg_input_type_puzzle); + + const cmd_puzzle_input = b.addRunArtifact(aoc_puzzleinput); + cmd_puzzle_input.addArgs(&.{ + arg_input_type_puzzle, + arg_year, + arg_day, + "input", + }); + const captured_output_puzzle = cmd_puzzle_input.captureStdOut(); // use this as anonymous import + + const cmd_example_input = b.addRunArtifact(aoc_puzzleinput); + cmd_example_input.addArgs(&.{ + arg_input_type_example, + arg_year, + arg_day, + "input", + }); + const captured_output_example = cmd_example_input.captureStdOut(); // use this as anonymous import + + const src_name = try std.fmt.allocPrint(b.allocator, "day{d:0>2}.zig", .{DAY}); + defer b.allocator.free(src_name); + const src_path = try std.fs.path.join(b.allocator, &.{ "src", src_name }); + defer b.allocator.free(src_path); + const exe_name = try std.fmt.allocPrint(b.allocator, "aoc-{d}-day{d:0>2}", .{ YEAR, DAY }); + defer b.allocator.free(exe_name); + + _ = std.fs.cwd().openFile(src_path, .{}) catch { + std.debug.print("{s} not found. Creating from template...\n", .{src_path}); + const contents = try createTemplate(b.allocator, DAY); + defer b.allocator.free(contents); + std.debug.print("{s}\n", .{contents}); + const src_file = try std.fs.cwd().createFile(src_path, .{}); + defer src_file.close(); + try src_file.writeAll(contents); + }; + + const exe = b.addExecutable(.{ + .name = exe_name, + .root_module = b.createModule(.{ + .root_source_file = b.path(src_path), + .optimize = optimize, + .target = target, + }), + }); + b.installArtifact(exe); + + const puzzle_input_name = try std.fmt.allocPrint(b.allocator, "puzzle-{d:0>2}", .{DAY}); + defer b.allocator.free(puzzle_input_name); + exe.root_module.addAnonymousImport(puzzle_input_name, .{ + .root_source_file = captured_output_puzzle, + }); + + const example_input_name = try std.fmt.allocPrint(b.allocator, "example-{d:0>2}", .{DAY}); + defer b.allocator.free(puzzle_input_name); + exe.root_module.addAnonymousImport(example_input_name, .{ + .root_source_file = captured_output_example, + }); + + exe.root_module.addImport("aoc", dep_aoc.module("aoc")); + + const dep_libs = b.dependency("libs", .{}); + exe.root_module.addImport("libs", dep_libs.module("libs")); + + const run_cmd = b.addRunArtifact(exe); + run_cmd.step.dependOn(b.getInstallStep()); + + const run_step = b.step("run", "Start the program"); + run_step.dependOn(&run_cmd.step); + + // Tests + const tests = b.addTest(.{ + .root_module = b.createModule(.{ + .root_source_file = b.path(src_path), + .target = target, + .optimize = optimize, + }), + }); + const run_tests = b.addRunArtifact(tests); + const test_step = b.step("test", "Run tests"); + test_step.dependOn(&run_tests.step); + + return exe; +} + pub fn createTemplate(allocator: std.mem.Allocator, day: types.Day) ![]const u8 { var template_str = utils.getTemplate(); std.debug.print("{any}\n", .{@TypeOf(template_str)}); @@ -10,6 +119,8 @@ pub fn createTemplate(allocator: std.mem.Allocator, day: types.Day) ![]const u8 const day_str = try std.fmt.allocPrint(allocator, "{d}", .{day}); defer allocator.free(day_str); template_str = try std.mem.replaceOwned(u8, allocator, template_str, search_DAY, day_str); + const day_input_num = try std.fmt.allocPrint(allocator, "{d:0>2}", .{day}); + defer allocator.free(day_str); const search_DAY_embedFile = "$DAY"; for (0..2) |_| { template_str = try std.mem.replaceOwned( @@ -17,7 +128,7 @@ pub fn createTemplate(allocator: std.mem.Allocator, day: types.Day) ![]const u8 allocator, template_str, search_DAY_embedFile, - day_str, + day_input_num, ); } return template_str; diff --git a/modules/aoc/src/common.zig b/modules/aoc/src/common.zig index 3b8f8d1..64bda7a 100644 --- a/modules/aoc/src/common.zig +++ b/modules/aoc/src/common.zig @@ -9,7 +9,15 @@ pub fn printTime(time: u64) void { const ns = time; const us: f64 = @floatFromInt(time / std.time.ns_per_us); const ms: f64 = @floatFromInt(time / std.time.ns_per_ms); - std.debug.print("\n— āŒ›ļø Running time: {d:3} ms / {d:3} μs / {d} ns\n", .{ ms, us, ns }); + const s: f64 = @floatFromInt(time / std.time.ns_per_s); + const min: f64 = @floatFromInt(time / std.time.ns_per_min); + std.debug.print("\n— āŒ›ļø Running time: {d:2}:{d:3} min / {d:3} ms / {d:3} μs / {d} ns\n", .{ + min, + s, + ms, + us, + ns, + }); } pub fn runPart( From b4231a9915913231e0c7bf552e8d5f7e1a856886 Mon Sep 17 00:00:00 2001 From: Guido Schmidt Date: Thu, 18 Dec 2025 00:23:21 +0100 Subject: [PATCH 15/18] refactor(.github): adjust github action for building all of 2025 --- .github/workflows/build.yml | 1 + 2025/build.zig | 122 +++++------------------------------- modules/aoc/build.zig | 12 ++-- 3 files changed, 23 insertions(+), 112 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d5ab199..6557ea9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,6 +24,7 @@ jobs: - name: Compile all solutions for current year run: zig build + working-directory: 2025 env: AOC_COOKIE: ${{ secrets.AOC_COOKIE }} diff --git a/2025/build.zig b/2025/build.zig index 23d2fc3..89b2ba3 100644 --- a/2025/build.zig +++ b/2025/build.zig @@ -11,113 +11,21 @@ pub fn build(b: *std.Build) !void { if (b.args) |args| { DAY = std.fmt.parseInt(u5, args[0], 10) catch 1; std.debug.print("šŸŽ…šŸ¼ Running day {d:0>2}\n", .{DAY}); + const exe = try aoc.setupDay(b, target, optimize, YEAR, DAY); + aoc.runDay(b, exe); } else { - // @TODO - // try aoc.getToday(); + const src_dir = try std.fs.cwd().openDir("src/", .{ + .no_follow = true, + .iterate = true, + .access_sub_paths = false, + }); + var it = src_dir.iterate(); + while (try it.next()) |f| { + if (f.name[0] == '.') continue; + const name = std.fs.path.stem(f.name); + const day = try std.fmt.parseInt(aoc.types.Day, name[3..], 10); + std.debug.print("šŸŽ…šŸ¼ Building day {d:0>2}\n", .{day}); + _ = try aoc.setupDay(b, target, optimize, YEAR, day); + } } - - // const cwd = try std.fs.cwd().realpathAlloc(b.allocator, "."); - // try aoc.fetchInputData(b, target, optimize, cwd, YEAR, DAY); - - const dep_aoc = b.dependency("aoc", .{}); - const aoc_puzzleinput = dep_aoc.artifact("puzzle-input-helper"); - - const arg_year = try std.fmt.allocPrint(b.allocator, "{d}", .{YEAR}); - defer b.allocator.free(arg_year); - - const arg_day = try std.fmt.allocPrint(b.allocator, "{d}", .{DAY}); - defer b.allocator.free(arg_day); - - const arg_input_type_puzzle = try std.fmt.allocPrint(b.allocator, "{d}", .{ - @intFromEnum(aoc.types.PuzzleInput.PUZZLE), - }); - defer b.allocator.free(arg_input_type_puzzle); - - const arg_input_type_example = try std.fmt.allocPrint(b.allocator, "{d}", .{ - @intFromEnum(aoc.types.PuzzleInput.EXAMPLE), - }); - defer b.allocator.free(arg_input_type_puzzle); - - const cmd_puzzle_input = b.addRunArtifact(aoc_puzzleinput); - cmd_puzzle_input.addArgs(&.{ - arg_input_type_puzzle, - arg_year, - arg_day, - "input", - }); - const captured_output_puzzle = cmd_puzzle_input.captureStdOut(); // use this as anonymous import - - const cmd_example_input = b.addRunArtifact(aoc_puzzleinput); - cmd_example_input.addArgs(&.{ - arg_input_type_example, - arg_year, - arg_day, - "input", - }); - const captured_output_example = cmd_example_input.captureStdOut(); // use this as anonymous import - - const src_name = try std.fmt.allocPrint(b.allocator, "day{d:0>2}.zig", .{DAY}); - defer b.allocator.free(src_name); - const src_path = try std.fs.path.join(b.allocator, &.{ "src", src_name }); - defer b.allocator.free(src_path); - const exe_name = try std.fmt.allocPrint(b.allocator, "aoc-{d}-day{d:0>2}", .{ YEAR, DAY }); - defer b.allocator.free(exe_name); - - _ = std.fs.cwd().openFile(src_path, .{}) catch { - std.debug.print("{s} not found. Creating from template...\n", .{src_path}); - const contents = try aoc.createTemplate(b.allocator, DAY); - defer b.allocator.free(contents); - std.debug.print("{s}\n", .{contents}); - const src_file = try std.fs.cwd().createFile(src_path, .{}); - defer src_file.close(); - try src_file.writeAll(contents); - }; - - const exe = b.addExecutable(.{ - .name = exe_name, - .root_module = b.createModule(.{ - .root_source_file = b.path(src_path), - .optimize = optimize, - .target = target, - }), - }); - b.installArtifact(exe); - - const puzzle_input_name = try std.fmt.allocPrint(b.allocator, "puzzle-{d:0>2}", .{DAY}); - defer b.allocator.free(puzzle_input_name); - exe.root_module.addAnonymousImport(puzzle_input_name, .{ - .root_source_file = captured_output_puzzle, - }); - - const example_input_name = try std.fmt.allocPrint(b.allocator, "example-{d:0>2}", .{DAY}); - defer b.allocator.free(puzzle_input_name); - exe.root_module.addAnonymousImport(example_input_name, .{ - .root_source_file = captured_output_example, - }); - - exe.root_module.addImport("aoc", dep_aoc.module("aoc")); - - const dep_libs = b.dependency("libs", .{}); - exe.root_module.addImport("libs", dep_libs.module("libs")); - - const run_cmd = b.addRunArtifact(exe); - run_cmd.step.dependOn(b.getInstallStep()); - - const run_step = b.step("run", "Start the program"); - run_step.dependOn(&run_cmd.step); - - // Tests - const tests = b.addTest(.{ - .root_module = b.createModule(.{ - .root_source_file = b.path(src_path), - .target = target, - .optimize = optimize, - }), - }); - // tests.root_module.addAnonymousImport(example_input_name, .{ - // .root_source_file = b.path(example_input_name), - // }); - const run_tests = b.addRunArtifact(tests); - const test_step = b.step("test", "Run tests"); - test_step.dependOn(&run_tests.step); } diff --git a/modules/aoc/build.zig b/modules/aoc/build.zig index 46652e3..fca51b9 100644 --- a/modules/aoc/build.zig +++ b/modules/aoc/build.zig @@ -91,6 +91,10 @@ pub fn setupDay( const dep_libs = b.dependency("libs", .{}); exe.root_module.addImport("libs", dep_libs.module("libs")); + return exe; +} + +pub fn runDay(b: *std.Build, exe: *std.Build.Step.Compile) void { const run_cmd = b.addRunArtifact(exe); run_cmd.step.dependOn(b.getInstallStep()); @@ -100,16 +104,14 @@ pub fn setupDay( // Tests const tests = b.addTest(.{ .root_module = b.createModule(.{ - .root_source_file = b.path(src_path), - .target = target, - .optimize = optimize, + .root_source_file = exe.root_module.root_source_file, + .target = exe.root_module.resolved_target, + .optimize = exe.root_module.optimize, }), }); const run_tests = b.addRunArtifact(tests); const test_step = b.step("test", "Run tests"); test_step.dependOn(&run_tests.step); - - return exe; } pub fn createTemplate(allocator: std.mem.Allocator, day: types.Day) ![]const u8 { From faf5ac526e2762d73e52a2c666bad78616f70239 Mon Sep 17 00:00:00 2001 From: Guido Schmidt Date: Thu, 18 Dec 2025 00:28:59 +0100 Subject: [PATCH 16/18] fix(2025): ensure input folders are present --- 2025/input/example/.gitkeep | 0 2025/input/puzzle/.gitkeep | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 2025/input/example/.gitkeep create mode 100644 2025/input/puzzle/.gitkeep diff --git a/2025/input/example/.gitkeep b/2025/input/example/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/2025/input/puzzle/.gitkeep b/2025/input/puzzle/.gitkeep new file mode 100644 index 0000000..e69de29 From 986c4a4a7b76763c035734c10213dfcedebde7e5 Mon Sep 17 00:00:00 2001 From: Guido Schmidt Date: Sat, 20 Dec 2025 13:52:04 +0100 Subject: [PATCH 17/18] feat(2025/day-11): solve day 11 part 1 --- 2025/src/day11.zig | 78 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 2025/src/day11.zig diff --git a/2025/src/day11.zig b/2025/src/day11.zig new file mode 100644 index 0000000..4b641d5 --- /dev/null +++ b/2025/src/day11.zig @@ -0,0 +1,78 @@ +const std = @import("std"); +const aoc = @import("aoc"); + +const DAY: u5 = 11; + +const Allocator = std.mem.Allocator; +const log = std.log; + +const Node = struct { + name: []const u8, + connections: std.array_list.Managed([]const u8), + + pub fn format(self: Node, writer: *std.Io.Writer) std.Io.Writer.Error!void { + try writer.print("{s}\n", .{self.name}); + for (self.connections.items) |c| { + try writer.print(" -- {s}\n", .{c}); + } + } +}; + +fn part1(allocator: Allocator) anyerror!void { + const input = @embedFile("puzzle-11"); + std.debug.print("--- INPUT---\n{s}\n------------\n", .{input}); + + var device_map: std.hash_map.StringHashMap(Node) = .init(allocator); + defer device_map.deinit(); + + var reader: std.Io.Reader = .fixed(input); + while (try reader.takeDelimiter('\n')) |line| { + const colon = std.mem.indexOf(u8, line, ":") orelse @panic(": not found"); + const device_name = line[0..colon]; + const connections = line[colon + 1 ..]; + var connections_it = std.mem.splitSequence(u8, connections, " "); + var node: Node = .{ + .name = device_name, + .connections = .init(allocator), + }; + while (connections_it.next()) |connection| { + if (connection.len == 0) continue; + try node.connections.append(connection); + } + try device_map.put(device_name[0..3], node); + } + + var stack: std.array_list.Managed(*Node) = .init(allocator); + defer stack.deinit(); + try stack.append(device_map.getPtr("you").?); + + var result: usize = 0; + while (stack.pop()) |next| { + std.debug.print("[{d}] Next: {f}\n", .{ stack.items.len, next }); + for (next.connections.items) |connection| { + if (std.mem.eql(u8, connection, "out")) { + result += 1; + continue; + } + if (device_map.getPtr(connection)) |ptr| { + try stack.append(ptr); + } + } + } + std.debug.print("Result: {d}\n", .{result}); +} + +fn part2(allocator: Allocator) anyerror!void { + _ = allocator; + const input = @embedFile("example-11"); + std.debug.print("--- INPUT---\n{s}\n------------\n", .{input}); +} + +pub fn main() !void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + + try aoc.runPart(allocator, part1); + // try aoc.runPart(allocator, part2); +} From f15d0ed20881cd788d59ec36c834609ccf9be88e Mon Sep 17 00:00:00 2001 From: Guido Schmidt Date: Sat, 20 Dec 2025 14:00:26 +0100 Subject: [PATCH 18/18] fix(modules/aoc): remove args count check for cross-platform support --- modules/aoc/src/fetch-puzzle-input.zig | 5 ----- 1 file changed, 5 deletions(-) diff --git a/modules/aoc/src/fetch-puzzle-input.zig b/modules/aoc/src/fetch-puzzle-input.zig index 2360cf3..3456c1e 100644 --- a/modules/aoc/src/fetch-puzzle-input.zig +++ b/modules/aoc/src/fetch-puzzle-input.zig @@ -19,11 +19,6 @@ pub fn main() !void { defer args.deinit(); _ = args.next(); // binary name - if (args.inner.count < 4) { - _ = try std.fs.File.stdout().write("Please provide 2 arguments: YEAR DAY, e.g. 2025 7"); - return; - } - const INPUT_TYPE: types.PuzzleInput = @enumFromInt(try std.fmt.parseInt( @typeInfo(types.PuzzleInput).@"enum".tag_type, args.next().?,