this post was submitted on 25 Dec 2024
15 points (100.0% liked)

Advent Of Code

995 readers
3 users here now

An unofficial home for the advent of code community on programming.dev!

Advent of Code is an annual Advent calendar of small programming puzzles for a variety of skill sets and skill levels that can be solved in any programming language you like.

AoC 2024

Solution Threads

M T W T F S S
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25

Rules/Guidelines

Relevant Communities

Relevant Links

Credits

Icon base by Lorc under CC BY 3.0 with modifications to add a gradient

console.log('Hello World')

founded 1 year ago
MODERATORS
 

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

top 14 comments
sorted by: hot top controversial new old
[–] sjmulder@lemmy.sdf.org 5 points 2 weeks ago

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
[–] lwhjp@lemmy.sdf.org 5 points 2 weeks ago

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)
[–] mykl@lemmy.world 4 points 2 weeks ago* (last edited 2 weeks ago)

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;
}
[–] VegOwOtenks@lemmy.world 3 points 2 weeks ago* (last edited 2 weeks ago)

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
[–] LeixB@lemmy.world 3 points 2 weeks ago

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"
[–] GiantTree@feddit.org 3 points 2 weeks ago

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()
}

Also on GitHub

[–] mykl@lemmy.world 3 points 2 weeks ago

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". 
[–] Gobbel2000@programming.dev 2 points 2 weeks ago

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

[–] Acters@lemmy.world 2 points 1 week ago* (last edited 1 week ago) (1 children)

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)

[–] CameronDev@programming.dev 1 points 1 week ago (1 children)

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.

[–] Acters@lemmy.world 2 points 1 week ago (1 children)

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.

[–] CameronDev@programming.dev 1 points 6 days ago (1 children)

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.

https://upload.wikimedia.org/wikipedia/commons/thumb/e/e8/Pin_tumbler_no_key.svg/400px-Pin_tumbler_no_key.svg.png

For the challenge, as long as its not overlapping (too tall), its a valid key/lock pair.

[–] Acters@lemmy.world 2 points 6 days ago

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.

[–] Zikeji@programming.dev 2 points 2 weeks ago

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);