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
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.
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.
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!();
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.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)
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.
C#
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()}"); }
C#
public class Day01 : Solver { private ImmutableArray<int> left; private ImmutableArray<int> right; public void Presolve(string input) { var pairs = input.Trim().Split("\n").Select(line => Regex.Split(line, @"\s+")); left = pairs.Select(item => int.Parse(item[0])).ToImmutableArray(); right = pairs.Select(item => int.Parse(item[1])).ToImmutableArray(); } public string SolveFirst() => left.Sort().Zip(right.Sort()).Select((pair) => int.Abs(pair.First - pair.Second)).Sum().ToString(); public string SolveSecond() => left.Select((number) => number * right.Where(v => v == number).Count()).Sum().ToString(); }
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.
Python
Part 1
left_list = [] right_list = [] for line in file: split_line = line.split() left_list.append(int(split_line[0])) right_list.append(int(split_line[1])) sorted_left = sorted(left_list) sorted_right = sorted(right_list) distance = [] for left, right in zip(sorted_left, sorted_right): distance.append(abs(left - right)) total = sum(distance) print(total)
Part 2
file = open('input.txt', 'r') left_list = [] right_list = [] for line in file: split_line = line.split() left_list.append(int(split_line[0])) right_list.append(int(split_line[1])) sim_score = 0 for item in left_list: sim = right_list.count(item) sim_score += (sim * item) print(sim_score)
I am sure there were better ways to do this, this was just the first way in my head, in the order it appeared
Part 1 is a sort and a quick loop. Part 2 could be efficient with a lookup table but it was practically instant with a simple non-memoized scan so left it that way.
You are using some interesting techniques there. I never imaged you could use the result of == for adding to a counter.
But how did you handle duplicates in part 2?
I’m not sure if I understand the question correctly but for every number in the left array I count in the right array. That means duplicate work but shrug 😅
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 }
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.)
Thanks! I like the
Pair
destruction andzip().sumOf()
approach. I’m relatively new to Kotlin, so this is a good learning experience. 😅
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.
Rust
I’m doing it in Rust again this year. I stopped keeping up with it after day 3 last year, so let’s hope I last longer this time around.
Solution Spoiler Alert
use std::collections::HashMap; use crate::utils::read_lines; pub fn solution1() { let (mut id_list1, mut id_list2) = get_id_lists(); id_list1.sort(); id_list2.sort(); let total_distance = id_list1 .into_iter() .zip(id_list2) .map(|(left, right)| (left - right).abs()) .sum::<i32>(); println!("Total distance = {total_distance}"); } pub fn solution2() { let (id_list1, id_list2) = get_id_lists(); let id_count_map = id_list2 .into_iter() .fold(HashMap::<_, i32>::new(), |mut map, id| { *map.entry(id).or_default() += 1i32; map }); let similarity_score = id_list1 .into_iter() .map(|id| id * id_count_map.get(&id).copied().unwrap_or_default()) .sum::<i32>(); println!("Similarity score = {similarity_score}"); } fn get_id_lists() -> (Vec<i32>, Vec<i32>) { read_lines("src/day1/input.txt") .map(|line| { let mut ids = line.split_whitespace().map(|id| { id.parse::<i32>() .expect("Ids from input must be valid integers") }); ( ids.next().expect("First Id on line must be present"), ids.next().expect("Second Id on line must be present"), ) }) .unzip() }
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 }