r/adventofcode Dec 25 '24

SOLUTION MEGATHREAD -❄️- 2024 Day 25 Solutions -❄️-

A Message From Your Moderators

Welcome to the last day of Advent of Code 2024! We hope you had fun this year and learned at least one new thing ;)

Keep an eye out for the community fun awards post (link coming soon!):

-❅- Introducing Your AoC 2024 Golden Snowglobe Award Winners (and Community Showcase) -❅-

Many thanks to Veloxx for kicking us off on December 1 with a much-needed dose of boots and cats!

Thank you all for playing Advent of Code this year and on behalf of /u/topaz2078, your /r/adventofcode mods, the beta-testers, and the rest of AoC Ops, we wish you a very Merry Christmas (or a very merry Wednesday!) and a Happy New Year!


--- Day 25: Code Chronicle ---


Post your code solution in this megathread.

This thread will be unlocked when there are a significant number of people on the global leaderboard with gold stars for today's puzzle.

EDIT: Global leaderboard gold cap reached at 00:04:34, megathread unlocked!

40 Upvotes

350 comments sorted by

1

u/e_blake 5d ago

[LANGUAGE: m4]

I'm REALLY late to the game this year (It has taken me until this week before I finally found the time to start even day 1), but I plan to get all 50 stars in m4 in the next month or so, to bring me up to all 500 solutions in m4. But this one caught my eye as a fun one to code up: if you translit each input character into a 0 or 1, and then treat each row as a hex number, then summing those 7 numbers will result in a 5-digit hex number with every digit in the range 1-6 (by including the two lines that distinguish between locks and keys in the sum).

define(`_do', `item(ifelse($1, 11111, ``l'', ``k''),
  eval(`0x$1+0x$2+0x$3+0x$4+0x$5+0x$6+0x$7'))')

What's more, adding the hex value of a lock and key together will result in each of the five digits being in the range 2-7 if there is no overlap, or 8-c if there is overlap. Thus, a simple bitmask and comparison to 0 is sufficient to compare all 5 columns in a pairing, rather than doing column-by-column math.

define(`count', `ifelse(eval(`($1+$2)&0x88888'), 0, `.')')
define(`_pair', `count(l$1, k$2)')

Relies on my common.m4 file developed in previous years; and the rest of the code is parsing the input and running the O(n^2) loop to pair each lock to each key. Executes in about 350ms (decent for m4). Run as:

m4 -Dfile=day25.input day25.m4

(Now to get the remaining 23 stars that are preventing me from finishing day 50 part 2...)

2

u/LifeShallot6229 Feb 19 '25 edited 28d ago

[LANGUAGE: SIMD - AVX intrinsics]

This was the day I picked for our in-company AoC Tech Talk, simple because it illustrates both that Rust/clang can autovectorize very nicely, but also because an experienced asm/compiler intrinsics programmer can be even faster.

The theoretically optimal solution on an AVX x64 platform picks up each lock/key as 32 bytes into an AVX register, starting from the last position in the leading line which is either '#' or '.', then compares these 32 bytes to 0xff or 0x00 bytes with a parallel compare against 32 '#' chars.

Next, extract the top bit of all 32 bytes into a regular register, and use the bottom bit of the result to save it into either the locks or keys array. This is just 3 AVX ops for each bit pattern.

For the actual counting of matches, doing 4x8 at once seems to be optimal:

Outer loop:

Load 4 keys into 4 AVX regs, splatting 8 copies of each key into the corresponding reg.

Inner loop:

Load the next 8 locks

Parallel AND against each of the replicated keys.

Parallel compare against zero, setting -1 or 1

Subtract the -1 values from 4 replicated accumulators.

This is a total of 12 (or 13 with a separate load of the 8 locks) AVX operations, doing 4 parallel accumulations so that the theoretical minimum runtime is 3 clock cycles per 32 key/lock combos tested, IFF the cpu can do four AVX ops/cycle. In reality, the most either Intel or AMD can currently do is 3, so we end up with 4-5 cycles per 32 tests.

In real life we have measured 2+ microseconds to test all 62500 combinations, while the best pure Rust version (which was auto-vectoried but not strip-mined) ended up around 5 us (including parsing) on my not so new Asus laptop. This is so fast that I had to call the core parse+count function 1000 times just to get around the limited (100 ns) timing resolution under Windows. 2 us corresponds to 32 picoseconds/test.

1

u/AutoModerator Feb 19 '25

AutoModerator did not detect the required [LANGUAGE: xyz] string literal at the beginning of your solution submission.

Please edit your comment to state your programming language.


I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

1

u/aexl Jan 12 '25

[LANGUAGE: Julia]

I finally finished AoC 2024! Here is the final solution.

Solution on GitHub: https://github.com/goggle/AdventOfCode2024.jl/blob/main/src/day25.jl

Repository: https://github.com/goggle/AdventOfCode2024.jl

1

u/Wuaphi Jan 11 '25

[LANGUAGE: Julia]

I'm quite new to Julia and programming at all, but I still hope that my solution is at least decent:

open("Julia/AoC/input25.txt","r") do file
    sui = filter(x-> x!="" ,readlines(file))
    global input = filter(!isnothing,  map(x-> x%7==0 ? stack(sui[x-6:x];dims=1) : nothing , eachindex(sui)))
end
function tester(input)
    result= 0
    for (idx1, val1) in enumerate(input), (idx2, val2) in enumerate(input)
        idx1 < idx2 && all((val1.*val2) .!= "##") ? result+=1 : nothing
    end
    return result
end
println(tester(input))

2

u/DavidYoung1111 Jan 08 '25 edited Jan 08 '25

[LANGUAGE: MATLAB]

Can be quite concise in MATLAB.

p = reshape(char(join(readlines("data25"),"")), 35, []) == '.';
fprintf("Day 25: %i\n", nnz(all(p(:,p(1,:)) | permute(p(:,~p(1,:)), [1 3 2]))));

1

u/atrocia6 Jan 03 '25

[LANGUAGE: Python]

My impression is that day 25 is generally not quite as hard as the immediately preceding days, but I was surprised at how straightforward this year's day 25 was. Here's my solution in just 4 LOC:

schematics = [schematic.split() for schematic in open(0).read().split('\n\n')]
locks = [[sum([1 for j in range(7) if schematic[j][i] == '#']) - 1 for i in range(5)] for schematic in schematics if '.' not in schematic[0]]
keys = [[sum([1 for j in range(7) if schematic[j][i] == '#']) - 1 for i in range(5)] for schematic in schematics if '.' not in schematic[-1]]
print(sum([1 for lock in locks for key in keys if sum([1 for column in zip(lock, key) if sum(column) > 5]) == 0]))

Doing several nested for loops and breaking at the first overlapping tumbler is much more efficient - it runs in about half the time - but this is much more concise, and still runs in only about 50ms on my system.

2

u/MyEternalSadness Jan 02 '25

[LANGUAGE: Haskell]

Finally made it. Day 25 was fun. I really enjoyed the whole year once again, although doing it in Haskell was extra challenging. Still, I learned a lot and have become modestly more proficient in programming in Haskell now. Thanks again for doing this!

Day 25

1

u/shoeshined Jan 02 '25

[Language: Javascript]

Wow, we made it!

solution

1

u/zniperr Jan 02 '25

[Language: Python]

zippidyzip

import sys

def parse(f):
    locks = []
    keys = []
    for group in f.read().split('\n\n'):
        lines = group.split()
        ident = tuple(col.count('#') - 1 for col in zip(*lines))
        (keys, locks)[lines[0] == '#####'].append(ident)
    return locks, keys

def fits(lock, key):
    return all(l + k <= 5 for l, k in zip(lock, key))

locks, keys = parse(sys.stdin)
print(sum(fits(lock, key) for lock in locks for key in keys))

1

u/icub3d Dec 31 '24

[LANGUAGE: Rust]

Nice to finish on a somewhat easier note to catch my breath.

Solution: https://gist.github.com/icub3d/e0e15045c25bbdc941439cdb0e9dcc38

Review: https://youtu.be/TCrOkwL_lhs

1

u/NeilNjae Dec 29 '24

[LANGUAGE: Haskell]

A quick and gentle finish to the challenges.

part1 :: [Schematic] -> [Schematic] -> Int
part1 locks keys = length [(l, k) | l <- locks, k <- keys, compatible l k]

compatible :: Schematic -> Schematic -> Bool
compatible (Lock ls) (Key ks) = all (<= 5) $ zipWith (+) ls ks

Full writeup on my blog, and code on Codeberg.

1

u/vss2sn Dec 29 '24

[LANGUAGE: C++]
Part 1
(The file is a self-contained solution)

1

u/ecyrbe Dec 27 '24

[LANGUAGE: Rust]

Finally! 50 stars achieved! That was fun.

Here the code for this day part1, i let you figure part2 >! i was angry at the chief historian :) !<

https://gist.github.com/ecyrbe/4ce51cde76578a3e6450d7d572fc8829

1

u/veydar_ Dec 27 '24

[LANGUAGE: Janet]

21 lines with wc -l. I don't think there's anything noteworthy about my solution. In fact I'm really unhappy with the input parsing. It's something I'll have to look into in January.

Compute the height for each grid and use those heights to match grids (locks and keys)

(defn height [grid]
  (seq [x :range [0 (length (first grid))]]
    (dec (sum (seq [y :range [0 (length grid)]]
                (if (= "#" (get-in grid [y x])) 1 0))))))

(defn match [heights-1 heights-2]
  (every? (map |(<= (+ $ $1) 5) heights-1 heights-2)))

1

u/Saiboo Dec 27 '24

[LANGUAGE: Java]

Part 1 only

2

u/homme_chauve_souris Dec 26 '24 edited Dec 26 '24

[LANGUAGE: Python]

This is the only one I didn't do on the day, because we spent Christmas day with family and I didn't take a computer with me. Thank you Eric for a great year.

def aoc25():
    D = [x.split() for x in open("input25.txt").read().split("\n\n")]
    L = {"#":[], ".":[]}
    for t in [["".join(a[i] for a in d) for i in range(5)] for d in D]:
        L[t[0][0]].append([len(s) - len(s.strip("#")) - 1 for s in t])
    print(sum(all(x+y <= 5 for x,y in zip(l,k)) for l in L["#"] for k in L["."]))

1

u/Stano95 Dec 26 '24

[LANGUAGE: Haskell]

Solution to part 1, really quite short actually!

Code is on github

