AoC 2024: Day 1

Hey all, I’ve decided to add a singular “main” discussion thread for general discussion around Advent of Code. Feel free to discuss the challenges and ask questions. If particular discussions become large, we can break them off into new threads.

Some Rules:

  1. Please try to keep spoilers within spoiler tags. This is done by typing [spoiler]text to be blurred[\spoiler]. If you have longer sections you can also use the [details=“Title”] tags.
  2. Have Fun

Day 1 Challenge

Templates:

Resources:

1910115-f465b6d3

5 Likes

At some point in the past, Andrew created a private leaderboard for ziggers. This is it, in case anybody wants to join.

@Calder-Ty it is not obvious to me whether you are in this leaderboard.

1 Like
My solution
const std = @import("std");

const real_data = @embedFile("data/day01.txt");

pub fn solution_part_1(data: []const u8) !u32 {
    var gpa = std.heap.GeneralPurposeAllocator(.{}).init;
    const allocator = gpa.allocator();
    var left_numbers = std.ArrayList(u32).init(allocator);
    var right_numbers = std.ArrayList(u32).init(allocator);

    var token_iter = std.mem.tokenizeAny(u8, data, &.{ '\n', ' ', 0x00 });

    var alternate_list_side: bool = true; // starts on left side of list
    while (token_iter.next()) |token| {
        // std.debug.print("token: {s}\n", .{token});
        if (alternate_list_side) {
            try left_numbers.append(try std.fmt.parseInt(u32, token, 10));
        } else {
            try right_numbers.append(try std.fmt.parseInt(u32, token, 10));
        }
        alternate_list_side = !alternate_list_side;
    }

    std.mem.sort(u32, left_numbers.items, void{}, std.sort.asc(u32));
    std.mem.sort(u32, right_numbers.items, void{}, std.sort.asc(u32));

    var sum: u32 = 0;
    for (left_numbers.items, right_numbers.items) |left, right| {
        sum += @max(left, right) - @min(left, right);
    }

    return sum;
}

pub fn solution_part_2(data: []const u8) !u32 {
    var gpa = std.heap.GeneralPurposeAllocator(.{}).init;
    const allocator = gpa.allocator();
    var left_numbers = std.ArrayList(u32).init(allocator);
    var right_numbers = std.ArrayList(u32).init(allocator);

    var token_iter = std.mem.tokenizeAny(u8, data, &.{ '\n', ' ', 0x00 });

    var alternate_list_side: bool = true; // starts on left side of list
    while (token_iter.next()) |token| {
        // std.debug.print("token: {s}\n", .{token});
        if (alternate_list_side) {
            try left_numbers.append(try std.fmt.parseInt(u32, token, 10));
        } else {
            try right_numbers.append(try std.fmt.parseInt(u32, token, 10));
        }
        alternate_list_side = !alternate_list_side;
    }

    std.mem.sort(u32, left_numbers.items, void{}, std.sort.asc(u32));
    std.mem.sort(u32, right_numbers.items, void{}, std.sort.asc(u32));

    var sum: u32 = 0;
    for (left_numbers.items) |left| {
        sum += left * @as(u32, @intCast(std.mem.count(u32, right_numbers.items, &.{left})));
    }
    return sum;
}

test {
    const data =
        \\3   4
        \\4   3
        \\2   5
        \\1   3
        \\3   9
        \\3   3
    ;
    try std.testing.expectEqual(@as(u32, 11), try solution_part_1(data));
    try std.testing.expectEqual(@as(u32, 31), try solution_part_2(data));
}

pub fn main() !void {
    std.debug.print("solution part 1: {any}\n", .{solution_part_1(real_data)});
    std.debug.print("solution part 2: {any}\n", .{solution_part_2(real_data)});
}

// Generated from template/template.zig.
// Run `zig build generate` to update.
// Only unmodified days will be updated.

I learned:

  • multiline literal syntax
  • @embedFile, what a cool feature! should learn how to actually read a file though…
  • standard library:
    • tokenization
    • sorting
    • counting

I have a suspicion my implementation could be a lot faster or more efficient but it works!

