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
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}")
Factor
: get-input ( -- left-list right-list ) "aoc-2024.01" "input.txt" vocab-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 ;
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 }
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 }
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.
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.
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
This is my third program in ruby after the ruby tutorial and half of the rails tutorial, so don’t expect anything too good from it.
Also i did this today since i had time, i will probably not comment every day.
fyi for lines 14-22 you an use
.abs
instead of checking for negatives and.sum
instead of doing it manually. Check my crystal solution to see what I meanThat is good to know, thank you.