Skip Navigation

🌲 - 2024 DAY 1 SOLUTIONS -🌲

Day 1: Historian Hysteria

Megathread guidelines

  • Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
  • You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://blocks.programming.dev/ if you prefer sending it through a URL

FAQ

39 comments
  • Uiua

    For entertainment purposes only, I'll be trying a solution in Uiua each day until it all gets too much for me...

     undefined
        
    $ 3   4
    $ 4   3
    $ 2   5
    $ 1   3
    $ 3   9
    $ 3   3
    βŠœβˆ˜βŠΈβ‰ @\n     # Partition at \n.
    ⊜(β†βˆ΅β‹•)βŠΈβ‰ @\s # Partition at space, parse ints, sort.
    
    &p/+/(⌡-). # Part1 : Get abs differences, sum, print.
    
    &p/+Γ—βŸœ(/+β‰β‰‘βŒ•)Β°βŠ‚ # Part 2 : Count instances, mul out, sum, print.
    
      
  • Haskell

    Plenty of scope for making part 2 faster, but I think simple is best here. Forgot to sort the lists in the first part, which pushed me waaay off the leaderboard.

     undefined
        
    import Data.List
    
    main = do
      [as, bs] <- transpose . map (map read . words) . lines <$> readFile "input01"
      print . sum $ map abs $ zipWith (-) (sort as) (sort bs)
      print . sum $ map (\a -> a * length (filter (== a) bs)) as
    
      
  • Nim

    I've got my first sub-1000 rank today (998 for part 2). Yay!
    Simple and straightforward challenge, very fitting for 1st day. Gonna enjoy it while it lasts.

     nim
        
    proc solve(input: string): AOCSolution[int, int] =
      var l1,l2: seq[int]
      for line in input.splitLines():
        let pair = line.splitWhitespace()
        l1.add parseInt(pair[0])
        l2.add parseInt(pair[1])
      l1.sort()
      l2.sort()
    
      block p1:
        for i in 0..l1.high:
          result.part1 += abs(l1[i] - l2[i])
    
      block p2:
        for n in l1:
          result.part2 += n * l2.count(n)
    
    
      

    Codeberg repo

  • Rust

    Right IDs are directly read into a hash map counter.

     rust
        
    use std::str::FromStr;
    use std::collections::HashMap;
    
    fn part1(input: String) {
        let mut left = Vec::new();
        let mut right = Vec::new();
        for line in input.lines() {
            let mut parts = line.split_whitespace()
                .map(|p| u32::from_str(p).unwrap());
            left.push(parts.next().unwrap());
            right.push(parts.next().unwrap());
        }
        left.sort_unstable();
        right.sort_unstable();
        let diff: u32 = left.iter().zip(right)
            .map(|(l, r)| l.abs_diff(r))
            .sum();
        println!("{diff}");
    }
    
    fn part2(input: String) {
        let mut left = Vec::new();
        let mut right: HashMap<u32, u32> = HashMap::new();
        for line in input.lines() {
            let mut parts = line.split_whitespace()
                .map(|p| u32::from_str(p).unwrap());
            left.push(parts.next().unwrap());
            *right.entry(parts.next().unwrap()).or_default() += 1;
        }
        let similar: u32 = left.iter()
            .map(|n| n * right.get(n).copied().unwrap_or_default())
            .sum();
        println!("{similar}");
    }
    
    util::aoc_main!();
    
      
  • Kotlin

    No πŸ’œ for Kotlin here?

     undefined
        
    import kotlin.math.abs
    
    fun part1(input: String): Int {
        val diffs: MutableList<Int> = mutableListOf()
        val pair = parse(input)
        pair.first.sort()
        pair.second.sort()
        pair.first.forEachIndexed { idx, num ->
            diffs.add(abs(num - pair.second[idx]))
        }
        return diffs.sum()
    }
    
    fun part2(input: String): Int {
        val pair = parse(input)
        val frequencies = pair.second.groupingBy { it }.eachCount()
        var score = 0
        pair.first.forEach { num ->
            score += num * frequencies.getOrDefault(num, 0)
        }
        return score
    }
    
    private fun parse(input: String): Pair<MutableList<Int>, MutableList<Int>> {
        val left: MutableList<Int> = mutableListOf()
        val right: MutableList<Int> = mutableListOf()
        input.lines().forEach { line ->
            if (line.isNotBlank()) {
                val parts = line.split("\\s+".toRegex())
                left.add(parts[0].toInt())
                right.add(parts[1].toInt())
            }
        }
        return left to right
    }
     
      
  • Not going to push hard on these first days (fever being a reason), so I slept in quite a bit before looking at the problem. ::: spoiler C#

     csharp
        
    List<int> _LeftList = new List<int>();
    List<int> _RightList = new List<int>();
    
    // Fed via File.ReadLines(...).Select(l => l.Trim())
    public void Input(IEnumerable<string> lines)
    {
      foreach (var line in lines)
      {
        var split = line.Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(s => int.Parse(s));
        _LeftList.Add(split.First());
        _RightList.Add(split.Last());
      }
    }
    
    public void Part1()
    {
      Console.WriteLine($"Sum: {_LeftList.Order().Zip(_RightList.Order()).Select(v => Math.Abs(v.First - v.Second)).Sum()}");
    }
    public void Part2()
    {
      Console.WriteLine($"Sum: {_LeftList.Select(l => _RightList.Where(i => i == l).Count() * l).Sum()}");
    }
    
    
      

    :::

  • Smalltalk

     smalltalk
        
    day1p12: input    
        | list1 list2 nums dist sim |
        
        list1 := OrderedCollection new.
        list2 := OrderedCollection new.
        
        input linesDo: [ :l |
            nums := l substrings collect: [ :n | n asInteger ].
            list1 add: (nums at: 1).
            list2 add: (nums at: 2).
        ].
    
        list1 sort.
        list2 sort.
        
        dist := 0.
        list1 with: list2 do: [ :a :b | dist := dist + (a - b) abs ].
        
        sim := list1 sumNumbers: [ :x | x * (list2 occurrencesOf: x) ].
        
        ^ Array with: dist with: sim.
    
      
  • Raku

    I'm trying warm up to Raku again.

    I'm happy to see that Lemmy no longer eats Raku code.

  • I'm late to the party, as usual. Damned timezones. This year I'm going to tackle with a small handful of languages, but primarily Elixir and Gleam. This is my first time trying this languages in earnest, so expect some terrible, inefficient and totally unidiomatic code!
    Here's day one:

    Elixir

     elixir
        
    part_one =
      File.read!("input.in")
      |> String.split("\n", trim: true)
      |> Enum.map(fn line ->
        line
        |> String.split()
        |> Enum.map(&String.to_integer/1)
      end)
      |> Enum.reduce({[], []}, fn [first, second], {list1, list2} ->
        {[first | list1], [second | list2]}
      end)
      |> then(fn {list1, list2} ->
        {Enum.sort(list1), Enum.sort(list2)}
      end)
      |> then(fn {list1, list2} ->
        Enum.zip(list1, list2)
        |> Enum.map(fn {x, y} -> abs(x - y) end)
      end)
      |> Enum.sum()
    
    part_two =
      File.read!("input.in")
      |> String.split("\n", trim: true)
      |> Enum.map(fn line ->
        line
        |> String.split()
        |> Enum.map(&String.to_integer/1)
      end)
      |> Enum.reduce({[], []}, fn [first, second], {list1, list2} ->
        {[first | list1], [second | list2]}
      end)
      |> then(fn {list1, list2} ->
        Enum.map(list1, fn line ->
          line * Enum.count(list2, fn x -> x === line end)
        end)
        |> Enum.sum()
      end)
    
    IO.inspect(part_one)
    IO.inspect(part_two)
    
      
  • Crystal

     ruby
        
    f = ARGV[0]? ? File.read_lines("input.txt") : test.lines
    list1 = Array(Int32).new(f.size)
    list2 = Array(Int32).new(f.size)
    
    f.each do |l|
        nums = l.split.map(&.to_i)
        list1.push(nums[0])
        list2.push(nums[1])
    end
    
    list1.sort!
    list2.sort!
    
    puts list1.zip(list2).sum{ |l1, l2| (l1 - l2).abs }
    
    puts list1.sum {|x| x * list2.count x}
    
      
  • TypeScript

    This is for part #2 only.

     typescript
        
    import { readFileSync } from 'fs'
    
    const f = readFileSync('./input.txt', 'utf-8')
    const lines = f.split("\n")
    
    let rights = {}
    for (const i in lines) {
        if (lines[i] == '') { continue }
    
        const [, right] = lines[i].split(/\s+/)
        if (rights[right] === undefined) {
            rights[right] = 0
        }
    
        rights[right]++
    }
    
    let ans = 0
    for (const i in lines) {
        const [left] = lines[i].split(/\s+/)
        const similarity = rights[left]
    
        if (similarity) {
            ans += (Number(left) * rights[left])
        }
    }
    
    console.dir(ans)
    
      

    Is it possible to get this more efficient? I would love a way that only required iterating over the list once, but I don't really have the focus to puzzle it out any less than O(2n) (probably more than that, even, if you count reading in the data...).

  • C#

     undefined
        
    using System;
    using System.Linq;
    
    public record Point(int X, int Y); 
    
    static class Program
    {
        static async Task Main(string[] args)
        {
            var data = (await ReadInputFromFile("data.txt")).ToArray();
    
            var part1Answer = CalculateTotalDifference(data);
            Console.WriteLine($"Part 1 = {part1Answer}");
    
            var part2Answer = CountFrequencies(data);
            Console.WriteLine($"Part 2 = {part2Answer}");
        }
    
        public static int CountFrequencies(ICollection<Point> points)
        {
            var freq = points
                .GroupBy(p => p.Y)
                .ToDictionary(g => g.Key, g => g.Count());
            return points
                .Sum(p => freq.GetValueOrDefault(p.X, 0) * p.X);
        }
    
        public static int CalculateTotalDifference(ICollection<Point> points)
            => points.OrderBy(p => p.X)
                .Zip(
                    points.OrderBy(p => p.Y),
                    (px, py) => Math.Abs(px.X - py.Y))
                .Sum();
    
        public static readonly char[] Delimiter = new char[] { ' ' };
        public static async Task<IEnumerable<Point>> ReadInputFromFile(string path)
            => (await File.ReadAllLinesAsync(path))
                .Select(l =>
                {
                    var parts = l.Split(
                        Delimiter,
                        StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
                    return new Point(int.Parse(parts[0]), int.Parse(parts[1]));
                });
    }
    
      
  • Zig

     undefined
        
    const std = @import("std");
    const List = std.ArrayList;
    const Map = std.AutoHashMap;
    
    const splitSeq = std.mem.splitSequence;
    const splitScalar = std.mem.splitScalar;
    const parseInt = std.fmt.parseInt;
    const print = std.debug.print;
    const sort = std.sort.block;
    
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const alloc = gpa.allocator();
    
    const Answer = struct {
        distance: u32,
        similarity: u32,
    };
    
    fn lessThan(_: void, lhs: []const u8, rhs: []const u8) bool {
        return std.mem.lessThan(u8, lhs, rhs);
    }
    
    pub fn solve(input: []const u8) !Answer {
        var rows = splitScalar(u8, input, '\n');
        var left_list = List([]const u8).init(alloc);
        defer left_list.deinit();
        var right_list = List([]const u8).init(alloc);
        defer right_list.deinit();
    
        // PART 1
    
        // split the rows into two lists
        while (rows.next()) |row| {
            var sides = splitSeq(u8, row, "   ");
            try left_list.append(sides.next() orelse break);
            try right_list.append(sides.next() orelse break);
        }
        _ = left_list.pop(); // last null
    
        // sort both lists
        sort([]const u8, left_list.items, {}, lessThan);
        sort([]const u8, right_list.items, {}, lessThan);
    
        var distance: u32 = 0;
        for (left_list.items, right_list.items) |left, right| {
            distance += @abs(try parseInt(i32, left, 10) - try parseInt(i32, right, 10));
        }
    
        // PART 2
        var right_scores = Map(i32, u32).init(alloc);
        defer right_scores.deinit();
    
        // count number of item appearances in the right list
        for (right_list.items) |item| {
            const value = try parseInt(i32, item, 10);
            const result = try right_scores.getOrPut(value);
            if (!result.found_existing) {
                result.value_ptr.* = 1;
            } else {
                result.value_ptr.* += 1;
            }
        }
    
        // sum up similarity between items in left list and right list scores
        var similarity: u32 = 0;
        for (left_list.items) |item| {
            const value = try parseInt(i32, item, 10);
            const result = right_scores.get(value) orelse 0;
            similarity += @as(u32, @intCast(value)) * result;
        }
        return Answer{ .distance = distance, .similarity = similarity };
    }
    
    pub fn main() !void {
        const answer = try solve(@embedFile("input.txt"));
        print("Part 1: {d}\n", .{answer.distance});
        print("Part 2: {d}\n", .{answer.similarity});
    }
    
    test "test input" {
        const answer = try solve(@embedFile("test.txt"));
        try std.testing.expectEqual(answer.distance, 11);
        try std.testing.expectEqual(answer.similarity, 31);
    }
    
    
      
  • Factor

     undefined
        
    : get-input ( -- left-list right-list )
      "vocab:aoc-2024/01/input.txt" utf8 file-lines
      [ split-words harvest ] map unzip
      [ [ string>number ] map ] bi@ ;
    
    : part1 ( -- n )
      get-input
      [ sort ] bi@
      [ - abs ] 2map-sum ;
    
    : part2 ( -- n )
      get-input
      histogram
      '[ dup _ at 0 or * ] map-sum ;
    
      

    https://github.com/AndydeCleyre/aoc-2024

39 comments