1 Like

My day 1:

Part One
const std = @import("std");

const input = @embedFile("input.txt");

pub fn main() !void {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();

    var left_list = std.ArrayList(u32).init(arena.allocator());
    var right_list = std.ArrayList(u32).init(arena.allocator());

    var line_iter = std.mem.tokenizeScalar(u8, input, '\n');
    while (line_iter.next()) |line| {
        const num_iter = std.mem.tokenizeScalar(u8, line, ' ');
        const left_location = try std.fmt.parseInt(u32, num_iter.next().?, 10);
        const right_location = try std.fmt.parseInt(u32, num_iter.next().?, 10);

        try left_list.append(left_location);
        try right_list.append(right_location);
    }

    std.sort.pdq(u32, left_list.items, {}, lessThan);
    std.sort.pdq(u32, right_list.items, {}, lessThan);

    var total_distance: u64 = 0;
    for (left_list.items, right_list.items) |left, right| {
        total_distance += if (left > right) left - right else right - left;
    }

    try std.io.getStdOut().writer()
        .print("total: {d}\n", .{total_distance});
}

fn lessThan(_: void, a: u32, b: u32) bool {
    return a < b;
}
Part Two
const std = @import("std");

const input = @embedFile("input.txt");

pub fn main() !void {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();

    var left_locations = std.AutoArrayHashMap(u32, void).init(arena.allocator());
    var right_occurrences = std.AutoArrayHashMap(u32, u16).init(arena.allocator());

    var lines_iter = std.mem.tokenizeScalar(u8, input, '\n');
    while (lines_iter.next()) |line| {
        var num_iter = std.mem.tokenizeScalar(u8, line, ' ');
        const left_location = try std.fmt.parseInt(u32, num_iter.next().?, 10);
        const right_location = try std.fmt.parseInt(u32, num_iter.next().?, 10);

        try left_locations.put(left_location, {});
        const get = try right_occurrences.getOrPutValue(right_location, 1);
        if (get.found_existing) {
            get.value_ptr.* += 1;
        }
    }

    var left_iter = left_locations.iterator();

    var similarity: u32 = 0;
    while (left_iter.next()) |entry| {
        const location = entry.key_ptr.*;
        const occurrences: u32 = @intCast(right_occurrences.get(location) orelse 0);
        similarity += location * occurrences;
    }

    std.debug.print("similarity: {d}\n", .{similarity});
}

First time using Zig’s hashmaps I think, had to make the choice between AutoHashMap and AutoArrayHashmap and I went with the array version by intuition. Can anyone provide some rational for choosing one over the other?

1 Like

my day1 solution https://zigbin.io/885f4a

EDIT:

comptime only solution https://zigbin.io/210853

1 Like

Ok sorry for the pollution I couldn’t get the post to work:

here’s my day1-part1 and here’s my day1-part2

Honestly it was very easy, I’m not sure if this is the best approach, I went with a counting array method.

1 Like
My Solution
const std = @import("std");
const fs = std.fs;
const fmt = std.fmt;
const sort = std.sort;
const Allocator = std.mem.Allocator;

const max_read = 15 * 1024;

pub fn main() !void {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const allocator = arena.allocator();
    const rawdata = try loadData(allocator, "data/prod.txt");

    var iter = std.mem.tokenizeAny(u8, rawdata, "\n ");
    // input is C, C \n
    var list1 = std.ArrayList(usize).init(allocator);
    var list2 = std.ArrayList(usize).init(allocator);
    var counter = std.AutoHashMap(usize, usize).init(allocator);

    var i: usize = 0;
    while (iter.next()) |id| : (i += 1) {
        if (i % 2 == 0) {
            try list1.append(try fmt.parseInt(usize, id, 10));
        } else {
            try list2.append(try fmt.parseInt(usize, id, 10));
            const entry = try counter.getOrPut(try fmt.parseInt(usize, id, 10));
            if (entry.found_existing) {
                entry.value_ptr.* += 1;
            } else {
                entry.value_ptr.* = 1;
            }
        }
    }
    sort.pdq(usize, list1.items, .{}, lessThan);
    sort.pdq(usize, list2.items, .{}, lessThan);

    // compute distances
    var distance: usize = 0;
    for (list1.items, list2.items) |a, b| {
        if (a > b) {
            distance += a - b;
        } else {
            distance += b - a;
        }
    }

    std.debug.print("Total Distance is {d}\n", .{distance});
    // compute Similarity
    var similarity: usize = 0;
    for (list1.items) |a| {
        const mult = counter.get(a) orelse 0;
        similarity += mult * a;
    }

    std.debug.print("Similarity Metric is {d}\n", .{similarity});
}