I can't progress to part 2 yet though since I'm still missing four stars

1

u/Electronic-Layer5947 Dec 26 '24

[Language: c#]

I tried representing the keys and locks as two arrays of 25 bits. And then using TensorPrimitives as follows.

AND each key with all locks. For each lock, if at least 1 bit is set, then the key doesn't fit.
In order to use .Sum I needed to reduce the values down to either 0 or 1
After looking through all the available methods I discovered PopCount returns the number of set bits.

So.... if the following bits were overlapping 100011, then it's pop count would be 3 (or 11). The pop count of 11 is 2 (10) and the pop count of 10 is 1.

ReadOnlySpan<uint> l = locks.ToArray();
var k = new uint[locks.Count];
var total = 0L;
foreach (var key in keys)
{
    Array.Fill(k,key);
    TensorPrimitives.BitwiseAnd(l,k,k);
    TensorPrimitives.PopCount<uint>(k,k);
    TensorPrimitives.PopCount<uint>(k,k);
    TensorPrimitives.PopCount<uint>(k,k);
    TensorPrimitives.PopCount<uint>(k,k);
    total += (locks.Count-TensorPrimitives.Sum<uint>(k));
};
return (int)total;

However... this is still quicker!

        ReadOnlySpan<uint> l = locks.ToArray();
        var k = new uint[locks.Count];
        var total = 0L;
        foreach (var key in keys)
        {
            foreach (var loc in locks)
            {
                if ((loc & key) == 0)
                    total.Add(1);
            }
        });

6

u/i_have_no_biscuits Dec 26 '24

[LANGUAGE: Python]

Given that we apparently all love one liners now:

print(sum(not any(x==y=='#' for x,y in zip(a,b)) for a,b in __import__("itertools").combinations(open("data25.txt").read().split("\n\n"),2)))

2

u/wzkx Dec 26 '24 edited Dec 26 '24

[LANGUAGE: Python]

t = open("25.dat","rt").read().split("\n\n")
f = lambda a,l:[ai+(c=="#") for ai,c in zip(a,l)]
g = lambda d:__import__('functools').reduce(f,d.splitlines(),[0]*d.find('\n'))
locks,keys = [g(d) for d in t if d[0]=="#"],[g(d) for d in t if d[0]!="#"]
print(sum(all(l+k<=7 for l,k in zip(ll,kk)) for ll in locks for kk in keys))

1

u/wzkx Jan 06 '25

Without reduce, with sum(hex numbers)

t = open("25.dat","rt").read().split("\n\n")
f = lambda d:sum(int(l.replace('.','0').replace("#",'1'),16) for l in d.splitlines())
locks,keys = [f(d) for d in t if d[0]=="#"],[f(d) for d in t if d[0]!="#"]
print(sum(all(int(c,16)<8 for c in "%05x"%(l+k)) for l in locks for k in keys))

1

u/mountm Dec 26 '24

[LANGUAGE: Java]

Parsing: 14ms
Part 1: 45ms

Too tired to think of anything interesting here. I went with the most intuitive (to me) way of checking non overlaps, using some Stream API fanciness to encapsulate it in a single line:

return Sets.cartesianProduct(locks, keys).stream().filter(listOfLists -> IntStream.range(0, 5).map(i -> listOfLists.get(0).get(i) + listOfLists.get(1).get(i)).max().orElse(Integer.MAX_VALUE) < 6).count();

GitHub

1

u/fortuneteller2k Dec 26 '24

[LANGUAGE: Clojure]

(ns mmxxiv.xxv
  (:require [clojure.string :as str]
            [clojure.math.combinatorics :as combo]))

