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

  • hosaka@programming.dev
    link
    fedilink
    arrow-up
    1
    ·
    1 day ago

    Zig

    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);
    }
    
    
  • rwdf@lemmy.world
    link
    fedilink
    arrow-up
    1
    ·
    edit-2
    1 day ago

    Elixir

    Total noob, but it’s fun to learn.

    {left, right} =
      File.read!("./input.txt")
      |> String.split("\n", trim: true)
      |> Enum.map(fn line ->
        String.split(line)
        |> Enum.map(&String.to_integer/1)
        |> List.to_tuple()
      end)
      |> Enum.unzip()
      |> then(fn {left, right} ->
        {Enum.sort(left), Enum.sort(right)}
      end)
    
    diffs =
      Enum.zip(left, right)
      |> Enum.map(fn {l, r} -> abs(l - r) end)
      |> Enum.sum()
    
    freqs =
      Enum.filter(right, fn r -> r in left end)
      |> Enum.frequencies()
    
    freqsum =
      Enum.map(left, fn n ->
        freq = Map.get(freqs, n, 0)
        n * freq
      end)
      |> Enum.sum()
    
    IO.puts("part 1: #{diffs}")
    IO.puts("part 2: #{freqsum}")
    
    
  • Andy@programming.dev
    link
    fedilink
    arrow-up
    1
    ·
    edit-2
    10 hours ago

    Factor

    : 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

  • rwdf@lemmy.world
    link
    fedilink
    arrow-up
    1
    ·
    2 days ago

    Go

    package main
    
    import (
    	"bufio"
    	"fmt"
    	"os"
    	"sort"
    	"strconv"
    	"strings"
    )
    
    func main() {
    	input, _ := os.Open("input.txt")
    	defer input.Close()
    
    	left, right := []int{}, []int{}
    
    	scanner := bufio.NewScanner(input)
    	for scanner.Scan() {
    		line := scanner.Text()
    		splitline := strings.Split(line, "   ")
    		l, _ := strconv.Atoi(splitline[0])
    		r, _ := strconv.Atoi(splitline[1])
    		left, right = append(left, l), append(right, r)
    	}
    
    	fmt.Printf("part 1 - total diff: %d\n", part1(left, right))
    	fmt.Printf("part 2 - new total: %d\n", part2(left, right))
    }
    
    func part1(left, right []int) int {
    	diff := 0
    	sort.Ints(left)
    	sort.Ints(right)
    
    	for i, l := range left {
    		if l > right[i] {
    			diff += (l - right[i])
    		} else {
    			diff += (right[i] - l)
    		}
    	}
    	return diff
    }
    
    func part2(left, right []int) int {
    	newTotal := 0
    
    	for _, l := range left {
    		matches := 0
    		for _, r := range right {
    			if l == r {
    				matches++
    			}
    		}
    		newTotal += l * matches
    	}
    	return newTotal
    }
    
  • proved_unglue@programming.dev
    link
    fedilink
    arrow-up
    1
    ·
    2 days ago

    Kotlin

    No 💜 for Kotlin here?

    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
    }
    
    • the_beber@lemm.ee
      link
      fedilink
      English
      arrow-up
      1
      ·
      edit-2
      1 day ago

      I have another Kotlin (albeit similar) solution:

      import kotlin.math.abs
      
      fun main() {
      
          fun getLists(input: List<String>): Pair<List<Int>, List<Int>> {
              val unsortedPairs = input.map {
                  it.split("   ").map { it.toInt() }
              }
      
              val listA = unsortedPairs.map { it.first() }
              val listB = unsortedPairs.map { it.last() }
              return Pair(listA, listB)
          }
      
          fun part1(input: List<String>): Int {
              val (listA, listB) = getLists(input)
      
              return listA.sorted().zip(listB.sorted()).sumOf { abs(it.first - it.second) }
          }
      
          fun part2(input: List<String>): Int {
              val (listA, listB) = getLists(input)
      
              return listA.sumOf { number ->
                  number * listB.count { it == number }
              }
          }
      
          // Or read a large test input from the `src/Day01_test.txt` file:
          val testInput = readInput("Day01_test")
          check(part1(testInput) == 11)
          check(part2(testInput) == 31)
      
          // Read the input from the `src/Day01.txt` file.
          val input = readInput("Day01")
          part1(input).println()
          part2(input).println()
      }
      
      

      It’s a bit more compact. (If you take out the part that actually calls the functions on the (test-)input.)

      • proved_unglue@programming.dev
        link
        fedilink
        arrow-up
        1
        ·
        23 hours ago

        Thanks! I like the Pair destruction and zip().sumOf() approach. I’m relatively new to Kotlin, so this is a good learning experience. 😅

  • SteveDinn@lemmy.ca
    link
    fedilink
    arrow-up
    1
    ·
    2 days ago

    C#

    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]));
                });
    }
    
  • sjmulder@lemmy.sdf.org
    link
    fedilink
    arrow-up
    1
    ·
    2 days ago

    JavaScript

    After writing a procedural to-the-point version in C, tried a JavaScript solution too because it’s just perfect for list comprehension. The part 2 search is inefficient but the data size is small.

    Code
    const fs = require("fs");
    const U = require("./util");
    
    const pairs = fs
        .readFileSync(process.argv[2] || process.stdin.fd, "utf8")
        .split("\n")
        .filter(x => x != "")
        .map(x => x.split(/ +/).map(Number));
    
    const ls = pairs.map(x => x[0]); ls.sort();
    const rs = pairs.map(x => x[1]); rs.sort();
    
    const p1 = U.sum(ls.map((l, i) => Math.abs(l - rs[i])));
    const p2 = U.sum(ls.map(l => l * U.count(rs, l)));
    
    console.log("01:", p1, p2);
    

    https://github.com/sjmulder/aoc/blob/master/2024/js/day01.js

  • morrowind@lemmy.ml
    link
    fedilink
    arrow-up
    3
    ·
    3 days ago

    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.
    
  • LeixB@lemmy.world
    link
    fedilink
    arrow-up
    1
    ·
    2 days ago

    Haskell

    import Control.Arrow
    import Control.Monad
    import Data.List
    import Data.Map
    
    part1 [a, b] = sum $ abs <$> zipWith (-) (sort a) (sort b)
    part2 [a, b] = sum $ ap (zipWith (*)) (fmap (flip (findWithDefault 0) (freq b))) a
      where
        freq = fromListWith (+) . fmap (,1)
    
    main = getContents >>= (print . (part1 &&& part2)) . transpose . fmap (fmap read . words) . lines
    
  • grrgyle@slrpnk.net
    link
    fedilink
    arrow-up
    1
    ·
    2 days ago

    TypeScript

    This is for part #2 only.

    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…).

  • mykl@lemmy.world
    link
    fedilink
    arrow-up
    7
    ·
    edit-2
    4 days ago

    Uiua

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

    $ 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.
    
  • Leo Uino@lemmy.sdf.org
    link
    fedilink
    arrow-up
    6
    ·
    4 days ago

    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.

    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
    
  • Euro@programming.dev
    link
    fedilink
    arrow-up
    2
    ·
    3 days ago

    Viml

    I think viml is a very fun language, i like weird languages lol, so this year im doing it in viml while trying to use as many of the original ed/ex commands as i can (:d, :p, :a, :g, …)

    Part 1
    !cp ./puzzle1 ./puzzle1.editing
    e ./puzzle1.editing
    
    1,$sort
    let row1 = []
    
    g/^\d/let row1 = add(row1, str2nr(expand("<cword>"))) | norm 0dw
    1d
    1,$sort
    g/^\d/execute 'norm cc' .. string(abs(expand("<cword>") - row1[line('.') - 1]))
    
    $a|---ANSWER---
    0
    .
    1,$-1g/^\d/call setline("$", str2nr(getline("$")) + str2nr(expand("<cword>")))
    
    Part 2
    read ./puzzle1
    
    let cnt = 0
    g/^\d/let cnt += expand("<cword>") *
                \ searchcount(#{pattern: '\s\+' .. expand("<cword>")}).total
    
    echo cnt .. "\n"
    
    w! ./puzzle1.editing
    
  • Gobbel2000@programming.dev
    link
    fedilink
    arrow-up
    4
    ·
    4 days ago

    Rust

    Right IDs are directly read into a hash map counter.

    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!();