Day 25: Code Chronicle
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://topaz.github.io/paste/ 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/6637268
- 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
A Christmas Day treat: a one-liner for you all to decipher!
"#####\n.####\n.####\n.####\n.#.#.\n.#...\n.....\n\n#####\n##.##\n.#.##\n...##\n...#.\n...#.\n.....\n\n.....\n#....\n#....\n#...#\n#.#.#\n#.###\n#####\n\n.....\n.....\n#.#..\n###..\n###.#\n###.#\n#####\n\n.....\n.....\n.....\n#....\n#.#..\n#.#.#\n#####" /+♭⊞(/×<8+)∩°□°⊟ ⊕(□≡≡/+⌕@#)≠@#≡(⊢⊢). ⊜(⍉⊜∘⊸≠@\n)¬±⦷"\n\n".
Kotlin
A fun and small challenge. First read all locks, transpose their profile and count the
#
s (-1 for the full row). Then do the same for the keys.Lastly find all keys for all locks that do not sum to more than 5 with their teeth:
Code
val lockRegex = Regex("""#{5}(\r?\n[.#]{5}){6}""") val keyRegex = Regex("""([.#]{5}\r?\n){6}#{5}""") fun parseLocksAndKeys(inputFile: String): Pair<List<IntArray>, List<IntArray>> { val input = readResource(inputFile) val locks = lockRegex .findAll(input) .map { it .value .lines() .map { line -> line.toList() } .transpose() .map { line -> line.count { c -> c == '#' } - 1 } .toIntArray() } .toList() val keys = keyRegex .findAll(input) .map { it .value .lines() .map { line -> line.toList() } .transpose() .map { line -> line.count { c -> c == '#' } - 1 } .toIntArray() } .toList() return locks to keys } fun part1(inputFile: String): String { val (locks, keys) = parseLocksAndKeys(inputFile) val matches = locks.map { lock -> keys.filter { key -> for (i in lock.indices) { // Make sure the length of the key and lock do not exceed 5 if (lock[i] + key[i] > 5) { return@filter false } } true } } .flatten() .count() return matches.toString() }
Haskell
Merry Christmas!
{-# LANGUAGE OverloadedStrings #-} module Main where import Data.Either import Data.Text hiding (all, head, zipWith) import Data.Text qualified as T import Data.Text.IO as TIO type Pins = [Int] toKeyLock :: [Text] -> Either Pins Pins toKeyLock v = (if T.head (head v) == '#' then Left else Right) $ fmap (pred . count "#") v solve keys locks = sum [1 | k <- keys, l <- locks, fit k l] where fit a b = all (<= 5) $ zipWith (+) a b main = TIO.getContents >>= print . uncurry solve . partitionEithers . fmap (toKeyLock . transpose . T.lines) . splitOn "\n\n"
Dart
Quick and dirty, and slightly tipsy, code.
Happy Christmas everyone!
Thanks to Eric and the team at Advent of Code, to @[email protected] and @[email protected] for giving us somewhere to share and discuss our solutions, and to everyone here for the friendly and supportive community.
See you all next year!
import 'dart:math'; import 'package:collection/collection.dart'; import 'package:more/more.dart'; late int w, h; List<num> maxY(List<Point> ps) => [for (var x in 0.to(w)) ps.where((p) => p.x == x).map((p) => p.y).max]; List<num> minY(List<Point> ps) => [for (var x in 0.to(w)) ps.where((p) => p.x == x).map((p) => p.y).min]; part1(List<String> lines) { w = lines.first.length; h = lines.indexOf(''); var gs = (lines..insert(0, '')) .splitBefore((l) => l.isEmpty) .map((g) => [ for (var y in g.indexed()) for (var x in y.value.split('').indexed().where((e) => e.value == '#')) Point<num>(x.index, y.index - 1) ]) .toList(); var locks = gs.where((g) => g.count((g) => g.y == 0) == w).map(maxY); var keys = gs.where((g) => g.count((g) => g.y == h - 1) == w).map(minY); return locks .map((l) => keys.count((k) => 0.to(w).every((x) => k[x] > l[x]))) .sum; }
Rust
Nice ending for this year. Lock and key arrays are just added together and all elements must be <= 5. Merry Christmas!
Solution
fn flatten_block(block: Vec<Vec<bool>>) -> [u8; 5] { let mut flat = [0; 5]; for row in &block[1..=5] { for x in 0..5 { if row[x] { flat[x] += 1; } } } flat } fn parse(input: &str) -> (Vec<[u8; 5]>, Vec<[u8; 5]>) { let mut locks = Vec::new(); let mut keys = Vec::new(); for block_s in input.split("\n\n") { let block: Vec<Vec<bool>> = block_s .lines() .map(|l| l.bytes().map(|b| b == b'#').collect::<Vec<bool>>()) .collect(); assert_eq!(block.len(), 7); // Lock if block[0].iter().all(|e| *e) { locks.push(flatten_block(block)); } else { keys.push(flatten_block(block)); } } (locks, keys) } fn part1(input: String) { let (locks, keys) = parse(&input); let mut count = 0u32; for l in locks { for k in &keys { if l.iter().zip(k).map(|(li, ki)| li + ki).all(|sum| sum <= 5) { count += 1; } } } println!("{count}"); } fn part2(_input: String) { println!("⭐"); } util::aoc_main!();
Also on github
Haskell
A total inability to write code correctly today slowed me down a bit, but I got there in the end. Merry Christmas, everyone <3
import Data.Either import Data.List import Data.List.Split readInput = partitionEithers . map readEntry . splitOn [""] . lines where readEntry ls = (if head (head ls) == '#' then Left else Right) . map (length . head . group) $ transpose ls main = do (locks, keys) <- readInput <$> readFile "input25" print . length $ filter (and . uncurry (zipWith (<=))) ((,) <$> locks <*> keys)
Haskell
Have a nice christmas if you’re still celebrating today, otherwise hope you had a nice evening yesterday.
import Control.Arrow import Control.Monad (join) import Data.Bifunctor (bimap) import qualified Data.List as List heights = List.transpose >>> List.map (pred . List.length . List.takeWhile (== '#')) parse = lines >>> init >>> List.groupBy (curry (snd >>> (/= ""))) >>> List.map (List.filter (/= "")) >>> List.partition ((== "#####") . head) >>> second (List.map List.reverse) >>> join bimap (List.map heights) cartesianProduct xs ys = [(x, y) | x <- xs, y <- ys] part1 = uncurry cartesianProduct >>> List.map (uncurry (List.zipWith (+))) >>> List.filter (List.all (<6)) >>> List.length part2 = const 0 main = getContents >>= print . (part1 &&& part2) . parse
C
Merry Christmas everyone!
Code
#include "common.h" int main(int argc, char **argv) { static char buf[7]; static short h[500][5]; /* heights */ static short iskey[500]; int p1=0, nh=0, i,j,k; if (argc > 1) DISCARD(freopen(argv[1], "r", stdin)); for (nh=0; !feof(stdin) && !ferror(stdin); nh++) { assert(nh < (int)LEN(h)); for (i=0; i<7; i++) { fgets(buf, 7, stdin); if (i==0) iskey[nh] = buf[0] == '#'; for (j=0; j<5; j++) h[nh][j] += buf[j] == '#'; } /* skip empty line */ fgets(buf, 7, stdin); } for (i=0; i<nh; i++) for (j=0; j<nh; j++) if (iskey[i] && !iskey[j]) { for (k=0; k<5 && h[i][k] + h[j][k] <= 7; k++) ; p1 += k == 5; } printf("25: %d\n", p1); return 0; }
https://codeberg.org/sjmulder/aoc/src/branch/master/2024/c/day25.c
Made the 1 second challenge with most of it to spare! 😎
$ time bmake bench day01 0:00.00 1912 Kb 0+88 faults day02 0:00.00 1992 Kb 0+91 faults day03 0:00.00 1920 Kb 0+93 faults day04 0:00.00 1912 Kb 0+90 faults day05 0:00.00 2156 Kb 0+91 faults day06 0:00.03 1972 Kb 0+100 faults day07 0:00.06 1892 Kb 0+89 faults day08 0:00.00 1772 Kb 0+87 faults day09 0:00.02 2024 Kb 0+137 faults day10 0:00.00 1876 Kb 0+87 faults day11 0:00.00 6924 Kb 0+3412 faults day12 0:00.00 1952 Kb 0+103 faults day13 0:00.00 1908 Kb 0+88 faults day14 0:00.05 1944 Kb 0+92 faults day15 0:00.00 2040 Kb 0+89 faults day16 0:00.03 2020 Kb 0+250 faults day17 0:00.00 1896 Kb 0+88 faults day18 0:00.00 1952 Kb 0+107 faults day19 0:00.01 1904 Kb 0+91 faults day20 0:00.01 2672 Kb 0+325 faults day21 0:00.00 1804 Kb 0+86 faults day22 0:00.03 2528 Kb 0+371 faults day23 0:00.02 2064 Kb 0+152 faults day24 0:00.00 1844 Kb 0+89 faults day25 0:00.00 1788 Kb 0+89 faults real 0m0,359s
Javascript
Spent 10 minutes debugging my solution until I reread and found out they wanted the number of keys that fit, not the ones that overlapped. Reading comprehension is not it tonight.
const [locks, keys] = require('fs').readFileSync(0, 'utf-8').split(/\r?\n\r?\n/g).filter(v => v.length > 0).map(s => s.split(/\r?\n/g).filter(v => v.length > 0)).reduce((acc, s) => { const lock = s[0].split('').every(v => v === '#'); const schema = s.slice(1, -1); let rotated = []; for (let i = 0; i < schema[0].length; i += 1) { for (let j = 0; j < schema.length; j += 1) { if (!rotated[i]) rotated[i] = []; rotated[i].push(schema[j][i]); } } if (!lock) { rotated = rotated.map(v => v.reverse()); } const pinHeights = []; for (const row of rotated) { const height = row.indexOf('.'); pinHeights.push(height !== -1 ? height : 5); } if (lock) { acc[0].push(pinHeights); } else { acc[1].push(pinHeights); } return acc; }, [[],[]]); let fits = 0; for (const lock of locks) { for (const key of keys) { let overlapped = false; for (let i = 0; i < lock.length; i += 1) { if ((lock[i] + key[i]) > 5) { overlapped = true; } } if (!overlapped) { fits += 1; } } } console.log('Part One', fits);