From 1889eb00c43b58bdacff30c00d96394a173c028e Mon Sep 17 00:00:00 2001 From: Altagos Date: Wed, 13 Nov 2024 15:45:35 +0100 Subject: [PATCH] day 1 --- .gitignore | 5 +++ build.zig | 76 ++++++++++++++++++++++++++++++++++++++++++++ build.zig.zon | 72 ++++++++++++++++++++++++++++++++++++++++++ src/days.zig | 18 +++++++++++ src/days/day1.zig | 80 +++++++++++++++++++++++++++++++++++++++++++++++ src/main.zig | 56 +++++++++++++++++++++++++++++++++ src/root.zig | 13 ++++++++ 7 files changed, 320 insertions(+) create mode 100644 .gitignore create mode 100644 build.zig create mode 100644 build.zig.zon create mode 100644 src/days.zig create mode 100644 src/days/day1.zig create mode 100644 src/main.zig create mode 100644 src/root.zig diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..79bbf96 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*zig-cache +*zig-out + +# Input files +src/days/inputs/* diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..ef38018 --- /dev/null +++ b/build.zig @@ -0,0 +1,76 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const options = b.addOptions(); + + const day = b.option(u8, "day", "Which day to run"); + const day_path: ?[]const u8 = blk: { + if (day) |d| { + const dp = std.fmt.allocPrint(b.allocator, "days/day{}.zig", .{d}) catch |err| { + std.log.err("Unable to create path to file day{}: {}", .{ d, err }); + return; + }; + break :blk dp; + } + break :blk null; + }; + + options.addOption(?[]const u8, "day_path", day_path); + options.addOption(?u8, "day", day); + + const lib = b.addStaticLibrary(.{ + .name = "aoc-2022", + .root_source_file = b.path("src/root.zig"), + .target = target, + .optimize = optimize, + }); + + b.installArtifact(lib); + + const exe = b.addExecutable(.{ + .name = "aoc-2022", + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }); + exe.root_module.addOptions("build-options", options); + b.installArtifact(exe); + + const run_cmd = b.addRunArtifact(exe); + + run_cmd.step.dependOn(b.getInstallStep()); + + if (b.args) |args| { + run_cmd.addArgs(args); + } + + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); + + const exe_unit_tests = b.addTest(.{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }); + + const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); + const test_step = b.step("test", "Run unit tests"); + test_step.dependOn(&run_exe_unit_tests.step); + + if (day) |d| { + const day_path_test = std.fmt.allocPrint(b.allocator, "src/days/day{}.zig", .{d}) catch |err| { + std.log.err("Unable to create path to file day{}: {}", .{ d, err }); + return; + }; + const day_unit_tests = b.addTest(.{ + .optimize = optimize, + .target = target, + .root_source_file = b.path(day_path_test), + }); + const run_day_unit_tests = b.addRunArtifact(day_unit_tests); + test_step.dependOn(&run_day_unit_tests.step); + } +} diff --git a/build.zig.zon b/build.zig.zon new file mode 100644 index 0000000..1463d58 --- /dev/null +++ b/build.zig.zon @@ -0,0 +1,72 @@ +.{ + // This is the default name used by packages depending on this one. For + // example, when a user runs `zig fetch --save `, this field is used + // as the key in the `dependencies` table. Although the user can choose a + // different name, most users will stick with this provided value. + // + // It is redundant to include "zig" in this name because it is already + // within the Zig package namespace. + .name = "aoc-2022", + + // This is a [Semantic Version](https://semver.org/). + // In a future version of Zig it will be used for package deduplication. + .version = "0.0.0", + + // This field is optional. + // This is currently advisory only; Zig does not yet do anything + // with this value. + //.minimum_zig_version = "0.11.0", + + // This field is optional. + // Each dependency must either provide a `url` and `hash`, or a `path`. + // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. + // Once all dependencies are fetched, `zig build` no longer requires + // internet connectivity. + .dependencies = .{ + // See `zig fetch --save ` for a command-line interface for adding dependencies. + //.example = .{ + // // When updating this field to a new URL, be sure to delete the corresponding + // // `hash`, otherwise you are communicating that you expect to find the old hash at + // // the new URL. + // .url = "https://example.com/foo.tar.gz", + // + // // This is computed from the file contents of the directory of files that is + // // obtained after fetching `url` and applying the inclusion rules given by + // // `paths`. + // // + // // This field is the source of truth; packages do not come from a `url`; they + // // come from a `hash`. `url` is just one of many possible mirrors for how to + // // obtain a package matching this `hash`. + // // + // // Uses the [multihash](https://multiformats.io/multihash/) format. + // .hash = "...", + // + // // When this is provided, the package is found in a directory relative to the + // // build root. In this case the package's hash is irrelevant and therefore not + // // computed. This field and `url` are mutually exclusive. + // .path = "foo", + // + // // When this is set to `true`, a package is declared to be lazily + // // fetched. This makes the dependency only get fetched if it is + // // actually used. + // .lazy = false, + //}, + }, + + // Specifies the set of files and directories that are included in this package. + // Only files and directories listed here are included in the `hash` that + // is computed for this package. Only files listed here will remain on disk + // when using the zig package manager. As a rule of thumb, one should list + // files required for compilation plus any license(s). + // Paths are relative to the build root. Use the empty string (`""`) to refer to + // the build root itself. + // A directory listed here means that all files within, recursively, are included. + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + // For example... + //"LICENSE", + //"README.md", + }, +} diff --git a/src/days.zig b/src/days.zig new file mode 100644 index 0000000..88c186f --- /dev/null +++ b/src/days.zig @@ -0,0 +1,18 @@ +const std = @import("std"); +const mem = std.mem; + +const options = @import("build-options"); + +pub const day1 = @import("days/day1.zig"); + +const days = .{day1}; + +pub const run = blk: { + if (options.day) |d| break :blk days[d - 1].run; + break :blk r; +}; + +fn r(alloc: mem.Allocator, progress: *std.Progress.Node) !void { + _ = alloc; + _ = progress; +} diff --git a/src/days/day1.zig b/src/days/day1.zig new file mode 100644 index 0000000..2c2c214 --- /dev/null +++ b/src/days/day1.zig @@ -0,0 +1,80 @@ +const std = @import("std"); +const mem = std.mem; + +const input = @embedFile("inputs/day01.txt"); + +pub fn run(alloc: mem.Allocator, progress: *std.Progress.Node) !void { + const n1 = progress.start("Parsing", 0); + + const parsed = try parse(alloc, input); + defer parsed.deinit(); + + n1.end(); + + const n2 = progress.start("Running", 0); + defer n2.end(); + + std.mem.sort(u64, parsed.items, .{}, BiggerThan(u64).sort); + + std.log.info("Result Part 1: {}", .{parsed.items[0]}); + std.log.info("Result Part 2: {}", .{parsed.items[0] + parsed.items[1] + parsed.items[2]}); +} + +pub fn parse(alloc: mem.Allocator, buffer: []const u8) !std.ArrayList(u64) { + var ret = std.ArrayList(u64).init(alloc); + + var idx: usize = 0; + var block_iter = std.mem.splitSequence(u8, buffer, "\n"); + while (block_iter.next()) |block| { + if (std.mem.eql(u8, block, "")) { + idx += 1; + try ret.append(0); + continue; + } + + const cal = try std.fmt.parseUnsigned(u64, block, 0); + if (ret.popOrNull()) |current| { + try ret.insert(idx, current + cal); + } else { + try ret.insert(idx, cal); + } + } + + return ret; +} + +fn BiggerThan(comptime T: type) type { + return struct { + fn sort(_: @TypeOf(.{}), lhs: T, rhs: T) bool { + return lhs > rhs; + } + }; +} + +test "test" { + const testing = std.testing; + + const test_input = + \\1000 + \\2000 + \\3000 + \\ + \\4000 + \\ + \\5000 + \\6000 + \\ + \\7000 + \\8000 + \\9000 + \\ + \\10000 + ; + + const parsed = try parse(testing.allocator, test_input); + defer parsed.deinit(); + + std.mem.sort(u64, parsed.items, .{}, BiggerThan(u64).sort); + + try testing.expectEqual(parsed.items[0], 24000); +} diff --git a/src/main.zig b/src/main.zig new file mode 100644 index 0000000..059e6d1 --- /dev/null +++ b/src/main.zig @@ -0,0 +1,56 @@ +const std = @import("std"); + +pub const std_options = std.Options{ + .log_level = .debug, +}; + +const options = @import("build-options"); +const days = @import("days.zig"); + +pub fn main() !void { + const day = blk: { + if (options.day) |d| break :blk d; + + std.log.err("please specify which day to build with the `-Dday` flag", .{}); + return; + }; + + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + const allocator = gpa.allocator(); + defer { + const deinit_status = gpa.deinit(); + if (deinit_status == .leak) @panic("Memory leak"); + } + + const day_str = try std.fmt.allocPrint(allocator, "Day {} - Advent of Code 2022", .{day}); + defer allocator.free(day_str); + + var progress = std.Progress.start(.{ .root_name = day_str }); + defer progress.end(); + + var timer = try std.time.Timer.start(); + try days.run(allocator, &progress); + const excution_time = timer.lap(); + printTimerResult(excution_time); +} + +fn printTimerResult(t: u64) void { + var rt = t; + + const hours = rt / std.time.ns_per_hour; + rt = rt - (hours * std.time.ns_per_hour); + + const minutes = rt / std.time.ns_per_min; + rt = rt - (minutes * std.time.ns_per_min); + + const seconds = rt / std.time.ns_per_s; + rt = rt - (seconds * std.time.ns_per_s); + + const ms = rt / std.time.ns_per_ms; + rt = rt - (ms * std.time.ns_per_ms); + + std.log.info( + "Executed in: {}h {}m {}s {}ms (raw ms: {}ms)", + .{ hours, minutes, seconds, ms, t / std.time.ns_per_ms }, + ); +} diff --git a/src/root.zig b/src/root.zig new file mode 100644 index 0000000..f034ca3 --- /dev/null +++ b/src/root.zig @@ -0,0 +1,13 @@ +//! By convention, root.zig is the root source file when making a library. If +//! you are making an executable, the convention is to delete this file and +//! start with main.zig instead. +const std = @import("std"); +const testing = std.testing; + +export fn add(a: i32, b: i32) i32 { + return a + b; +} + +test "basic add functionality" { + try testing.expect(add(3, 7) == 10); +}