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
Decided to try and use Uiua for each day this year. At least I’m not the only one to get this idea ^^
Run with example input here
PartOne ← ( &rs ∞ &fo "input-1.txt" ⊜(⊜⋕≠@ .)≠@\n. ≡⍆⍉ ⌵/- /+ ) PartTwo ← ( &rs ∞ &fo "input-1.txt" ⊜(⊜⋕≠@ .)≠@\n. ⊢⟜⊣⍉ 0 ⍢(+⊙(:⊙(×⧻⊚◡⌕)↘1⟜⊢)|⋅(≠0⧻)) ⊙(◌◌) # just cleaning up the stack ) &p "Day 1:" &pf "Part 1: " &p PartOne &pf "Part 2: " &p PartTwo
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 }
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}")
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); }
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 ;
Elixir
defmodule AdventOfCode.Solution.Year2024.Day01 do use AdventOfCode.Solution.SharedParse @impl true def parse(input) do numbers = input |> String.split("\n", trim: true) |> Enum.map(fn l -> String.split(l, ~r/\s+/) |> Enum.map(&String.to_integer/1) end) {Stream.map(numbers, &Enum.at(&1, 0)), Stream.map(numbers, &Enum.at(&1, 1))} end def part1({left, right}) do Enum.zip_reduce(Enum.sort(left), Enum.sort(right), 0, &(&3 + abs(&1 - &2))) end def part2({left, right}) do freq = Enum.frequencies(right) left |> Stream.map(&(&1 * Map.get(freq, &1, 0))) |> Enum.sum() end end
I’ve honestly forgotten how fucking cool the pipe operator is
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 😅
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 mean
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(); }
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
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)
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() }
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.
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