27 votes

Day 1: Historian Hysteria

27 comments

  1. [4]
    jonah
    Link
    Hey folks, I didn't know how many of us were planning to (try to) keep up with Advent of Code this year. I see last year there ended up being a scheduled topic, but I wanted to just post here and...

    Hey folks, I didn't know how many of us were planning to (try to) keep up with Advent of Code this year. I see last year there ended up being a scheduled topic, but I wanted to just post here and remind anyone who's interested that today is the first day of December, which means it's the first day of Advent of Code! Last year was a lot of fun even when I couldn't keep up. I really enjoyed seeing everyone's solutions and discussions around the puzzles every day. I'm hoping that will continue this year :)

    Happy holidays!

    10 votes
    1. [2]
      xavdid
      Link Parent
      Thank you! I'm not sure how scheduled topics work- can we set up another one? It would be fun to have our own solution threads again

      Thank you!

      I'm not sure how scheduled topics work- can we set up another one? It would be fun to have our own solution threads again

      6 votes
      1. Deimos
        Link Parent
        I've set up the scheduled topic again now, so it will automatically post every day right when the new problem releases (starting tomorrow, today's was about an hour late).

        I've set up the scheduled topic again now, so it will automatically post every day right when the new problem releases (starting tomorrow, today's was about an hour late).

        8 votes
    2. RheingoldRiver
      Link Parent
      sadly im not doing it this year, last year was a ton of fun but this year im way too busy to complete it and i dont want to do it partway maybe next year work will be slower!

      sadly im not doing it this year, last year was a ton of fun but this year im way too busy to complete it and i dont want to do it partway

      maybe next year work will be slower!

      1 vote
  2. fidwell
    (edited )
    Link
    I knew today's puzzle would be easy so I stayed up til midnight to get it done quick. I thought 6 minutes for part 1 was blazing fast, but apparently there are just so many people doing it now...

    I knew today's puzzle would be easy so I stayed up til midnight to get it done quick. I thought 6 minutes for part 1 was blazing fast, but apparently there are just so many people doing it now that it was still only good enough for rank 2,737. Oh well! I'm not in it for the points, so I probably won't be trying to rush again.

    Good luck with the rest of the month!

    (edit: by the way here's my repo; I'm doing 2024 in C# again just because I'm too lazy to try something new. I have done a few old problems in Python, just to learn it; but I'm thinking of saving some other old ones to try in Rust. we'll see.)

    5 votes
  3. [4]
    Halfloaf
    (edited )
    Link
    I did my first advent of code! It was pretty easy, but I want to go back and do it in a different way. I finished it first with a jupyter notebook, but I want to get comfortable doing it in raw...

    I did my first advent of code!

    It was pretty easy, but I want to go back and do it in a different way. I finished it first with a jupyter notebook, but I want to get comfortable doing it in raw python, as I want to get a little more comfortable in that sort of development.

    My basic python solution for both parts!
    import pandas as pd
    import numpy as np
    
    def main() -> None:
        df = pd.read_csv("aoc/day1/input_data.txt",sep='   ',names=['left','right'])
        ls = df['left'].sort_values()
        rs = df['right'].sort_values()
        total_distance = 0
        similarity_score = 0
        r_val_counts = rs.value_counts()
        for l,r in zip(ls,rs):
            total_distance = total_distance + np.abs(l-r)
            similarity_score = similarity_score + l*r_val_counts.get(l, default=0)
        
        print(f"total distance:\n\t{total_distance}\nsimilarity:\n\t{similarity_score}")
    
    5 votes
    1. [3]
      Halfloaf
      Link Parent
      I wanted to play around with an object oriented version: My OO solution import pandas as pd import numpy as np class Day1Solver(): left = [] right = [] def __init__(self, datafile: str=None): if...

      I wanted to play around with an object oriented version:

      My OO solution
      import pandas as pd
      import numpy as np
      class Day1Solver():
          left = []
          right = []
      
          def __init__(self, datafile: str=None):
              if not datafile is None:
                  df =  pd.read_csv(datafile, sep="   ",names=["left","right"], engine='python')
                  self.left = df['left'].sort_values()
                  self.right = df['right'].sort_values()
                  
          def get_distance(self):
              return np.sum([np.abs(l-r) for l,r in zip(self.left, self.right)])
          
          def get_similarity(self):
              counts = self.right.value_counts()
              return np.sum([counts.get(l, default=0)*l for l in self.left])
      
      if __name__=="__main__":
          p = Day1Solver("aoc/day1/input_data.txt")
          print(p.get_distance())
          print(p.get_similarity())
      
      3 votes
      1. [2]
        scarecrw
        Link Parent
        This is awesome! It's wild seeing somebody comfortable with dataframes enough that it'd be the first choice to solving this! They always overwhelmed me, and I find myself falling back on 2D...

        This is awesome! It's wild seeing somebody comfortable with dataframes enough that it'd be the first choice to solving this! They always overwhelmed me, and I find myself falling back on 2D lists...

        If feedback is welcome, I wanted to give one small tip: there's no need to declare left and right at the class level: those would actually be defining Day1Solver.left (for the whole class) instead of p.left (for an instance).

        3 votes
        1. Halfloaf
          Link Parent
          Oh awesome! Thank you for the tip!! I'm coming into more of a programming role from an Electrical Engineering -> Data Science -> Signal Processing route, which got me to work with a lot of...

          Oh awesome! Thank you for the tip!!

          I'm coming into more of a programming role from an Electrical Engineering -> Data Science -> Signal Processing route, which got me to work with a lot of dataframes. I just love pandas' read_csv function - it does so dang much.

          feedback is really welcome - I want to build my confidence in programming in general here - I feel so much impostor syndrome when working with people that have used Python for a decade!

          2 votes
  4. lily
    Link
    I'm trying out the Jai language for the first time, after getting into the beta recently (I didn't expect I'd be qualified enough to get in, but I'll take what I can get!). Probably not the...

    I'm trying out the Jai language for the first time, after getting into the beta recently (I didn't expect I'd be qualified enough to get in, but I'll take what I can get!). Probably not the smartest choice for solving puzzles quickly, but I'll take the opportunity to learn something new. It's an interesting language. This solution is a little verbose and maybe not optimal or the best / most idiomatic way to do things in Jai, but at least it solves the problem.

    Solution
    /* Advent of Code 2024
     * Day 01: Historian Hysteria
     */
    
    #import "Basic";
    #import "File";
    #import "Hash_Table";
    #import "Math";
    #import "Sort";
    #import "String";
    
    int_compare :: (a: int, b: int) -> int {
        if a > b return 1;
        if a < b return -1;
        return 0;
    }
    
    main :: () {
        input, success := read_entire_file("inputs/day_01.txt");
    
        left_column: [..] int;
        right_column: [..] int;
    
        for split(input, "\n") {
            numbers := split(it, "   ");
            if numbers.count < 2 {
                break;
            }
    
            array_add(*left_column, string_to_int(numbers[0]));
            array_add(*right_column, string_to_int(numbers[1]));
        }
    
        quick_sort(left_column, int_compare);
        quick_sort(right_column, int_compare);
    
        difference_sum := 0;
        for 0..left_column.count - 1 {
            difference_sum += abs(left_column[it] - right_column[it]);
        }
    
        print("Part 1: %\n", difference_sum);
    
        right_column_occurrences: Table(int, int);
        similarity := 0;
    
        for left_column {
            occurrences, success := table_find(*right_column_occurrences, it);
            if !success {
                occurrences = 0;
                for other: right_column {
                    if other == it {
                        occurrences += 1;
                    }
                }
    
                table_set(*right_column_occurrences, it, occurrences);
            }
    
            similarity += it * occurrences;
        }
    
        print("Part 2: %\n", similarity);
    }
    
    4 votes
  5. csos95
    Link
    This year I'm doing Advent of Code in rhai, an embedded scripting language for rust, because I've been meaning to try it out for a while. It's not amazing speed-wise because it's a treewalk...

    This year I'm doing Advent of Code in rhai, an embedded scripting language for rust, because I've been meaning to try it out for a while.

    It's not amazing speed-wise because it's a treewalk interpreter, but it has a lot of nice features such as many safety options for when you need to run untrusted code, custom operators/syntax, and a decent api for making rust types and functions usable within it.

    I initially did it using the built-in object map type, but it seems to only support string keys and I was annoyed that I had to convert the integer keys I had with every get and set.
    I could've just re-processed the input to have the values as strings again and not need to do any conversion, but I'm almost certainly going to need to use integer keys more than once so I added an HashMap<INT, Dynamic> type named IntMap to Rhai.

    It was very easy to get started with, I had no trouble finding the methods I needed in the docs to complete the initial challenge or for creating the IntMap type and methods.
    I'm quite impressed with how easy it was to extend.

    Rhai Code
    fn get_input(day) {
       let input = open_file(`day${day}.txt`)
           .read_string();
       input.trim();
       input
    }
    
    let input = get_input(1);
    
    // PART 1
    let list1 = [];
    let list2 = [];
    for line in input.split("\n") {
        let items = line.split();
        list1.push(items[0].parse_int());
        list2.push(items[1].parse_int());
    }
    
    list1.sort();
    list2.sort();
    
    let total = 0;
    for i in 0..list1.len() {
        total += (list1[i] - list2[i]).abs();
    }
    
    print(`part1: ${total}`);
    
    // PART 2
    let list2_occurrences = new_int_map();
    
    for location in list2 {
        let prev = list2_occurrences[location] ?? 0;
        list2_occurrences[location] = prev + 1;
    }
    
    let total = 0;
    for location in list1 {
        let occurrences = list2_occurrences[location] ?? 0;
        total += location * occurrences;
    }
    
    print(`part2: ${total}`);
    
    4 votes
  6. jzimbel
    Link
    Using Elixir again this year. After using it for 4 consecutive years, I've built up a nice project with various conveniences and a few modules for the data structures commonly used by AoC puzzles....

    Using Elixir again this year.

    After using it for 4 consecutive years, I've built up a nice project with various conveniences and a few modules for the data structures commonly used by AoC puzzles.

    I'm thinking about learning to use dataframes with Explorer (Elixir's answer to Python's pandas), but for now I just used the standard library.

    Parts 1 and 2

    (The SharedParse thing signals to my solution runner that both part1 and part2 can reuse the same value parsed from the input.)

    defmodule AdventOfCode.Solution.Year2024.Day01 do
      use AdventOfCode.Solution.SharedParse
    
      @impl AdventOfCode.Solution.SharedParse
      def parse(input) do
        input
        |> String.split()
        |> Enum.map(&String.to_integer/1)
        |> then(
          &[
            _col_a = Enum.take_every(&1, 2),
            _col_b = Enum.take_every(tl(&1), 2)
          ]
        )
      end
    
      def part1(cols) do
        cols
        |> Enum.map(&Enum.sort/1)
        |> Enum.zip()
        |> Enum.map(fn {a, b} -> abs(a - b) end)
        |> Enum.sum()
      end
    
      def part2([col_a, col_b]) do
        a_freq = Enum.frequencies(col_a)
        b_freq = Enum.frequencies(col_b)
    
        a_freq
        |> Enum.map(fn {n, freq} -> n * freq * Map.get(b_freq, n, 0) end)
        |> Enum.sum()
      end
    end
    
    4 votes
  7. balooga
    Link
    Is it that time again already?? I’ve never made it all the way through but I can usually ride for a couple weeks. I’ll have to make some time for this later today and see how it goes.

    Is it that time again already?? I’ve never made it all the way through but I can usually ride for a couple weeks. I’ll have to make some time for this later today and see how it goes.

    3 votes
  8. scarecrw
    Link
    I'm embarrassed to say this, but AoC is probably one of the things I most look forward to this time of year! I'm a sucker for math/programming puzzles, and AoC hits that sweet spot of being...

    I'm embarrassed to say this, but AoC is probably one of the things I most look forward to this time of year! I'm a sucker for math/programming puzzles, and AoC hits that sweet spot of being genuinely challenging without losing the fun or adding pressure if you don't solve a day.

    In continuing to try new languages each year, I'm giving a shot to Pharo (Smalltalk). I'm trying to really embrace the OO design and TDD approaches, even if it feels a bit silly for puzzles like these. One unfortunate thing is that this, and the language's use of images, doesn't lend itself well to sharing solutions. Here's day 1 (omitting accessors and tests), but I'll probably give up and just post snippets once the days get more complex.

    Smalltalk Solution
    Class {
    	#name : 'Day1Solver',
    	#superclass : 'Object',
    	#instVars : [
    		'rawData',
    		'leftList',
    		'rightList'
    	],
    	#category : 'AoCDay1',
    	#package : 'AoCDay1'
    }
    
    Day1Solver class >> newFromFilename: afilename [
    	^ self new rawData: afilename asFileReference contents lines; parseRawData
    ]
    
    Day1Solver class >> newFromString: aString [
    	^ self new rawData: aString lines; parseRawData
    ]
    
    Day1Solver >> parseRawData [
    	leftList := rawData collect: [ :s | ((s findTokens: ' ') at: 1) asNumber].
    	rightList := rawData collect: [ :s | ((s findTokens: ' ') at: 2) asNumber].
    ]
    
    Day1Solver >> countRightOccurrences: anInteger [ 
    	^ (rightList select: [ :x | x == anInteger ]) size
    ]
    
    Day1Solver >> solvePart1 [
    	leftList sort.
    	rightList sort.
    	^ (leftList with: rightList collect: [ :l :r | (l - r) abs ]) sum
    ]
    
    Day1Solver >> solvePart2 [
    	^ (leftList collect: [ :x | x * (self countRightOccurrences: x) ]) sum
    ]
    
    3 votes
  9. first-must-burn
    Link
    I have a couple of bigger fish to fry in December, so I am going to have to skip AoC this year :( Nevertheless, I will enjoy hearing about it from folks that are tackling it.

    I have a couple of bigger fish to fry in December, so I am going to have to skip AoC this year :( Nevertheless, I will enjoy hearing about it from folks that are tackling it.

    3 votes
  10. tomf
    (edited )
    Link
    I'm doing Google Sheets again for as long as I can without it consuming me. Part 1 & 2 ``` =SORT(LET(r,SORT(RIGHT(A:A,5)),l,SORT(LEFT(A:A,5)),{SUM(ABS(l-r));SUM(l*COUNTIF(r,l))})) ``` Using that...

    I'm doing Google Sheets again for as long as I can without it consuming me.

    Part 1 & 2 ``` =SORT(LET(r,SORT(RIGHT(A:A,5)),l,SORT(LEFT(A:A,5)),{SUM(ABS(l-r));SUM(l*COUNTIF(r,l))})) ```

    Using that first SORT instead of ARRAYFORMULA is only a dirty trick to save characters.

    edit: shorter formula

    3 votes
  11. Eabryt
    Link
    I'll be traveling for 2 weeks in the middle of December, but I plan on doing the days I'm around at least. I did today with Python but I'm debating choosing a random language to teach myself....

    I'll be traveling for 2 weeks in the middle of December, but I plan on doing the days I'm around at least.

    I did today with Python but I'm debating choosing a random language to teach myself.

    Anyone have suggestions for a language to choose?

    3 votes
  12. [2]
    minion
    Link
    I wanted to learn lisp, so I decided "hey, why not do Advent of Code in it" Day 1 started with me grasping at straws, trying to make anything work at all, and ended with me having some fun,...

    I wanted to learn lisp, so I decided "hey, why not do Advent of Code in it"

    Day 1 started with me grasping at straws, trying to make anything work at all, and ended with me having some fun, getting into a bit of a flow and writing some lisp

    My solution
    (require 'uiop)
    
    (defun contentful-file-lines (filename)
      (let* (
             (content (uiop:read-file-lines filename))
             (lines-without-empties
               (remove-if
                 (lambda (str) (string= "" str))
                 content)))
        lines-without-empties))
    
    (defun space-split (str)
      (uiop:split-string str :separator " "))
    
    (defun file-lists (filename)
      (let* (
             (lines (contentful-file-lines filename))
             (split-lines (mapcar #'space-split lines))
             (lists (apply #'mapcar #'list split-lines)))
        lists))
    
    (defun lr-lists (filename)
      (let ((lists (file-lists filename)))
        (list (first lists) (first (last lists)))))
    
    (defun to-integers (strings)
      (mapcar #'parse-integer strings))
    
    (defun lists-to-integers (lists)
      (mapcar #'to-integers lists))
    
    (defun numerical-lr-lists (filename)
      (lists-to-integers (lr-lists filename)))
    
    (defun sort-lists (lists)
      (mapcar
        (lambda (l) (sort l #'<))
        lists))
    
    (defun sorted-lr-lists (filename)
      (sort-lists (numerical-lr-lists filename)))
    
    (defun list-differences (lists)
      (apply
        #'mapcar
        (lambda (i1 i2) (abs (- i1 i2)))
        lists))
    
    (defun sum (numbers)
      (reduce #'+ numbers))
    
    (defun part1 (filename)
      (let* (
             (lr (sorted-lr-lists filename))
             (differences (list-differences lr))
             (total (sum differences)))
        total))
    
    (defun digit-counts (integers)
      (let ((counts (make-hash-table)))
        (progn
          (loop for digit in integers
                do (if (gethash digit counts)
                     (setf (gethash digit counts) (+ (gethash digit counts) 1))
                     (setf (gethash digit counts) 1))))
        counts))
    
    (defun score-list (lr)
      (let* (
             (counts (digit-counts (second lr)))
             (scores (mapcar
                       (lambda
                         (num) (* (gethash num counts 0) num))
                       (first lr))))
        scores))
    
    (defun part2 (filename)
      (let* (
             (lr (numerical-lr-lists filename))
             (scores (score-list lr))
             (total (sum scores)))
        total))
    
    3 votes
    1. kari
      Link Parent
      Hey, I wanted to learn some lisp too, but specifically Racket. I'd literally used the Racket REPL maybe 2 times before I started working on my solution so just part 1 took me like 3 hours lmao.

      Hey, I wanted to learn some lisp too, but specifically Racket. I'd literally used the Racket REPL maybe 2 times before I started working on my solution so just part 1 took me like 3 hours lmao.

      3 votes
  13. [3]
    xavdid
    Link
    I always use Python so I can get some reps in writing clear, maintainable code (which is understandably uncommon for AoC). I also write a step-by-step solution to my approach so everyone can...

    I always use Python so I can get some reps in writing clear, maintainable code (which is understandably uncommon for AoC). I also write a step-by-step solution to my approach so everyone can understand each day. Takes a lot of time (especially in later days), but I enjoy it.

    Anyway, my day 1 post is here (which also links to my full solution): https://advent-of-code.xavd.id/writeups/2024/day/1/

    Parsing the "vertical" lists was tricky, so I did it simply to start:

    l, r = [], []
    for line in self.input:
        a, b = line.split()
        l.append(int(a))
        r.append(int(b))
    
    l.sort()
    r.sort()
    

    But, after some playing with it, I got it down to a more svelte 2 liner:

    pairs = [map(int, l.split()) for l in self.input]
    l, r = [sorted(col) for col in zip(*pairs)]
    

    zip returns tuples, but sorted turns any iterable into a list, so we get some of the type transformation we'd have to do otherwise for free.

    A collections.Counter made part 2 simple and clean.

    Great to be back in it!

    2 votes
    1. [2]
      Halfloaf
      Link Parent
      I’m a relative newbie to properly organized code - thank you for this write-up! A question on this solution - what is the StrSplitSolution superclass being used for in this case? For organization...

      I’m a relative newbie to properly organized code - thank you for this write-up!

      A question on this solution - what is the StrSplitSolution superclass being used for in this case? For organization in the future?

      Edit: ah ha! I see your template structure! I’m reading through it now!

      1 vote
      1. xavdid
        Link Parent
        You already edited, but for others, it's from my project template. Specifically, that tells the input parsing function to treat the input as a list of strings:...

        You already edited, but for others, it's from my project template.

        Specifically, that tells the input parsing function to treat the input as a list of strings: xavdid/advent-of-code-python-template | base.py#L142-L152. Other options are one big string, an int, or a list of ints.

        1 vote
  14. kari
    Link
    I'd literally never written an Racket or other Lispy language before yesterday, but it seems like one of those things that's good to be familiar with as a programmer, so I decided to do AOC in it...

    I'd literally never written an Racket or other Lispy language before yesterday, but it seems like one of those things that's good to be familiar with as a programmer, so I decided to do AOC in it this year. Just part 1 took me about 3 hours and then maybe another 45 minutes for part 2, but I think it's starting to make a bit of sense. I'm sure my code is extremely un-idiomatic, though, so if anyone has suggestions please don't hesitate to let me know.

    My solution
    #! /usr/bin/env racket
    #lang racket
    
    (require math/base rackunit)
    
    (define (line-pairs in)
      (map (lambda (lst)
             (list (string->number (list-ref lst 0))(string->number (list-ref lst 1))))
           (map (lambda (lst)
                  (list (list-ref lst 0) (list-ref lst 1)))
                (for/list ([l (in-lines in)])
                  (string-split l "   ")))))
    
    (define (left-list pair-lst)
      (let ([lst (for/list ([lst (in-list pair-lst)])
                   (car lst))])
        (sort lst <)))
    
    (define (right-list pair-lst)
      (let ([lst (for/list ([lst (in-list pair-lst)])
                   (list-ref lst 1))])
        (sort lst <)))
    
    (define (distances left-lst right-lst)
      (for/list ([i (in-range (length left-lst))])
        (abs (- (list-ref left-lst i) (list-ref right-lst i)))))
    
    ; score := (sum (for i (in-list left)
    ;               (* left (length (filter (equal?=left))))))
    (define (similarity-score left-lst right-lst)
      (sum (for/list ([i (in-list left-lst)])
             (* i (length (filter (lambda (x) (equal? i x)) right-lst))))))
    
    (define (run-part01 in)
      (let* ([pairs (line-pairs in)]
             [left (left-list pairs)]
             [right (right-list pairs)])
        (sum (distances left right))))
    
    (define (run-part02 in)
      (let* ([pairs (line-pairs in)]
             [left (left-list pairs)]
             [right (right-list pairs)])
        (similarity-score left right)))
    
    (let ([input (open-input-file "inputs/day01.in")])
      (display (run-part01 input)))
    (newline)
    (let ([input (open-input-file "inputs/day01.in")])
      (display (run-part02 input)))
    (newline)
    
    (check-equal? (run-part01 (open-input-file "examples/day01.in")) 11 "Test part 1")
    (check-equal? (run-part02 (open-input-file "examples/day01.in")) 31 "Test part 2")
    
    2 votes
  15. [3]
    hamstergeddon
    (edited )
    Link
    Never done this before, but figured I'd give it a shot. I originally wrote Day 1 in Javascript, but just wrapped up rewriting it in Typescript. It's nothing too fancy, and lord knows it isn't...

    Never done this before, but figured I'd give it a shot. I originally wrote Day 1 in Javascript, but just wrapped up rewriting it in Typescript. It's nothing too fancy, and lord knows it isn't optimized, but it works and I had fun putting it together alongside the demo site.

    Demo: https://projects.duenas.dev/aoc/
    Source: https://github.com/jd13313/advent-of-code-2024

    1 vote
    1. [2]
      balooga
      Link Parent
      Cool, I’m doing mine in TS also. One observation I had looking at your code: if (a > b) return Math.abs(a - b); return Math.abs(b - a); The Math.abs() call makes the a > b check redundant. It...

      Cool, I’m doing mine in TS also. One observation I had looking at your code:

      if (a > b) return Math.abs(a - b);
                  
      return Math.abs(b - a);
      

      The Math.abs() call makes the a > b check redundant. It should work fine if you drop the first line altogether!

      3 votes
  16. Crespyl
    Link
    Nice and straightforward, it's good to be back! I'm still a little busy with life at the moment, so I'm just doing it in Ruby again this year. Parts 1 and 2, Ruby def compute_p1(input) pairs =...

    Nice and straightforward, it's good to be back! I'm still a little busy with life at the moment, so I'm just doing it in Ruby again this year.

    Parts 1 and 2, Ruby
    def compute_p1(input)
      pairs = input.lines.map { |line|
        line.split(/\s+/).map(&:to_i)
      }
      list_a = pairs.map(&:first).sort
      list_b = pairs.map(&:last).sort
      list_a.zip(list_b).map { |x| x.sort.reverse.inject(:-) }.sum
    end
    
    def compute_p2(input)
      pairs = input.lines.map { |line|
        line.split(/\s+/).map(&:to_i)
      }
      list_a = pairs.map(&:first).sort
      list_b = pairs.map(&:last).sort
    
      list_a.reduce(0) { |sum, x|
        sum + (x * list_b.count(x))
      }
    end
    
    1 vote