/// Load the Data from path
fn loadData(allocator: Allocator, path: []const u8) ![]u8 {
    const fd = try fs.cwd().openFile(path, .{});
    return try fd.readToEndAlloc(allocator, max_read);
}

fn lessThan(_: @TypeOf(.{}), a: usize, b: usize) bool {
    return a < b;
}

A fairly easy logical problem, to break into the month. Discovered some new modules like sort.

I’m going to try a few solutions this year in Zig. I spent some time with this one, to break it into readable functions. I wrote up some notes about my solution: Aoc2024Day1

My solution
const std = @import("std");

pub fn main() !void {
    // get allocator
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    const allocator = arena.allocator();
    defer _ = arena.deinit();

    // get command-line args
    const args = try std.process.argsAlloc(allocator);
    defer std.process.argsFree(allocator, args);

    // see if filename is on command-line
    if (args.len == 2) {
        const filename = args[1];
        solveDay(allocator, filename) catch |err| {
            switch (err) {
                error.FileNotFound => {
                    std.debug.print("Error: File {s} was not found.\n", .{filename});
                    std.process.exit(1);
                },
                else => {
                    std.debug.print("Error: in processing file.\n", .{});
                    std.process.exit(1);
                },
            }
        };
    } else {
        std.debug.print("Provide a filename of input data.\n", .{});
    }
}

fn solveDay (allocator: std.mem.Allocator, filename: []const u8) !void {
    // put the numbers in two lists, one per column
    var column1 = std.ArrayList(usize).init(allocator);
    var column2 = std.ArrayList(usize).init(allocator);

    try readData(allocator, filename, &column1, &column2);

    // sort the two columns
    std.mem.sort(usize, column1.items, {}, comptime std.sort.asc(usize));
    std.mem.sort(usize, column2.items, {}, comptime std.sort.asc(usize));

    std.debug.print("Part 1: {d}\n", .{totalDifference(&column1, &column2)});
    std.debug.print("Part 2: {d}\n", .{totalSimilarity(&column1, &column2)});
}

fn totalDifference(column1: *std.ArrayList(usize), column2: *std.ArrayList(usize)) usize {
    var result : usize = 0;
    
    for (column1.items, column2.items) |left, right| {
        if (left > right) {
            result += left - right;
        } else {
            result += right - left;
        }
    }

    return result;
}

fn totalSimilarity(column1: *std.ArrayList(usize), column2: *std.ArrayList(usize)) usize {
    var result: usize = 0;

    for (column1.items) |left| {
        result += left * std.mem.count(usize, column2.items, &[_]usize{left});
    }

    return result;
}

// Read in the two columns of data from given filename
fn readData (allocator: std.mem.Allocator, filename: []const u8, 
    column1: *std.ArrayList(usize), column2: *std.ArrayList(usize)) !void {

    const file = try std.fs.cwd().openFile(filename, .{});
    defer file.close();
    const stat = try file.stat();
    const data = try file.readToEndAlloc(allocator, stat.size);

    var lines = std.mem.tokenize(u8, data, "\n");
    while (lines.next()) |line| {
        var columns = std.mem.tokenize(u8, line, " ");
        const number1 = try std.fmt.parseInt(usize, columns.next() orelse return, 10);
        const number2 = try std.fmt.parseInt(usize, columns.next() orelse return, 10);
        try column1.append(number1);
        try column2.append(number2);
    }
}
1 Like

Just started, here is my repo: