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
- What is this?: Here is a post with a large amount of details: https://programming.dev/post/22323136
- Where do I participate?: https://adventofcode.com/
- Is there a leaderboard for the community?: We have a programming.dev leaderboard with the info on how to join in this post: https://programming.dev/post/6631465
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])); }); }
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
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
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…).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.
Crystal
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}
Python
ids = """<multiline input string>""" ids = [pair.split(" ") for pair in ids.split("\n")] left = [int(t[0]) for t in ids] right = [int(t[-1]) for t in ids] left.sort() right.sort() print(sum(abs(l - r) for l, r in zip(left, right))) # 2 s = 0 for l in left: s += right.count(l) * l print(s)
Lost a minute because I forgot about
abs
.-.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
Raku
I’m trying warm up to Raku again.
Solution
use v6; sub MAIN($input) { my $file = open $input; grammar LocationList { token TOP { <row>+%"\n" "\n"* } token row { <left=.id> " "+ <right=.id> } token id { \d+ } } my $locations = LocationList.parse($file.slurp); my @rows = $locations<row>.map({ (.<left>.Int, .<right>.Int)}); my $part-one-solution = (@rows[*;0].sort Z- @rows[*;1].sort)».abs.sum; say "part 1: $part-one-solution"; my $rbag = bag(@rows[*;1].sort); my $part-two-solution = @rows[*;0].map({ $_ * $rbag{$_}}).sum; say "part 2: $part-two-solution"; }
I’m happy to see that Lemmy no longer eats Raku code.
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.
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
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)
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
TypeScript
Solution
import { AdventOfCodeSolutionFunction } from "./solutions"; function InstancesOf(sorted_array: Array<number>, value: number) { const index = sorted_array.indexOf(value); if(index == -1) return 0; let sum = 1; for (let array_index = index + 1; array_index < sorted_array.length; array_index++) { if(sorted_array[array_index] != value) break; sum += 1; } return sum; } export const solution_1: AdventOfCodeSolutionFunction = (input) => { const left: Array<number> = []; const right: Array<number> = []; const lines = input.split("\n"); for (let index = 0; index < lines.length; index++) { const element = lines[index].trim(); if(!element) continue; const leftRight = element.split(" "); left.push(Number(leftRight[0])); right.push(Number(leftRight[1])); } const numSort = (a: number, b: number) => a - b; left.sort(numSort); right.sort(numSort); let sum = 0; for (let index = 0; index < left.length; index++) { const leftValue = left[index]; const rightValue = right[index]; sum += Math.abs(leftValue - rightValue); } const part1 = `Part 1: ${sum}`; sum = 0; for (let index = 0; index < left.length; index++) { sum += left[index] * InstancesOf(right, left[index]); } const part2 = `Part 2: ${sum}`; return `${part1}\n${part2}`; };
Not the most elegant solution but it works. Decided to reuse the array since it is sorted for both sides.
python
I didn’t realize it was december until this afternoon. I’ve generally chosen a new or spartan lang to challenge myself, but I’m going easy mode this year with python and just focusing on meeting the requirement.
solution
import aoc def setup(): lines = aoc.get_lines(1) l = [int(x.split()[0]) for x in lines] r = [int(x.split()[1]) for x in lines] return (l, r, 0) def one(): l, r, acc = setup() for p in zip(sorted(l), sorted(r)): acc += abs(p[0] - p[1]) print(acc) def two(): l, r, acc = setup() for n in l: acc += n * r.count(n) print(acc) one() two()