From eeaeb5350cd357cf69a91d051592c897a2cf075e Mon Sep 17 00:00:00 2001 From: Antoine Date: Wed, 4 Dec 2024 15:55:44 +0100 Subject: [PATCH] 2024 day 4 --- 2024/04/build.zig | 41 +++++++++++ 2024/04/build.zig.zon | 11 +++ 2024/04/solve.zig | 160 ++++++++++++++++++++++++++++++++++++++++++ README.md | 6 +- 4 files changed, 215 insertions(+), 3 deletions(-) create mode 100644 2024/04/build.zig create mode 100644 2024/04/build.zig.zon create mode 100644 2024/04/solve.zig diff --git a/2024/04/build.zig b/2024/04/build.zig new file mode 100644 index 0000000..c984712 --- /dev/null +++ b/2024/04/build.zig @@ -0,0 +1,41 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const utils = b.dependency("utils", .{ .target = target, .optimize = optimize }).module("utils"); + + const exe = b.addExecutable(.{ + .name = "day4", + .root_source_file = b.path("solve.zig"), + .target = target, + .optimize = optimize, + }); + + exe.root_module.addImport("utils", utils); + b.installArtifact(exe); + + { // Run + const run_step = b.step("run", "Run the app"); + const run_cmd = b.addRunArtifact(exe); + + run_cmd.step.dependOn(b.getInstallStep()); + if (b.args) |args| { + run_cmd.addArgs(args); + } + run_step.dependOn(&run_cmd.step); + } + { // Test + const test_step = b.step("test", "Run unit tests"); + const unit_tests = b.addTest(.{ + .root_source_file = b.path("solve.zig"), + .target = target, + .optimize = optimize, + }); + const run_unit_tests = b.addRunArtifact(unit_tests); + + unit_tests.root_module.addImport("utils", utils); + test_step.dependOn(&run_unit_tests.step); + } +} diff --git a/2024/04/build.zig.zon b/2024/04/build.zig.zon new file mode 100644 index 0000000..6a15935 --- /dev/null +++ b/2024/04/build.zig.zon @@ -0,0 +1,11 @@ +.{ + .name = "day04", + .version = "0.1.0", + .dependencies = .{ + .utils = .{ + .url = "../utils/", + .hash = "122059863bba3b73097c2905eacfa772e5c9ad7cd6616270b08dfdf41e3d81900420", + }, + }, + .paths = .{ "build.zig", "build.zig.zon", "solve.zig" }, +} diff --git a/2024/04/solve.zig b/2024/04/solve.zig new file mode 100644 index 0000000..1ce42be --- /dev/null +++ b/2024/04/solve.zig @@ -0,0 +1,160 @@ +const std = @import("std"); +const utils = @import("utils"); + +const PositionedLetter = std.AutoHashMap(Point, u8); +const Allocator = std.mem.Allocator; + +const Point = struct { + x: i32, + y: i32, + + pub fn add(self: Point, other: Point) Point { + return .{ .x = self.x + other.x, .y = self.y + other.y }; + } + + pub fn corners(self: Point) CornerIterator { + return .{ .center = self }; + } +}; + +/// Loop over the 8 possible directions +const directions: [8]Point = blk: { + var result: [8]Point = undefined; + + for (0..9) |i| { + result[i - (if (i > 4) 1 else 0)] = .{ .x = (@as(i32, i) % 3) - 1, .y = @as(i32, i) / 3 - 1 }; + } + break :blk result; +}; + +/// Infinite iteration from a point in a given direction +const RayIterator = struct { + current: Point, + move: Point, + + pub fn next(self: *RayIterator) ?Point { + self.current = self.current.add(self.move); + return self.current; + } +}; + +/// Iterate over the 4 corners of a given point +const CornerIterator = struct { + center: Point, + index: u8 = 0, + + const corners: [4]Point = .{ + .{ .x = -1, .y = -1 }, + .{ .x = 1, .y = -1 }, + .{ .x = 1, .y = 1 }, + .{ .x = -1, .y = 1 }, + }; + + pub fn next(self: *CornerIterator) ?Point { + if (self.index == 4) + return null; + const current = self.center.add(corners[self.index]); + self.index += 1; + return current; + } +}; + +/// Build the hashmap from an iterator of lines +pub fn parse(allocator: Allocator, input: anytype) !PositionedLetter { + var result = PositionedLetter.init(allocator); + var lines = utils.lineIterator(input); + var y: i32 = 0; + + while (lines.next()) |line| { + for (line, 0..line.len) |c, x| { + try result.put(.{ .x = @intCast(x), .y = y }, c); + } + y += 1; + } + return result; +} + +pub fn part1(input: PositionedLetter) u32 { + var iter = input.iterator(); + var result: u32 = 0; + + while (iter.next()) |letter| { + if (letter.value_ptr.* == 'X') { + for (directions) |direction| { + var ray: RayIterator = .{ .current = letter.key_ptr.*, .move = direction }; + var i: u32 = 0; + + while (ray.next()) |p| { + if (input.get(p) orelse break != "MAS"[i]) + break; + i += 1; + if (i > 2) { + result += 1; + break; + } + } + } + } + } + return result; +} + +pub fn part2(input: PositionedLetter) u32 { + var iter = input.iterator(); + var result: u32 = 0; + + outer: while (iter.next()) |letter| { + if (letter.value_ptr.* == 'A') { + var corners = letter.key_ptr.corners(); + var surroundings: [4]u8 = undefined; + var i: u32 = 0; + + while (corners.next()) |corner| { + const c = input.get(corner) orelse continue :outer; + surroundings[i] = c; + i += 1; + } + if (std.mem.indexOf(u8, "MSSMMSS", &surroundings) != null) { + result += 1; + } + } + } + return result; +} + +pub fn main() !void { + var gpa: std.heap.GeneralPurposeAllocator(.{}) = .{}; + defer _ = gpa.deinit(); + const allocator = gpa.allocator(); + + var stdin = std.io.bufferedReader(std.io.getStdIn().reader()); + + var input = try parse(allocator, stdin.reader()); + defer input.deinit(); + + std.debug.print("Number of 'XMAS' : {:5}\n", .{part1(input)}); + std.debug.print("Number of X 'MAS': {:5}\n", .{part2(input)}); +} + +// -------------------- Tests -------------------- + +test part1 { + const sample = + \\MMMSXXMASM + \\MSAMXMSMSA + \\AMXSXMAAMM + \\MSAMASMSMX + \\XMASAMXAMM + \\XXAMMXXAMA + \\SMSMSASXSS + \\SAXAMASAAA + \\MAMMMXMMMM + \\MXMXAXMASX + ; + var stream = std.io.fixedBufferStream(sample); + var input = try parse(std.testing.allocator, stream.reader()); + defer input.deinit(); + + try std.testing.expectEqual(18, part1(input)); + try std.testing.expectEqual(9, part2(input)); +} diff --git a/README.md b/README.md index 528e257..9a3173e 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ My solutions for the [Advent of Code](https://adventofcode.com), a challenge sta | 01 |:star::star:|:star::star:|:star::star:|:star::star:|:star::star:|:star::star:|:star::star:|:star::star:|:star::star: | 02 |:star::star:|:star::star:|:star::star:|:star::star:|:star::star:|:star::star:|:star::star:|:star::star:|:star::star: | 03 |:star::star:|:star::star:||:star::star:|:star::star:|:star::star:|:star::star:|:star::star:|:star::star: -| 04 |:star::star:|:star::star:|||:star::star:|:star::star:|:star::star:|:star::star:| +| 04 |:star::star:|:star::star:|||:star::star:|:star::star:|:star::star:|:star::star:|:star::star: | 05 |:star::star:|:star::star:|||:star::star:|:star::star:|:star::star:|:star::star:| | 06 ||:star::star:|||:star::star:|:star::star:|:star::star:|:star::star:| | 07 ||:star::star:|||:star::star:|:star::star:|:star::star:|:star::star:| @@ -30,6 +30,6 @@ My solutions for the [Advent of Code](https://adventofcode.com), a challenge sta | 23 |||||||:star::star:|:star:| | 24 ||||||:star::star:|:star::star:|:star:| | 25 |||||:star:|:star:|:star::star:|| -| Total | 10 | 14 | 4 | 6 | 42 | 44 | 50 | 44 | 6 +| Total | 10 | 14 | 4 | 6 | 42 | 44 | 50 | 44 | 8 -Total stars: 220 +Total stars: 222