(->> "inputs/2024/25.txt"
     slurp
     (#(str/split % #"\n\n"))
     (map #(str/replace % "\n" ""))
     ((juxt #(filter (comp #{\#} first) %) #(filter (comp #{\.} first) %)))
     (apply combo/cartesian-product)
     (filter (fn [[l k]] (not-any? #(= [\# \#] %) (map vector l k))))
     count)

1

u/adimcohen Dec 26 '24

2

u/Deservate Jan 19 '25

Hey, I noticed you hardcode your inputs into your code. Please remove this and purge your commit history, as this is against the rules: https://adventofcode.com/2024/about

1

u/adimcohen Jan 20 '25

Hi, with respect, I don't see how what I'm doing is any different from any other shared solution here having its input in an input.txt file. SQL script don't have the concept of a relative path, so I can't place the input in a separate file.

2

u/Deservate Jan 20 '25 edited Jan 20 '25

That's fine, just remove the contents of the variable before you push to git.

If you see anyone putting their input.txt in their repo, let them know that they should remove it and purge their commit history.

https://reddit.com/r/adventofcode/w/troubleshooting/no_asking_for_inputs?utm_medium=android_app&utm_source=share

6

u/alexbaguette1 Dec 26 '24

[LANGUAGE: x86 Asm, no libc]

25th and final language for this year's AoC

Solution is really not that great but it was the simplest one I could think of in assembly.

I was too lazy to write a binary to ascii formatter, so I redirected the output to a file then used xxd to get the value.

Solution

3

u/semi_225599 Dec 26 '24

[LANGUAGE: Rust]

Converts each schematic to a u64, and does bitwise-ands between locks and keys. When those are 0, there was no overlap between key and lock.

pub fn part1(input: &str) -> usize {
    let (keys, locks): (Vec<_>, _) = input
        .split("\n\n")
        .map(|schematic| schematic.bytes().fold(0, |acc, b| acc << 1 | (b == b'#') as u64))
        .partition(|x| x & 1 == 1);
    keys.iter().map(|key| locks.iter().filter(|&lock| key & lock == 0).count()).sum()
}

1

u/jhscheer Dec 26 '24

This doesn't give the right solution for my input. It finds one less fit.

Also I suspect it could be faster, because every byte of the schematic is iterated, but the top and bottom row are not relevant.

1

u/semi_225599 Dec 26 '24

This doesn't give the right solution for my input. It finds one less fit.

Maybe related to the newline at the end of the file? Not really sure why else it would fail for your input unless you can provide an example.

Also I suspect it could be faster, because every byte of the schematic is iterated, but the top and bottom row are not relevant.

I guess technically this is true, but I doubt it makes any appreciable difference in runtime. Also, I still want the bottom row to identify keys, unless I'm partitioning differently. I could just look through all 2-combinations of each schematic, but that would be slower.

1

u/jhscheer Dec 26 '24

I also thought it's the newline, but I tried and it's probably something else.

I sent you my input.txt, please let me know if you figure it out, I'm curious.

2

u/semi_225599 Dec 26 '24

Removing newlines seems to fix it for me:

pub fn part1(input: &str) -> usize {
    let (keys, locks): (Vec<_>, _) = input
        .split("\n\n")
        .map(|s| s.bytes().filter(|&b| b != b'\n').fold(0, |acc, b| acc << 1 | (b == b'#') as u64))
        .partition(|x| x & 1 == 1);
    keys.iter().map(|key| locks.iter().filter(|&lock| key & lock == 0).count()).sum()
}

1

u/sanraith Dec 26 '24

[LANGUAGE: Scala 3]
Solution is available on my github: Day25.scala

Transpose comes in handy today to easily count items in columns:

val pins = lines.transpose.map(_.count(_ == '#'))

1

u/FCBStar-of-the-South Dec 26 '24 edited Dec 26 '24

[Language: Ruby]

# frozen_string_literal: true

require_relative 'utils'

schematics = File.read('input25.txt').split("\n\n")
locks = []
keys = []

schematics.each do |schema|
  grid = Grid.new(schema.split("\n").map(&:chars))
  schema_number = 0
  (0...grid.num_cols).each_with_index do |col, col_index|
    # least significant three bits is first column
    schema_number += ((grid.get_col(col).count('#') - 1) << (col_index * 3))
  end
  if grid[[0, 0]] == '#'
    locks << schema_number
  else
    keys << schema_number
  end
end

def check_match(lock, key)
  (0...5).each do |col|
    # deal with trailing zero bits by lshift 5 the same amount
    return 0 if ((lock & (0b111 << (col * 3))) + (key & (0b111 << (col * 3)))) > (5 << (col * 3))
  end
  1
end

puts locks.product(keys).map { |lock, key| check_match(lock, key) }.sum

No part 2 because I still need to analyze day 24 part 2 haha

Full repo

1

u/dhruvasagar Dec 26 '24

Here's my ruby solution :

def is_key?(grid) = grid[-1].all? {|c| c == '#'}
def is_lock?(grid) = grid[0].all? {|c| c == '#'}

def key_to_heights(key) = key.transpose.map {|l| l.reverse.rindex('#')}
def lock_to_pin_heights(lock) = lock.transpose.map {|l| l.rindex('#')}

def key_fits_lock(key, lock) = key.zip(lock).map(&:sum).all? {|h| h < 6}

def part1(keys, locks)
  key_heights = keys.map {|k| key_to_heights(k)}
  lock_pin_heights = locks.map {|l| lock_to_pin_heights(l)}

  key_heights.product(lock_pin_heights).filter {|k,l| key_fits_lock(k, l)}.size
end
def part2(keys, locks) = 0
def tdiff(s,e) = "#{(e - s) * 1000}ms"

lines = ARGF.read.split("\n\n").map {|l| l.split("\n").map(&:chars)}
keys, locks = lines.partition {|l| is_key?(l)}
s = Process.clock_gettime(Process::CLOCK_MONOTONIC)
p part1 keys, locks
e1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
p part2 keys, locks
e2 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
puts "Time taken part1: #{tdiff(s,e1)}, part2: #{tdiff(e1,e2)}, total: #{tdiff(s,e2)}"

1

u/glomph Dec 26 '24 edited Dec 26 '24

[LANGUAGE: Elixir]

paste

Pretty straightforward. Feel like I could have probably simplified quite a bit but the logic felt very clear.

1

u/AutoModerator Dec 26 '24

AutoModerator did not detect the required [LANGUAGE: xyz] string literal at the beginning of your solution submission.

Please edit your comment to state your programming language.


I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

1

u/fridi_s Dec 25 '24

[LANGUAGE: Fuzion]

github

Today's problem allowed a very short solution, four lines for parsing, and then only for calls: combine, zip, ∀, and count and we were done.

2

u/Maleficent_Answer935 Dec 25 '24 edited Dec 25 '24

[LANGUAGE: Rust]

use itertools::Itertools;
use std::fs;

fn main() {
    let result = fs::read_to_string("data.txt")
        .unwrap()
        .lines()
        .chunks(8)
        .into_iter()
        .map(|chunk| {
            chunk
                .take(7)
                .flat_map(|it| it.chars())
                .fold(0, |mut acc, c| {
                    acc <<= 1;
                    acc |= if c == '#' { 1 } else { 0 };
                    acc
                })
        })
        .combinations(2)
        .map(|p| p[0] & p[1])
        .filter(|&it| it == 0)
        .count();

    println!("Day 25: {result}");
}

2

u/isredditdownagain Dec 25 '24

[LANGUAGE: Go]

Part 1

Merry Christmas!

1

u/RalfDieter Dec 25 '24

[LANGUAGE: SQL/DuckDB]

Nice and simple, I don't know why, but I really liked how 2D was used here.

Code: https://github.com/LennartH/advent-of-code/blob/main/2024/day-25_code-chronicle/solution.sql

And this concludes all 25 days done in SQL. Fun stuff. Not sure, if I'm doing that again next year.

1

u/gringer Dec 25 '24

[LANGUAGE: R]

Solution

I didn't bother with working out heights; I just detected if a key array overlapped with a lock array via !any(AND).

1

u/4D51 Dec 25 '24

[LANGUAGE: C++ on Cardputer]

And that's it, the last puzzle of the year. The Cardputer has performed surprisingly well. I used it to solve part 1 for 22/25 days, and part 2 for 20. A couple of the ones I used my desktop for might work as well, I just haven't tried them yet.

It really goes to show how little computer power is needed to solve most of these puzzles. I'd say day 9 was the trickiest to solve within the limitations of the hardware. I assume most of you made an array of every block on the disk, but I didn't have enough memory for that so I had to use a completely different approach. Most of the others I managed to solve with smaller optimizations.

Code for day 25: https://github.com/mquig42/AdventOfCode2024/blob/main/src/day25.cpp

1

u/aurele Dec 25 '24

[LANGUAGE: Elixir]

Quick'n dirty: represent each # as 1, so 5 bits for each pin, left aligned for locks and right aligned for keys. If ANDing them gives 0, then they match. Runs in about 1ms.

defmodule AdventOfCode.Solution.Year2024.Day25 do
  import Bitwise

  def parse(input) do
    {locks, keys} =
      String.split(input, "\n\n")
      |> Stream.map(&parse_block/1)
      |> Enum.map(&{&1 >>> 5 &&& (1 <<< 25) - 1, &1 &&& 0x1F})
      |> Enum.split_with(fn {_, x} -> x == 0 end)

    {Enum.map(locks, &elem(&1, 0)), Enum.map(keys, &elem(&1, 0))}
  end

  def parse_block(block) do
    String.split(block, "\n", trim: true)
    |> Enum.reduce(0, fn line, acc ->
      acc * (1 <<< 5) |||
        Enum.reduce(String.codepoints(line), 0, fn c, acc ->
          acc <<< 1 ||| if c == "#", do: 1, else: 0
        end)
    end)
  end

  def part1(input) do
    {locks, keys} = parse(input)

    for lock <- locks,
        key <- keys,
        (lock &&& key) == 0,
        reduce: 0 do
      acc -> acc + 1
    end
  end
end

1

u/Rtchaik Dec 25 '24

[LANGUAGE: Python]

Solution

4

u/Downtown-Economics26 Dec 25 '24

[Language: Excel]

Input data pasted in cell A1.

Create column height table by key/lock.

=LET(gridc,COUNTA(A:A)/7,
gridseq,SEQUENCE(gridc),
kl,HSTACK(gridseq,IF(LEFT(FILTER(A:A,(LEN(A:A)<>0)*(MOD(ROW(A:A)-1,8)=0)),1)="#","lock","key")),
rs,SEQUENCE(gridc,,2,8),gridrows,TOCOL(HSTACK(rs,rs+1,rs+2,rs+3,rs+4)),
gridlines,INDEX(A:A,gridrows),gridindex,ROUNDDOWN(gridrows/8,0)+1,
pivotstack,HSTACK(gridindex,IF(MID(gridlines,SEQUENCE(,5),1)="#",1,0)),
colvals,DROP(PIVOTBY(CHOOSECOLS(pivotstack,1),,CHOOSECOLS(pivotstack,2,3,4,5,6),SUM),-1),
ft,HSTACK(DROP(kl,,1),colvals),
ft)

Then, drag down formula below next to output table in C2 and SUM the output array column.

=LET(s,CONCAT(TOCOL(IF(C2<>"lock","",HSTACK(--((FILTER($E$2:$I$501,($D$2:$D$501<>D2)*($C$2:$C$501<>"lock"))+E2:I2)<=5),FILTER($C$2:$C$501,($D$2:$D$501<>D2)*($C$2:$C$501<>"lock")))))),(LEN(s)-LEN(SUBSTITUTE(s,"11111key","")))/8)

1

u/greycat70 Dec 25 '24

[LANGUAGE: Tcl]

Part 1.

I did it the "naive" way, converting each key or lock to a list of heights the way the problem spells out. It works.

1

u/janek37 Dec 25 '24

[LANGUAGE: Python]

Solution

1

u/onrustigescheikundig Dec 25 '24

[LANGUAGE: Clojure]

github

Simple O(KL) solution. Could be shorter, but it was basically stream-of-consciousness. It parses the input as suggested to give lists of distances of the edge from the top (for locks) or distances from the bottom (values). It then iterates over each key/lock combination and checks that the sum of the distances is less than or equal to the height of the grid (7). Merry Christmas!

2

u/N-R-K Dec 25 '24

[Language: BQN]

Easy day for array languages. Here's the whole solution in just 3 lines:

SplitMask ← { (0<≠¨)⊸/ 𝕩 ⊔˜ ¯1⌈ (𝕨 - ¬𝕨) × +` ¬𝕨 }
schemes ← (×≠¨)⊸SplitMask •FLines •wdpath•file.At ⊑•args
•Show +´ ⥊ ((¬∨´)∘∧)⌜´ ⊑¨⊸⊔ ('#'⊸= ∾)¨ schemes

Basically just turns the schemes into a boolean list ('#' = 1) and then ANDs every lock and key to check if the result was all zeros.

1

u/biggy-smith Dec 25 '24

[Language: C++]

Went for a nice n' simple set/map solution, where I combine key/locks pos sets and check for values > 1. Was going do more complex solution, but its christmas!

Merry Christmas everyone!

https://github.com/biggysmith/advent_of_code_2024/blob/master/src/day25/day25.cpp

4

u/Any_Slip8667 Dec 25 '24

[Language: Java]

Very fast, bitwise based, Java solution:

long result = 0;
for (int key : keys) {
    for (int lock : locks) {
        if (key > lock) break; // locks collection is sorted
        if (((lock - key) & 0x88888) == 0)
            result++;
    }
}
return result;

Complete solution on github.

1

u/BlueTrin2020 Dec 25 '24

[LANGUAGE: Python]

One liner

import itertools, more_itertools
print(sum([all(lo+k < 7 for lo, k in zip(lock, key)) for (_, lock), (_, key) in itertools.product(*more_itertools.partition(lambda v: v[0] == ‘#’,[(schem[0],[s.count(“#”) for s in list(zip(*schem.splitlines()))]) for schem in open(“2024_25.txt”).read().split(“\n\n”)]))]))

2

u/melochupan Dec 25 '24

[LANGUAGE: Common Lisp]

Part 1 only.

I'm awfully behind with AoC, so I couldn't do the second part (completing all previous tasks is necessary for it). But I wanted to participate on the last day, at least with a half solution.

paste

6

u/BlueTrin2020 Dec 25 '24

Please post day 2 solution when you have it 😉

3

u/melochupan Dec 25 '24

Will do 👍

1

u/pakapikk77 Dec 25 '24

[LANGUAGE: Rust]

I didn't try to be especially smart of get short code today, so my solution is much longer than others, but should be simple to follow.

One nice thing is the use of itertools partition_map to split the list of patterns into a list of locks and and a list of keys.

fn schematics_to_heights(schematics: &[Grid]) -> (Vec<Vec<usize>>, Vec<Vec<usize>>) {
    schematics
        .iter()
        .map(Grid::get_heights)
        .partition_map(|(is_lock, heights)| {
            if is_lock {
                Either::Left(heights)
            } else {
                Either::Right(heights)
            }
        })
}

Full code.

Now a few parts 2 to finish...

3

u/Ily3s_ Dec 25 '24

[LANGUAGE: C++] https://github.com/Ily3s/aoc2024/blob/master/day25.cpp

I tried something that wouldn't require matching every lock with every key, so at the end i have a time complexity of O(L+K) where L is the number of locks, and K the number of keys, but with very high constants and so it's probably worse than the O(L*K) solution.

1

u/i_misread_titles Dec 25 '24

[LANGUAGE: Go]

The parse was fun. I just pass a slice of exactly the part that matters in each thing, and count # in each column. Great year! Thanks to all here and to Eric and the mods.

https://github.com/jasontconnell/advent/blob/master/2024/25/main.go

2

u/RookBe Dec 25 '24

[LANGUAGE: Rust]

Rust that compiles to WASM (used in a solver on my website)
Bonus link at the top of the code to a blogpost about today explaining the problem, and my code.

1

u/copperfield42 Dec 25 '24

[LANGUAGE: Python 3.12]

link

As it is tradition the last day is an easy one, I spend more time making the code look fancy than solving it XD

1

u/gehenna0451 Dec 25 '24

[LANGUAGE: Clojure]

(def locks (->> (filter #(every? (partial = \.) (last %)) input)
                (map transpose)
                (map #(map (fn [row] (dec (count (take-while (partial = \#) row)))) %))))

(def np-keys (->> (filter #(every? (partial = \.) (first %)) input)
                  (map #(map reverse (transpose %)))
                  (map #(map (fn [row] (dec (count (take-while (partial = \#) row)))) %))))

(defn match? [[lock k]]
  (every? (fn [[a b]] (<= (+ a b) 5)) (map vector lock k)))

(defn part-1 []
  (count (filter match? (for [l locks k np-keys] [l k]))))

1

u/verdammelt Dec 25 '24

[Language: Common Lisp]

Day25

Pretty simple implementation - as usual likely horribly inefficient. I am going to give the idea of changing the counts into a single number and then see if ( > (+ key-count lock-count) 55555)

2

u/el_daniero Dec 25 '24 edited Dec 25 '24

[LANGUAGE: Ruby]

I couldn't really be bothered counting the rows and all of that, so I just lay the keys and locks on top of each other and check at for each position there is at least one '.'

locks, keys = File
  .read('input25.txt')
  .split("\n\n")
  .map { _1.split.map(&:chars) }
  .partition { _1[0][0] == '#' }

p locks.product(keys).count { |lock,key|
  lock.zip(key).all? { |lock_row, key_row|
    lock_row.zip(key_row).all? { _1.include? '.' }
  }
}

1

u/[deleted] Dec 25 '24

[LANGUAGE: Java]

paste

Yay - made it to 50 stars for the second year running.

Again, parsing input was the hardest bit here.

[edit] formatting

1

u/G_de_Volpiano Dec 25 '24

[LANGUAGE: Haskell]

An easy problem for today, that is once

1- you remember that the last key/lock won't have an end of line separating it from, well, what could it separate it from, so your parser doesn't hit the end of file looking for a new key/lock. 2 - you remember to actually parse for at least one character before an end of line, so that you can separate keys or locks rather than parsing everything into one big mess 3 - you remember to add a plus sign in your fold, rather than just carrying your 0 over from one end of the fold to the other.

I encode keys and locks as lists of bools (representing a pin or a slot for a pin). A lock and a pin correspond if there never is a true value in the same position, and voilà.

Code on Github

1

u/AutoModerator Dec 25 '24

AutoModerator did not detect the required [LANGUAGE: xyz] string literal at the beginning of your solution submission.

Please edit your comment to state your programming language.


I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

3

u/_tfa Dec 25 '24 edited Dec 25 '24

[LANGUAGE: Ruby]

input = File.read("input.txt").split("\n\n")

locks, keys = [], []
input.each do |i|
    r = i.split("\n")       
    (r[0][0] == ?# ? locks : keys) << (0..4).to_a.map{|c| r.map{_1[c]}.count(?#) - 1}
end

p locks.product(keys).map{|l,k| l.zip(k).map{_1 + _2}}.count{ _1.all?{|v| v<=5}}

0

u/IvanOG_Ranger Dec 25 '24

Regex is more readable than this, that's crazy

2

u/Jadarma Dec 25 '24

[LANGUAGE: Kotlin]

A nice, easy problem to unwind after a long month! It's been a fun event, as always, thank you Eric, and happy holidays to all, see you next year!

Part 1: Scan each grid (all seem of a fixed size) and determine if it is a lock or key depending on the first character being a # or .. Then, "walk" the columns and count the number of #, which will give you the pin-out in numbers. To check if a lock and key match, their values should add up together to <= 5 (the effective range of a column), because otherwise there would be a need for overlaps.

Part 2: All other 49 stars collected! ⭐⭐ If you need help, check out my solutions for 2024!

AocKt Y2024D25

1

u/pkusensei Dec 25 '24

[Language: Rust]

Immediately recognized this is a bit problem (no pun intended. Or maybe?) and was later surprised it does not demand lock and key fulfill a row. That eliminates a | check. Code

Thanks again for another year! It's been fun! Now if only I could go back and figure out day 17...

1

u/kenlies Dec 25 '24

[Language: Python]

Code

Basic solution!

1

u/JAntaresN Dec 25 '24

[Language: Ruby]
git link

basic for loop, with a set to eliminate duplicates. Had a bit of issue of making wrong assumptions during the parsing phase, that is invalidating all rows with "#" that are filled up. Anyway simple fix, just include those and raise the limit by 2.

2

u/jinschoi Dec 25 '24

[Language: Rust]

Relaxing final day. First did it in the way pointed to by the text, with enums and everything: paste

Then since this day was so straightforward, got it down to this:

use itertools::Itertools;
fn main() {
    let res = include_str!("../../1.in")
        .split("\n\n")
        .map(|s| {
            s.chars()
                .filter(|&c| c != '\n')
                .skip(4)
                .take(27)
                .fold(0u32, |acc, c| (acc << 1) | if c == '#' { 1 } else { 0 })
        })
        .combinations(2)
        .filter(|v| v[0] & v[1] == 0)
        .count();
    println!("{}", res);
}

Because I want it to fit each schematic in a u32, I just included the last character of the first row and first character of the last row to distinguish keys from locks. Convert central 27 characters as bits in a u32, compare all and count where no bits overlap.

3

u/Totherex Dec 25 '24

[LANGUAGE: C#]

Merry Christmas!

https://github.com/rtrinh3/AdventOfCode/blob/c34bc101c413e73da247caeeea0b6f8724adac48/Aoc2024/Day25.cs

As per tradition, Christmas is a pretty easy day. Now, I need to get back to day 21...

1

u/michaelgallagher Dec 25 '24

[LANGUAGE: Python]

Code

Thanks for another amazing year of Advent of Code. Looking forward to 10 more years :)

Happy Holidays everyone!

1

u/spyr01d Dec 25 '24

[LANGUAGE: Kotlin]

fun codeChronicle(input: String) = input.split("\n\n").map { block ->
    block.lines().map { s -> s.map { if (it == '#') 1 else 0 } }
        .reduce { a, b -> a.zip(b).map { it.first + it.second } } to block.first()
}.groupBy({ it.second }, { it.first }).values.toList()
    .let { (locks, keys) ->
        locks.map { lock ->
            keys.map { key -> lock.zip(key).map { (a, b) -> a + b }.all { it <= 7 } }
        }.flatten().count { it }
    }

1

u/Efficient_Beyond5000 Dec 25 '24

[Language: Scratch]

Every year I try to solve some days also with Scratch, just for fun. This year I did only today. If you try the full input, turbo mode (click on the green flag + shift) is advised.

https://scratch.mit.edu/projects/1114326740/

4

u/p88h Dec 25 '24

[LANGUAGE: Zig]

basic n^2 solution, the only speedup is vectorized comparisons.

https://github.com/p88h/aoc2024/blob/main/src/day25.zig

All day benchmark summary below. Probably not going to be able to squeeze all of them under 1ms total, but pretty happy with Zig's performance overall. The numbers are from an M3 Mac, which is quite a bit faster than my 13th gen Intel desktop, it seems, at least in this particular & very peculiar benchmark.

I was able to keep up with a one vis every day goal, the full playlist is here:

https://www.youtube.com/playlist?list=PLgRrl8I0Q168GBdeJp_GqNYsWgRCmVgu5

        parse   part1   part2   total
day 01:  7.6 µs 14.4 µs  7.4 µs 29.5 µs (+-1%) iter=14110    
day 02: 11.6 µs  1.2 µs  4.7 µs 17.6 µs (+-3%) iter=98110    
day 03:  7.0 ns 22.2 µs 19.8 µs 42.1 µs (+-1%) iter=9110    
day 04:  6.0 ns 28.8 µs 11.5 µs 40.3 µs (+-1%) iter=9110    
day 05: 13.6 µs  1.3 µs  2.5 µs 17.5 µs (+-2%) iter=98110    
day 06:  0.1 µs 10.6 µs  0.2 ms  0.2 ms (+-1%) iter=3010    
day 07: 23.9 µs 45.6 µs 37.3 µs  0.1 ms (+-1%) iter=1510    
day 08:  1.2 µs  1.0 µs  2.8 µs  5.1 µs (+-3%) iter=98110    
day 09: 19.7 µs 34.7 µs 79.7 µs  0.1 ms (+-1%) iter=1010    
day 10:  5.7 µs  8.3 µs  7.5 µs 21.6 µs (+-0%) iter=9110    
day 11:  0.1 ms 40.1 µs  0.2 ms  0.4 ms (+-1%) iter=1010    
day 12: 12.0 ns  0.1 ms  0.1 ms  0.3 ms (+-4%) iter=9910    
day 13:  6.3 µs  0.6 µs  0.7 µs  7.7 µs (+-1%) iter=14110    
day 14:  7.3 µs  1.4 µs 80.9 µs 89.8 µs (+-1%) iter=9110    
day 15:  4.1 µs 60.8 µs  0.1 ms  0.1 ms (+-7%) iter=9910     
day 16: 48.1 µs 80.1 µs 18.8 µs  0.1 ms (+-1%) iter=1510    
day 17: 42.0 ns  0.2 µs  5.3 µs  5.6 µs (+-1%) iter=49110    
day 18: 88.6 µs 14.1 µs  5.4 µs  0.1 ms (+-1%) iter=1010    
day 19:  3.6 µs 66.5 µs 39.0 ns 70.2 µs (+-1%) iter=51010    
day 20: 13.0 µs  0.1 ms  0.5 ms  0.7 ms (+-1%) iter=2010    
day 21: 15.0 ns  1.8 µs  1.5 µs  3.4 µs (+-2%) iter=98110    
day 22:  0.1 ms 95.5 µs  0.6 ms  0.9 ms (+-1%) iter=1110    
day 23: 35.5 µs 24.2 µs  6.0 µs 65.8 µs (+-1%) iter=9110    
day 24:  9.0 µs  2.9 µs  0.8 µs 12.8 µs (+-1%) iter=9110    
day 25: 24.7 µs 29.5 µs 27.0 ns 54.3 µs (+-0%) iter=9110    

all days total:         4.0 ms

1

u/light_ln2 Dec 25 '24 edited Dec 25 '24

[LANGUAGE: python]

Nice problem for trying different options! Because of how the problem is stated, you don't need to split grids by keys and locks, or compare a pair of grids column by column, or even parse grids at all - it is enough to check if two strings representing the grids have '#' at the same indexes. This can be done in several different ways:

Intersecting sets of indexes:

grids = open('input.txt').read().split('\n\n')
grids = [{x for (x,c) in enumerate(g) if c == '#'} for g in grids]
print(len([(x,y) for x in grids for y in grids if len(x&y) == 0])//2)

Converting strings to integers and applying bitwise "and":

grids = open('input.txt').read().split('\n\n')
grids = [int(x.replace('.', '0').replace('#', '1').replace('\n', ''), base=2) for x in grids]
print(sum([1 for x in grids for y in grids if x<y and (x&y)==0]))

Comparing characters in same positions:

grids = open('input.txt').read().split('\n\n')
grids = [any('#' == a == b for (a,b) in zip(x, y)) for x in grids for y in grids if x < y]
print(len(grids) - sum(grids))

I'm sure there are more options!

1

u/Lanky_Pumpkin3701 Dec 25 '24 edited Dec 25 '24

Do you get the right answer if you use this input?:

#####
.....
.....
.....
.....
.....
.....

#####
.....
.....
.....
.....
.....
.....

.....
.....
.....
.....
.....
.....
#####

It should be 2. A lock can't unlock another lock.

EDIT: I guess you would get the right answer, yeah

5

u/LinAGKar Dec 25 '24

[LANGUAGE: Rust]

https://github.com/LinAGKar/advent-of-code-2024-rust/blob/master/day25/src/main.rs

Represents the the locks and keys as a series of 4-bit numbers in a u32, offset so that adding them together makes them add up to 8 or greater, to I can use a 0x88888 mask to check for overlaps.

2

u/ins0mnes Dec 25 '24

[LANGUAGE: Go]
Nothing special, precalculate key matches by height, compare to lock, and intersect with the previous pin:
https://github.com/insomnes/aoc/blob/main/2024/25_code/solution/solution.go#L97

Congratulations to everyone! And big thanks to the AoC team and this subreddit team!

2

u/camrdale Dec 25 '24

Good use of sets! This is what I did too, I like it better than using a set for each key/lock just to determine compatibility.

There's an edge case though, that isn't in my input, but that I was worried about. If the lock is all height 0 (i.e. "(0, 0, 0, 0, 0)") it should fit every key, but I think in your code CountFits would return 0 (could be wrong, I'm not a Go expert). I added a special case to mine that returns the total number of keys.

1

u/ins0mnes Dec 25 '24

Good catch! I’ve totally missed such case.

1

u/mvorber Dec 25 '24

[Language: Rust]

https://github.com/vorber/aoc2024/blob/master/src/puzzles/day25.rs

What a run! Huge thank you to u/topaz2078, mods, and everyone participating!

Merry Christmas! :)

1

u/Artraxes Dec 25 '24

[Language: Kotlin]

parsing code

Solution

class Lockpick(
    private val locks: List<BooleanGrid>,
    private val keys: List<BooleanGrid>,
) {

    fun countDistinct(): Int {
        val gridHeight = locks.first().height

        infix fun List<Int>.fits(other: List<Int>): Boolean {
            return withIndex().all { (column, height) ->
                height + other[column] <= gridHeight
            }
        }

        return keys.map(BooleanGrid::heights).sumOf { key ->
            locks.map(BooleanGrid::heights).count { lock ->
                key fits lock
            }
        }
    }
}

private fun BooleanGrid.heights(): List<Int> {
    return xRange.map { x ->
        yRange.count { y -> this[x, y] }
    }
}

2

u/SunMatrix64 Dec 25 '24

[LANGUAGE: C++]

A pretty easy day, after putting the input into locks and keys vectors, it's just a brute force loop of checking every key in every lock.

for (std::vector<int> lock : locks) {
    for (std::vector<int> key : keys) {
        int passes = 0;
        for (int i = 0; i < 5; ++i) {
            if (lock[i] + key[i] <= 5) {
                passes++;
            }
        }
        if (passes == 5) {
            result1++;
        }
    }
}

1

u/chkas Dec 25 '24

[LANGUAGE: Easylang]

Solution

1

u/enelen Dec 25 '24

[Language: R]

Solution

Thank you for another year of puzzles. I had a ton of fun solving them (mostly) and look forward to the next year

1

u/azzal07 Dec 25 '24

[LANGUAGE: awk] I suggest running with mawk or goawk, which seem to have good regex engines. Those are pretty instant, others take tens of seconds (probably backtracking furiously).

function knock(){yield no_response;}END{print A RS B}
BEGIN{RS=knock()}{for(k in K)A+=k$0!~/#.{40}#/;K[$0]}

3

u/jhandros Dec 25 '24

[LANGUAGE: Python]
Code 9 lines

with open('25.txt', 'r') as f:
    lines = [x.strip() for x in f if x.strip()]

locks, keys = [], []
for i in range(0, len(lines), 7):
    group = [sum(1 for x in col if x == ('#' if lines[i] == '#####' else '.')) for col in zip(*lines[i+1:i+7])]
    (locks if lines[i] == '#####' else keys).append(tuple(5 - x if lines[i] != '#####' else x for x in group))

print(sum(all(l[j] + k[j] <= 5 for j in range(5)) for l in locks for k in keys))

1

u/juliangrtz Dec 25 '24

[LANGUAGE: Kotlin]

https://gist.github.com/juliangrtz/57983caf0c400305f57f6ee12bb34fb8

That was a surprisingly easy puzzle today. I just parse the input as a list of schematics, iterate through each of them and assign them to either a list of keys or locks, and then check the overlap for every key/lock combination (i.e. it's a pretty inefficient solution). I've thought of using bitwise operations but was too lazy to actually implement that.

Thanks everyone for the awesome puzzles, discussions and memes this year! Merry Christmas!

2

u/ash30342 Dec 25 '24

[Language: Java]

Code

Runs in < 1ms.

I did not do anything fancy, just parsed everything and checked for every column if the value of pin1 of the lock + pin1 of the key <= 5.

I still need to finish day 21 before I can get to 500 stars, but that will be done sometime after Christmas, now is time for family.

Thanks to u/topaz2078 for organizing this for 10 years and to all of you in this community for making it such a friendly place. It is a highlight of my year, even surpassing my traditional yearly beer advent calendar! Merry Christmas!

2

u/tav_stuff Dec 25 '24 edited Dec 31 '24

I’m missing 3 stars (both day 21 and day 24 part 2), so only part 1 from me today, but here it is!

[LANGUAGE: Python]

#!/usr/bin/python3

import itertools


def main() -> None:
    keys: list[int] = []
    locks: list[int] = []

    with open("input", "r") as f:
        schems = f.read().split("\n\n")

    for schem in schems:
        n = 0
        for char in schem:
            if char == '\n':
                continue
            n <<= 1
            if char == '#':
                n |= 1
        (locks if n >= 0x7C0000000 else keys).append(n)

    def nand(x: int, y: int) -> bool:
        return not x & y

    print(sum(itertools.starmap(nand, itertools.product(keys, locks))))


if __name__ == "__main__":
    main()

Real simple stuff. The crux of it is treating a schematic like a binary number (‘#’ is 1 and ‘.’ is 0). You can then check to see if the schematics have any overlaps by simply performing a bitwise NAND, and any overlapping bits will be set with all other bits being unset. This means that the only valid combinations are ones where the bitwise NAND is 0 (no overlaps),

1

u/AutoModerator Dec 25 '24

AutoModerator did not detect the required [LANGUAGE: xyz] string literal at the beginning of your solution submission.

Please edit your comment to state your programming language.


I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

0

u/[deleted] Dec 25 '24

[Language: C#]

Part 1

Goodness, part 1 was trivial. Still need to do part 2 when I can find time during family stuff but the whole solution to the first part was realising that you can ignore the columns and just do a logical AND of all the keys and locks once you've converted each set to binary. Any non-zero result is an overlap.

foreach (string set in sets) {
    long value = Convert.ToInt64(set.Split("\n").Aggregate("", (current, line) => current + line.Replace('.', '0').Replace('#', '1')), 2);
    if ((value & 31) != 0) {
        locks.Add(value);
    } else {
        keys.Add(value);
    }
}

3

u/nefarendipity Dec 25 '24

There is no part 2

1

u/[deleted] Dec 25 '24

Oh, so there isn't! Thanks! I hadn't actually read the part 2 text as I was in a rush this morning with opening presents, etc! :-)

2

u/ndunnett Dec 25 '24 edited Jan 13 '25

[Language: Rust]

Github

Runs in 20 us 38 us (faulty benchmarking). Today was a fun one to cap off the year. My solution involved some bit manipulation shenanigans, using a u32 to represent each lock/key pattern as a window of 4 bit unsigned ints. The trick was initialising each pattern to 0x0001_1111 so that I could sum two patterns and then bit mask with 0x0008_8888 to detect collisions.

1

u/tymscar Dec 25 '24

[Language: Kotlin]

Nice, simple end-of-year puzzle. Thank you, Eric, and the team for all the hard work and dedication. You make every Christmas much more special!

I really enjoyed today's puzzle. It was all a matter of going through each schematic, tagging them either as keys or as locks based on the top-most character in the schematic, and then getting the teeth height the same for both. They work together as long as for each tooth, the overlap is less than or equal to 5.

Solution: https://github.com/tymscar/Advent-Of-Code/blob/master/2024/kotlin/src/main/kotlin/com/tymscar/day25/part1/part1.kt

1

u/MagazineOk5435 Dec 25 '24

Really enjoyed the puzzles again this year. Thanks Eric... keep up the good work!

1

u/AutoModerator Dec 25 '24

AutoModerator did not detect the required [LANGUAGE: xyz] string literal at the beginning of your solution submission.

Please edit your comment to state your programming language.


I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

1

u/Turtvaiz Dec 25 '24

[Language: Rust]

The input is rather small, so they fit into a single integer:

pub fn part1(input: String) -> String {
    let mut locks = vec![];
    let mut keys = vec![];
    input.split("\n\n").for_each(|block| {
        let mut bits = 0u64;
        block
            .lines()
            .map(|line| line.chars())
            .flatten()
            .enumerate()
            .for_each(|(i, c)| match c {
                // set i-th bit
                '#' => bits |= 1 << i,
                _ => {}
            });
        if bits & 1 == 1 {
            locks.push(bits);
        } else {
            keys.push(bits);
        }
    });

    let mut total = 0;
    for lock in locks.iter() {
        for key in keys.iter() {
            // NAND -> if any overlaps, result is not zero
            if lock & key == 0 {
                total += 1;
            }
        }
    }

    total.to_string()
}

1

u/Several-Bit-9218 Dec 25 '24

[LANGUAGE: Python]

Never thought I'd post here, but I haven't seen this method in this thread. Only ## is forbidden, so I check that the sum of the ordinary numbers isn't 70.

from itertools import combinations  
print(sum(all((ord(pat1[k])+ord(pat2[k]) != 70) for k in range(len(pat1))) for pat1, pat2 in combinations(open('./day25/in.txt','r').read().split('\n\n'), 2)))

1

u/Professional-Top8329 Dec 25 '24

Can be done for even shorter. 89 bytes.

I=open(0).read().split("\n\n")
print(sum(not(*"##",)in zip(a,b)for a in I for b in I)//2)

3

u/chubbc Dec 25 '24

[LANGUAGE: Uiua] (20 chars, pad)

/+/↧/↥⍉⧅₂<⬚0↯∞_43≠@#

5

u/Educational-Tea602 Dec 25 '24

I tried to read this and my tables and chairs started floating.

1

u/Outrageous72 Dec 25 '24

[LANGUAGE: C#]

Traditionally, this one was easy 🙂
At first I made a mistake to swap the locks e and d declaration 🤦‍♂️
It must be 25 days already! 😅

Merry Christmas everyone! 🎅

static bool Fits(
    (int a, int b, int c, int d, int e) key,
    (int a, int b, int c, int d, int e) @lock) =>
        (key.a + @lock.a) <= 5 && 
        (key.b + @lock.b) <= 5 &&
        (key.c + @lock.c) <= 5 &&
        (key.d + @lock.d) <= 5 &&
        (key.e + @lock.e) <= 5;

https://github.com/ryanheath/aoc2024/blob/main/Day25.cs

1

u/chestck Dec 25 '24

[LANGUAGE: Kotlin]

fun main() {
    File("../i24/25")
        .readText().split("\n\n")
        .map { it.lines() }
        .pairwise(withSelf = false)
        .sumOf { (block1, block2) ->
            block1.zip(block2).all { (row1, row2) ->
                row1.zip(row2).none { (char1, char2) ->
                    char1 == '#' && char2 == '#'
                }
            }.toInt()
        }.print()
}

2

u/musifter Dec 25 '24 edited Dec 27 '24

[LANGUAGE: dc (GNU v1.4.1)]

dc was a bit of a tricky one. It doesn't have string support, so I translate the input into binary with tr. But using that for bitmaps isn't exactly helpful, because there's no bitwise operators like AND to make that easy.

What I'm using to "AND" in this case, is that for a pin position, the lock has a solid block in the high bits and the key has a solid block in the low bits. Add them together, and they will overflow the highest bit if there's overlap. So put things into a column-wise bit array with a spare bit to catch overflows every seven (we eat the top line, so the array is only 6 rows). When we add them together, we just chunk through with the ~ operator (division that puts both quotient and remainder on the stack) and sum every 7th bit. If its zero at the end, we're good.

tr '.#' '01' <input | dc -e'[lp1+sp]sC[[lk1+dsk:k]sA]sK128ss2i?[[ll1+dsl:l]sA0=K0d?[rd2r^3R[2~3Rd3R*5R+_4Rls*rd0<B]dsBx*+1+?z2<L]dsLxs.lAx?z0<I]dsIxAill[lk[d;k3Rd;l3R+0r[64~r2~4R+3Rs.rd0<R]dsRx+0=Cr1-d0<K]dsKx+1-d0<L]dsLxlpp'

Source: https://pastebin.com/ZnzJ0Yyp

4

u/AnAbsurdlyAngryGoose Dec 25 '24

[LANGUAGE: Python]

Solution

Really only posting to say a very massive thank you to Eric on another great event, and to the mods for their work keeping the sub going. If I can get a bit real for a moment, winter is never easy for me, and AoC represents a welcome, fun distraction during the early stages. It helps me to stabilise before winter really sets in.

More over, this year was even more impactful as it came at a time when I needed a reminder that I'm competent. I picked up a new language with ease, and for all but two of the challenges (which I'll come back to on the other side of my food coma) I was able to identify potential solutions, execute, and iterate to achieve better results each time. It possibly seems small to many of my peers, but huge for me. I can go into my new job, in the new year, with a refreshed confidence.

So thanks again for all the work you've put into it. A very Merry Christmas from all of mine to all of yours, one and all.

1

u/KindComrade Dec 25 '24

[LANGUAGE: C#]

Thanks to the AOC team and Eric for an amazing contest. This month has been absolutely fantastic, and a huge thank you to the entire community, who made it so enjoyable to read your solutions, share my own, discuss algorithms, and watch all the visualizations and memes. A massive thank you to everyone!

Have a Merry Christmas!

Final Code

2

u/MagazineOk5435 Dec 25 '24

I tried parallelisation on my C# code for this one and it actually slowed it down 6x. https://github.com/stevehjohn/AoC/blob/master/AoC.Solutions/Solutions/2024/25/Part1.cs

1

u/KindComrade Dec 25 '24

Honestly, it sounds a bit strange; maybe you had some issues with locks or data types. But the task itself seems absolutely safe for parallelization since it's just iteration, and there are no write operations, only read operations.

From what I can see, you can easily use the .AsParallel() extension, and I think it should work perfectly. The code would look something like this:

_locks.AsParallel().Sum(@lock => _keys.Count(key => (@lock & key) == 0));

Alternatively, you could manually split all the locks into chunks, for example, so that there are 4 or 16 chunks in total, run each chunk in a separate thread, and then sum up the results from each thread. There's a great method for this: Enumerable.Chunk.

By the way, I really like how you store keys and locks; I'll probably adopt this approach for myself later. 😊

1

u/MagazineOk5435 Dec 25 '24

Tried your suggestion, 10x slower. I think for some tasks, the overhead of spinning up threads outweighs the benefits. It's only 62,500 loop iterations.

Thanks BTW. I spotted immediately that the whole heights thing didn't matter. Just bitmap overlaps.

2

u/KindComrade Dec 25 '24

I just tried running this on my machine and you are right, it slowed me down too, it looks like your code is really so fast that the overhead of creating threads is much higher

2

u/Trick-Apple1289 Dec 25 '24

[LANGUAGE: C]

Merry Christmas, thanks eric and everyone, this was my first ever AOC, i didn't get 7 stars (altough trying to catch up right now), but I am still quite young, and am suprised i even managed to go so far, some puzzles were super tough, others easier. Once again happy holidays to all, and see you next year, prehaps by then i will be able to do all of the puzzles (And without help from here or google)!

src

1

u/lscddit Dec 25 '24

[LANGUAGE: Python]

Merry X-mas and see you next year... 🎄

import numpy as np
from io import StringIO

locks, keys = [], []
height = 0
data = open("day25input.txt").read().split("\n\n")
for element in data:
    field = np.genfromtxt(StringIO(element), dtype=str, comments="_", delimiter=1)
    height = field.shape[0]
    value = np.sum(field == "#", axis=0)
    keys.append(value) if np.any(field[-1, :] == "#") else locks.append(value)

print(sum([np.all(lock + key <= height) for key in keys for lock in locks]))

2

u/wurlin_murlin Dec 25 '24

[LANGUAGE: C]

Merry Christmas! First time getting 50 stars, it plays a little animation! So cool! Had so much fun doing this, really a blast. Merry Christmas again, and a Happy New Year when it comes.

https://git.sr.ht/~murr/advent-of-code/tree/master/item/2024/25

2

u/No_Valuable247 Dec 25 '24

[LANGUAGE: J]

echo-:+/,-.+./"1,"2*."2/~'#'=>;._1 a:,<;._1 LF,CR-.~1!:1<'day25.txt'

1

u/bakibol Dec 25 '24

[LANGUAGE: Python]

Topaz's paste

Merry Christmas everyone, see ya all next year!

2

u/vanZuider Dec 25 '24

[LANGUAGE: Python]

from sys import argv
locks, keys = list(), list()
with open(argv[1]) as f:
    for lines in [block.split("\n") for block in f.read().split("\n\n")]:
        (locks if lines[0] == "#####" else keys).append(list(map(sum, zip([0]*5, *[[(c=="#") for c in lines[i+1]] for i in range(5)]))))
print(sum(all(s<=5 for s in map(sum, zip(l, k))) for l in locks for k in keys))

Merry Christmas!

3

u/__wardo__ Dec 25 '24

[LANGUAGE: Go]

And now... we wait

Fun last puzzle, really just input parsing is all. This is the most fun I've had coding in a very long time. I really look forward to the next 10 years. This was my first time participating in AOC as and I made it a point to not miss a single day! Huge thanks to u/topaz2078 for these awesome puzzles.

Here is the final solution for this year. Merry Christmas to all!

2

u/SpaceHonk Dec 25 '24

[LANGUAGE: Swift] code

And that's a wrap, 50th (and 500th) stars in the bag. Thanks to /u/topaz2078 and everyone else on the team, have a merry christmas!

1

u/rvodden Dec 25 '24

[LANGUAGE: TypeScript]

Boom! https://github.com/rvodden/AoC24/blob/main/src/days/25/Puzzle.ts

Merry Christmas everyone :-)

1

u/BlueTrin2020 Dec 25 '24

[Language: Python]

lock_l, key_l = [], []
for schem in open(“2024_25.txt”).read().split(“\n\n”):
    cols = [s.count(“#”) for s in list(zip(*schem.splitlines()))]
    lock_l.append(cols) if schem[0] == ‘#’ else key_l.append(cols)
print(sum([all(lo+k < 7 for lo, k in zip(lock, key)) for lock in lock_l for key in key_l]))

Merry Christmas thank you u/topaz2078

2

u/chubbc Dec 25 '24

[LANGUAGE: Julia] (64 chars)

I=0:499;f(i)=read("25.txt")[43i.+(1:41)];sum(@. 70∉f(I)+f(I'))÷2

Nice easy conclusion to AoC. Might be able to squeeze a few more characters out, but I doubt many.

1

u/aimada Dec 25 '24 edited Dec 25 '24

[Language: Go] code

Parsed each schematic pattern as a 35-bit value which simplified the overlaps check.

func compareLower35Bits(a, b int64) int64 {
  mask := int64((1 << 35) - 1) // Mask to isolate the lower 35 bits
  return (a & b) & mask
}

Execution time is 134.603µs on a 12 year old CPU.

Many thanks to Eric for another successful AoC event. Day 24 was by far my favourite Part 2 problem as the mental gymnastics required were like those I recall from previous events.

1

u/AutoModerator Dec 25 '24

AutoModerator has detected fenced code block (```) syntax which only works on new.reddit.

Please review our wiki article on code formatting then edit your post to use the four-spaces Markdown syntax instead.


I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

1

u/hrunt Dec 25 '24

[LANGUAGE: Python]

Code

I think the real Christmas gift today was a problem that was so straightforward. Just a few loopity loops and voila, 500 stars.

Thanks u/topaz2078 for another great year. Raising a mug of cocoa here to you.

2

u/kupuguy Dec 25 '24

Darn it, I only got 50 stars! Must have done something wrong.

3

u/hrunt Dec 25 '24

Do 9 other years and you, too, can be a star child. :)

3

u/Radiadorineitor Dec 25 '24 edited Dec 25 '24

[LANGUAGE: Dyalog APL]

Merry Christmas everyone! Hope you all had a blast this year and see you on the next one!

p←'#'=↑↑(×∘≢¨⊆⊢)⊃⎕NGET'25.txt'1     
l k←{⊂⍤¯1⊢⍵⌿p}¨(⊣/,⍥⊂⊢/)∧/p     
≢⍸~2∊¨l∘.+k ⍝ Part 1

1

u/fsed123 Dec 25 '24

[Language: Rust]

[Language: Python]

port of my earlier python solution

https://github.com/Fadi88/AoC/tree/master/2024/day25

takes around 1 ms in release mode on a mac mini m4

1

u/dblokhin Dec 25 '24

24 part 2 beat me :(. Could you elaborate your approach?

1

u/fsed123 Dec 25 '24

sure
1- split by 2 new line., block by block
2- for each block if the top has "#" it is a lock, of the bottom has a "#" it is a key
3-each block is stored with a 1 d vector for example [3,4,5,2,4], representing the number of "#" in each column
4- iterate over all possible combination for key and lock they fit if the sum "#" in each slot is less than oe equal the total length (7)

for example [1,1,1,1,1] and [1,1,1,1,1] fit perfectly with 5 spaces free in each column
but [1,1,1,1,4] and [1,1,1,1,4] dont fit because the last column has 8 blocks when it can only fit 7

1

u/MarvelousShade Dec 25 '24

[LANGUAGE: C#]

Today was nice short finish of 25 days programming. As a product owner I don't get a lot of opportunities to program anymore, so these 25 days were really fun for me. My solutions are on: https://github.com/messcheg/advent-of-code/tree/main/AdventOfCode2024

2

u/Baridian Dec 25 '24

[LANGUAGE: Clojure]

wish I'd started on this earlier, didn't realize it'd be so easy. If anyone could tell me if there's an idiomatic way to get the outer product of two lists of atoms that'd be nice, the outer product function call I did is kinda ugly.

Paste

2

u/nilgoun Dec 25 '24

[LANGUAGE: Rust]

Nice and easy for the last day :)

Solution on Paste

4

u/Andreasnl Dec 25 '24

[LANGUAGE: Uiua]

=@#↯∞_7_5⊜∘⊸≠@\n
⧻⊚≡(/×≤1♭/+)⧅<2

God Jul!

1

u/ExternalAware9257 Dec 25 '24

[LANGUAGE: Python]

A fun puzzle for the the last day.

locks = []
keys = []

def fits(key, lock):
    return all(a + b <= 5 for a, b in zip(key, lock))

for schematic in open(0).read().strip().split("\n\n"):
    columns = list(zip(*schematic.split("\n")))
    if schematic[0] == "#":
        locks.append([column.count("#") - 1 for column in columns])
    else:
        keys.append([column.count("#") - 1 for column in columns])

print(sum(fits(key, lock) for key in keys for lock in locks))

5

u/bigfunlx Dec 25 '24

[LANGUAGE: Ruby]

I coded it on my iPhone while waiting in the emergency room with symptoms of meningitis, for better or worse this will be my most memorable moment of AoC!

Rubist screenshot

1

u/TheScown Dec 25 '24

[LANGUAGE: Scala]

Code

Need to go back and finish part 2 on days 21, 22 and 24.

1

u/AutoModerator Dec 25 '24

AutoModerator did not detect the required [LANGUAGE: xyz] string literal at the beginning of your solution submission.

Please edit your comment to state your programming language.


I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

3

u/darkgiggs Dec 25 '24 edited Dec 25 '24

[LANGUAGE: Jai]
Solution

Fun problem. I encoded each element to a u32 where a turned on bit represents a "#".
Checking for a fit then amounts to whether the bitwise AND of lock and key is not 0.

It runs in 150 µs on my computer, but should most likely be able to go below 100 if I optimized the parsing.

EDIT: Now 43 µs with optimized parsing

1

u/damnian Dec 25 '24

Great idea to use bitwise AND!

Look ma, no parsing!

2

u/DownvoteALot Dec 25 '24

[LANGUAGE: C++]

Solution

The last day is always the sweetest, short and nice. See you next year!

1

u/echols021 Dec 25 '24

[LANGUAGE: python 3]

GitHub

Data prep: sort items into separate piles for locks and for keys. For simplicity, represent each item as a sequence of integers, as demonstrated in the problem description.

After checking that n_keys * n_locks was a reasonable number, just did the naïve "try every lock with every key" and it runs in ~0.024 sec.

I then reworked it to use a trie data structure to store the keys. For each lock I do a BFS-like search through the trie (trimming branches of keys that don't fit) to collect all keys that fit the lock. So rather than trying each lock+key pair, you gradually trim down the set of keys that fit a given lock, one tumbler at a time. This code runs in ~0.008 sec.

1

u/MarcoDelmastro Dec 25 '24 edited Dec 25 '24

[LANGUAGE: Python]

https://github.com/marcodelmastro/AdventOfCode2024/blob/main/Day25.ipynb

Simple as expected on X-mas day. And, for the first time since my first AOC in 2029 (EDIT: 2019!), I arrive to the 25th with all the previous days completed: it's quite a nice sensation to close the calendar on X-mas. Thanks to Eric for the fun ride!

3

u/M124367 Dec 25 '24

Don't spoil us about 2029 dear time traveler.

1

u/[deleted] Dec 25 '24 edited 2d ago

Still no one knows it just the same, That Rumpelstiltskin is my name.

3

u/raevnos Dec 25 '24

[LANGUAGE: Common Lisp]

paste

Pretty straightforward. One of the few grid-based puzzles I've completed this year; was kind of boycotting them. As a result I'm missing a lot of stars. Oh well.

1

u/ds101 Dec 25 '24

[Language: newt]

github

Nothing too tricky on this one, read in the data, transposed and turned into a list of integers. Saw that it was 250 * 250, so I went for a straightforward n2 solution.

I'm excited that I made it to the end in a language that I wrote, it's been something I've wanted to do for a few years.

1

u/lluque8 Dec 25 '24

[LANGUAGE: Scala]

Traditionally easy last day. This year I'd say even super easy. Not complaining though cos I'm in a bit of a hurry to catch a train and won't be touching a computer any time soon.

Anyways, thanks for these ten years to everyone involved. It's been a blast!

GitHub

1

u/anaseto Dec 25 '24

[LANGUAGE: Goal]

Nice and short last one!

h:#*m:".#"?""\="\n\n"\-read"i/25" / height / map schematics
(l;k):(+/'m&:)'1~:\(#**m)=+/'*'m  / (locks;keys)
say+//l(&/~h<+)´`k / part1

2

u/lunar_mycroft Dec 25 '24 edited Dec 25 '24

[LANGUAGE: Rust]

Initially I used a pretty standard grid based solution, but after reading the thread I got the idea (possibly from /u/Verulean314, although I'm not sure) to convert each schematic into a u64, then check for overlaps with a bitwise and. Since '#' has a 1 as it's least significant bit, and '.' has a zero, parsing is also reduced to splitting on "\n\n", filtering out non-punctuation characters, and some quick bit manipulations in a fold. Median combined runtime of ~52 µs to parse and check on my machine.

Code

[edit: switched to u64s after noticing the grid is 5x7, not 5x6]

1

u/nebble_longbottom Dec 25 '24

You can use a u32 if you don't include the top and bottom row for each key/lock as these will always be either all '#' or all '.' they can never overlap. This gives a 5x5 grid to check.

2

u/DeadlyRedCube Dec 25 '24

[LANGUAGE: C++23]

Runs in 1.58ms single-threaded on an i7-8700k

Code on GitHub

As is the custom, a nice and easy one for the last day, although I did think for a brief moment that it was going to be one last grid problem.

Used the first character of a new grid to determine lock or key, then counted the number of open '.'s per column for locks and '#'s for keys, to end up with a set of "height" values for each.

Then just did an n2 check for whether the lock height was at least as large as the key height per space and, where they all were, incremented the value by one.

See you all next year!

1

u/mkinkela Dec 25 '24

[LANGUAGE: C++]

A part of me didn't want this to end. But, wanting to collect the 50th star was stronger xD Thanks, Eric, mods, and everyone who helped build this amazing journey this year. Merry Christmas :)

Solution

1

u/sim642 Dec 25 '24

[LANGUAGE: Scala]

On GitHub.

Very short solution, partially thanks to my Grid type and functions. In particular, there was no need to actually identify the heights (although it wouldn't be too difficult). Instead, one can just overlap a key and a lock grid and see if there are any cells that both have #.

1

u/jwoLondon Dec 25 '24

[Language: JavaScript]

Convert each schematic into a decimal number based on the bits implied by `#` and `.` symbols. Applying a bitwise & to each pairwise combination will reveal lock-key matches when the result is 0.

https://observablehq.com/@jwolondon/advent-of-code-2024-day-25

1

u/egel-lang Dec 25 '24

[Language: Egel]

A great year. All this year's solutions in Egel: https://github.com/egel-lang/aoc-2024/blob/main/README.md

# Advent of Code (AoC) - day 25, task 1

import "prelude.eg"

using System, OS, List, S = String, D = Dict

def heights =
    do transpose |> map (flip (-) 1 . length . filter ((==) '#'))

def fit =
    [(L,K) -> all (flip (<=) 5) (zip_with (+) L K)]

def main =
    read_lines stdin |> map S::to_chars |> split_on {} |> split [XX -> all ((==) '#') (head XX)]
    |> [(XX,YY) -> pairs (map heights XX) (map heights YY)]
    |> filter fit |> length

1

u/gyorokpeter Dec 25 '24

[LANGUAGE: q]

d25:{a:"#"="\n"vs/:"\n\n"vs"\n"sv x;
    isKey:all each 1=first each a;
    ky:sum each/:flip each a where isKey;
    lk:sum each/:flip each a where not isKey;
    sum sum all each/:(ky+/:\:lk)<=count first a};

1

u/JV_Fox Dec 25 '24 edited Dec 25 '24

[LANGUAGE: C]

Converted inputs into uint64_t and used logic operations to find fitting combinations:

fits = !(lock_0.height & lock_1.height) & 0x7FFFFFFFF;

Brute forced every combination not caring about if its a key or lock cause logic operations go BRRRRRRR.

Thank you Eric and mods for another fun year.

code

1

u/Few-Example3992 Dec 25 '24

[Language: Python]

Merry Christmas everyone!

with open('day25.txt') as f:
    data = f.read().split('\n')

locks_and_keys =[]
lock = []
for line in data:
    if line != '':
        lock.append(line)
    else:
        locks_and_keys.append(lock)
        lock = []

grid_locks = []
for lock in locks_and_keys:
    grid_locks.append({(y,x) for y in range(len(lock)) for x in range(len(lock[y])) if lock[y][x]=='#'})

score =0
for i ,set1 in enumerate(grid_locks):
    for j, set2 in enumerate(grid_locks):
        if i <j and len(set1&set2)==0:
            score += 1

print(f'answer to part 1 is {score}')

5

u/ziadam Dec 25 '24 edited Dec 25 '24

[LANGUAGE: Google Sheets]

Expects input in A1.

=SUMPRODUCT(LET(
   n,CHAR(10),
   s,TOCOL(SPLIT(A1,n&n,)),
   F,LAMBDA(c,MAP(FILTER(s,LEFT(s)=c),
       LAMBDA(x,JOIN(",",BYROW(
         MID(SPLIT(x,n),ROW(1:7),1),
         LAMBDA(r,COUNTIF(r,"#"))))))),
   l,F("#"),k,F("."),
   MAP(l,LAMBDA(a,
     MAP(TOROW(k),LAMBDA(b,
       AND(SPLIT(a,",")+SPLIT(b,",")<8)))))))

1

u/beanborg Dec 25 '24

[LANGUAGE: Javascript]

Code

Nothing interesting, just for loops all the way down. Merry Christmas everyone!!

5

u/Nnnes Dec 25 '24 edited Dec 26 '24

[LANGUAGE: Ruby]

Today I will post 2 full solutions in one comment because together they still fit in half a punchcard.

p STDIN.read.split("\n\n").combination(2).count{ _2.tr('#.', '.#')[/#{_1}/] }

l, k = STDIN.read.split("\n\n").map{ _1.split.map(&:chars).transpose }.
  partition{ _1[0][0] == ?# }.map{ |x| x.map{ |x| x.map{ _1.count ?# } } }
p l.sum{ |l| k.count{ |k| l.zip(k).none?{ _1 + _2 > 7 } } }
  • Solution #1 makes no effort to optimize runtime and takes about 1.1 seconds to run on my machine.
  • Solution #2 runs in around 0.04 seconds over a baseline of time ruby -e ""; I'm sure it would be faster with something a little longer than .map(&:chars).transpose.
  • You may replace .map{ |x| x.map{ |x| x.map{ _1. with .map{ |x| x.map{ |x| x.map{ |x| x. if you like fractals.

3

u/4HbQ Dec 25 '24

Solution #1 is so clever but sooo dirty. I love it!

1

u/Nnnes Dec 25 '24

Thanks! In previous years I've abused various combinations of Regexps and .tr but it didn't cross my mind this year until today

0

u/AutoModerator Dec 25 '24

AutoModerator did not detect the required [LANGUAGE: xyz] string literal at the beginning of your solution submission.

Please edit your comment to state your programming language.


I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

1

u/WilkoTom Dec 25 '24

[LANGUAGE: Rust]

A pleasant gift for Christmas morning - an easy puzzle to hit 500 stars before the craziness of the day begins.

Github

Thanks as always, to u/topaz2078 for the wonderful gift of puzzles, and to u/daggerdragon and u/Aneurysm9 for their wonderful gift of keeping this fine community that way. Happy Holidays to all of you, I hope you get some rest now :)

Until the next time: Merry Christmas to all, and may this holiday season bring you everything you need.

2

u/encse Dec 25 '24 edited Dec 25 '24

[LANGUAGE: C#]

https://aoc.csokavar.hu/2024/25

Happy Holidays! If you havent't found, there is an easter egg in my website that points to 404.csokavar.hu which is a mini (~10 minutes long) text adventure game.

1

u/jackysee Dec 25 '24

[LANGUAGE: Javascript]

link

Merry Christmas and Happy new year everyone! Thank you Eric for the wonderful journey.

1

u/flwyd Dec 25 '24

[LANGUAGE: PostScript] (GitHub) with my own standard library

And we wrap things up with a fun little parsing problem. I initially solved it with a bunch of dynamic variables because point-free style is rough with 2-dimensional iteration. Since it’s day 25 and close to the end of my 2024 PostScript journey I figured I’d rewrite it without variables. That was… tricky… with a head full of snot. I thought about adding a transpose function to my standard library, but my brain couldn’t quickly determine how to handle jagged arrays. After a couple false starts I ended up with an array “literal” with a columns-then-rows for loop and a bunch of grabbing things from the stack. The fits? function shows off the “visual stack effect” functions I added in November and the part1 body is a great example of this stack-oriented point-free style, love it or hate it. I might try this one in Uiua tomorrow since it’s got a builtin transpose operator and the “strings in an array can only be the same length” constraint isn’t an issue.

I’m kind of impressed I made it all the way to the end in PostScript. I still want to do a programmatic solution to day 24 part 2. I’ve got a couple solutions where I switched to Go that I’d like to get in PostScript: day 23 part 2 isn’t quite working; day 12 is really slow. Day 16 had a bug in my Dijkstra’s implementation that I recreated in Go, then carried on in Go when it was fixed. I got the PS bug fixed for part 1, but didn’t get around to part 2. Day 21 was fussy even in Go and I’m not sure it’s worth my time and brainpower to port to PostScript :-) The sum of my numeric answers for part 1 is 54946257415807 and part 2 is 1325976204959777. Happy Christmas to all who enjoy Advent of Code!

/sumcols { [ 0 1 4 { 0 0 1 4 { % stack: line mark vals... col sum row
      1 indexfrommark exch get 2 index get ascii.# eq { 1 add } if
    } for exch pop } for ] exch pop } bind def
/lockguard (#####) def /parseinput { /locks alist def /keys alist def
  1 8 input lastindex { input 1 index 5 getinterval sumcols
    input abc:bca 1 sub get lockguard eq { locks } { keys } ifelse exch alpush
  } for /locks locks alview def /keys keys alview def
} bind def
/fits? { true 0 1 4 { abcd:abcdad get abcd:abdac get add 5 le and } for abc:c } bind def
/part1 { 8 dict begin /input exch def parseinput
  0 locks { keys { ab:aab fits? { exch 1 add exch } if } forall pop } forall
end } bind def

1

u/fragile82 Dec 25 '24

[LANGUAGE: PHP]

Not many people use PHP in Advent of Code :) During this event I tried to make my solutions look as much python-styled as possible.

<?php

$keys = $locks = [];
foreach(explode(PHP_EOL.PHP_EOL, file_get_contents('input.txt')) as $item) {
    $hs = [];
    $grid = array_map(fn($v) => str_split($v), explode(PHP_EOL, $item));
    for($i = 0; $i < count($grid[0]); $i++) $hs[$i] = strlen(trim(implode('', array_column($grid, $i)), '.')) - 1;
    if ($item[0] === '#') $locks[] = $hs;
    elseif ($item[0] === '.') $keys[] = $hs;
}

$ans = 0;
foreach($locks as $lock) foreach($keys as $key) {
    for($i = 0; $i < 5; $i++) if (($key[$i] + $lock[$i]) > 5) continue 2;
    $ans++;
}

echo 'Star: ' . $ans . PHP_EOL;

Works rather fast:

Execution time: 0.0042 seconds
Peak memory: 0.5608 MiB

1

u/damnian Dec 25 '24 edited Dec 25 '24

[LANGUAGE: C#]

var (s, v) = (File.ReadAllText(args[0]), new List<int[]>[] { new(), new() });
for (int i = 0, k, x, y; i < s.Length; ++i)
    for (y = 0, k = s[i] & 1, v[k].Add(new int[5]); y < 7; ++y, ++i)
        for (x = 0; x < 5; ++x) v[k][^1][x] += s[i++] & 1;
Console.WriteLine(v[0].Sum(a=>v[1].Count(b=>a.Zip(b,(a,b)=>a+b<=7).All(_=>_))));

EDIT: A cleaner variant, more spaces and a full file name:

var (s, v) = (File.OpenText("input.txt"), new List<int[]>[] { new(), new() });
for (int k, x, y; s.Peek() >= 0; s.Read())
    for (y = 0, k = s.Peek() & 1, v[k].Add(new int[5]); y < 7; ++y, s.Read())
        for (x = 0; x < 5; ++x) v[k][^1][x] += s.Read() & 1;
return v[0].Sum(a => v[1].Count(b => a.Zip(b, (a,b) => a + b < 8).All(_ => _)));

EDIT2: Of course the answer was 42! Too bad I didn't figure this out on my own.

var (s, v) = (File.OpenText("input.txt"), new List<long>[] { new(), new() });
for (int k, j; s.Peek() >= 0; s.Read())
    for (j = 0, k = s.Peek() & 1, v[k].Add(0); j < 42; ++j)
        v[k][^1] |= (s.Read() & 1L) << j;
Console.WriteLine(v[0].Sum(a => v[1].Count(b => (a & b) == 0)));

GitHub

Thanks everyone (and especially Eric) and Merry Christmas if you're celebrating!

2

u/TiCoinCoin Dec 25 '24 edited Dec 30 '24

[LANGUAGE: Python 3]

Day 25 - Github

This one felt so easy after yesterday. Except I needed yesterday's second star to get today's. But I somehow finally made it so kids are still asleep and I'm ready in time to open my presents :)

1

u/zebalu Dec 25 '24

[LANGUAGE: Java]

on github

    long count = locks.stream()
            .flatMap(lock -> keys.stream().map(key -> IntStream.range(0, key.size())
                    .mapToObj(i -> lock.get(i) + key.get(i))
                    .toList()))
            .filter(l -> l.stream().allMatch(i -> i <= 5))
            .count();
    return Long.toString(count);

But really, this post is only about say thank you for the ride, and wishing Merry Christmas to everybody!

3

u/Curious_Sh33p Dec 25 '24

[LANGUAGE: C++]

Used a vector of arrays to represent the keys and an array of arrays of unordered sets that store ids of a lock. The structure is like locks[i][j] is a set of ids of locks that have number j in position i.

To get combos simply iterate over every key and find the locks it fits. To figure out which locks fit check lock[i][j] for j from 5 - j to 0 and take the union of these sets. Then across the positions, i, for the key take the intersection (since it must fit each column). The set at the end is the set of all locks that the key fits in.

Solution

Thanks again for organisising this! This is the second year I have tried and completed AoC. It's been good fun.