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
Python3
ah well this year ends with a simple ~12.5 ms solve and not too much of a brain teaser. Well at least I got around to solving all of the challenges.
Code
from os.path import dirname,realpath,join from collections.abc import Callable def profiler(method) -> Callable[..., any]: from time import perf_counter_ns def wrapper_method(*args: any, **kwargs: any) -> any: start_time = perf_counter_ns() ret = method(*args, **kwargs) stop_time = perf_counter_ns() - start_time time_len = min(9, ((len(str(stop_time))-1)//3)*3) time_conversion = {9: 'seconds', 6: 'milliseconds', 3: 'microseconds', 0: 'nanoseconds'} print(f"Method {method.__name__} took : {stop_time / (10**time_len)} {time_conversion[time_len]}") return ret return wrapper_method @profiler def solver(locks_and_keys_str: str) -> int: locks_list = [] keys_list = [] for schematic in locks_and_keys_str.split('\n\n'): if schematic[0] == '#': locks_list.append(tuple([column.index('.') for column in zip(*schematic.split())])) else: keys_list.append(tuple([column.index('#') for column in zip(*schematic.split())])) count = 0 for lock_configuration in locks_list: for key_configuration in keys_list: for i,l in enumerate(lock_configuration): if l>key_configuration[i]: # break on the first configuration that is invalid break else: # skipped when loop is broken count += 1 return count if __name__ == "__main__": BASE_DIR = dirname(realpath(__file__)) with open(join(BASE_DIR, r'input'), 'r') as f: input_data = f.read().replace('\r', '').strip() result = solver(input_data) print("Day 25 final solve:", result)
Congrats on reaching the finish line!
The bit that caught me out was that the key + lock should equal 5 in reality, instead of being up to 5 in the challenge.
Thanks! I quickly wrote it but didn’t think to count things. I just took the index of where the edge was located at and ran with it.
So I don’t understand what you mean by equal 5. Could you elaborate? Cause I must have read the challenge text differently.
For a real world lock, the key height + pin height must equal the height of the barrel exactly. If it is taller or shorter, the lock will bind and not open.
For the challenge, as long as its not overlapping (too tall), its a valid key/lock pair.
Oh! I didn’t think it that way, lol, I was thinking this quickly through. I didn’t think of relating to physical locks because it clearly said it was virtual. But I guess, there could theoretically be a physical tumbler lock with 0-5 spacers, it would just be a tall lock. You know like how some master keys have it so that there are spacers for the master key or the client key to open the lock.
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
Dart
Quick and dirty, and slightly tipsy, code.
Happy Christmas everyone!
Thanks to Eric and the team at Advent of Code, to @Ategon@programming.dev and @CameronDev@programming.dev 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 'package:collection/collection.dart'; import 'package:more/more.dart'; part1(List<String> lines) { var (w, h) = (lines.first.length, lines.indexOf('')); var (falsey: keys, truthy: locks) = (lines..insert(0, '')) .splitBefore((l) => l.isEmpty) .map((g) => [ for (var x in 0.to(w)) [for (var y in 1.to(h + 1)) g[y][x]] ]) .partition((g) => g[0][0] == '#'); return keys .map((l) => locks.count((k) => 0.to(w).every((r) => (l[r] + k[r]).count((e) => e == '#') < 8))) .sum; }
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)
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".
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
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"
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);
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