this post was submitted on 14 Nov 2025
3 points (71.4% liked)

Advent Of Code

1122 readers
4 users here now

An unofficial home for the advent of code community on programming.dev! Other challenges are also welcome!

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.

Everybody Codes is another collection of programming puzzles with seasonal events.

EC 2025

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 2 years ago
MODERATORS
 

Quest 9: Encoded in the Scales

  • 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

Link to participate: https://everybody.codes/

top 6 comments
sorted by: hot top controversial new old
[โ€“] vole@lemmy.world 2 points 1 day ago

Scheme/Guile

I was stuck on part 3 for a while, for not taking account that a child scale that I'm evaluating may already be a parent scale in some group.

(import (rnrs io ports (6))
        (srfi srfi-1))
#!curly-infix

(define (parse-file file-name)
  (let* ((lines (string-split (string-trim-both (call-with-input-file file-name get-string-all)) #\newline))
         (split-lines (map (lambda (l) (string-split l #\:)) lines))
         (parsed-lines (map (lambda (l) (cons (string->number (car l)) (string->list (cadr l)))) split-lines)))
    parsed-lines))

(define (child-score child p1 p2 p1-sim p2-sim)
  (if (and-map null? (list child p1 p2))
      (* p1-sim p2-sim)
      (let ((matches-p1 (eq? (car child) (car p1)))
            (matches-p2 (eq? (car child) (car p2))))
        (cond
          ((not (or matches-p1 matches-p2)) #f)
          (else (child-score (cdr child) (cdr p1) (cdr p2) (+ p1-sim (if matches-p1 1 0)) (+ p2-sim (if matches-p2 1 0))))))))
(let ((dna-lines (parse-file "notes/everybody_codes_e2025_q09_p1.txt")))
  (format #t "P1 Answer: ~a\n\n" (or
    (child-score (cdar dna-lines) (cdadr dna-lines) (cdaddr dna-lines) 0 0)
    (child-score (cdadr dna-lines) (cdar dna-lines) (cdaddr dna-lines) 0 0)
    (child-score (cdaddr dna-lines) (cdadr dna-lines) (cdar dna-lines) 0 0))))


(let ((dna-lines (list->vector (parse-file "notes/everybody_codes_e2025_q09_p2.txt"))))
  (let loop ((child 0) (total-sim 0))
    (if {child < (vector-length dna-lines)}
        (loop (1+ child) (+ total-sim (let loop ((i 0))
          (cond
            ((eq? i child) (loop (1+ i)))
            ({i >= {(vector-length dna-lines) - 1}} 0)
            (else
              (or
                (let loop ((j (1+ i)))
                  (cond
                    ((eq? j child) (loop (1+ j)))
                    ({j >= (vector-length dna-lines)} #f)
                    (else (let ((res (child-score
                               (cdr (vector-ref dna-lines child))
                               (cdr (vector-ref dna-lines i))
                               (cdr (vector-ref dna-lines j)) 0 0)))
                      (or res (loop (1+ j)))))))
                (loop (1+ i))))))))
        (format #t "P2 Answer: ~a\n\n" total-sim))))


(define (init-id-to-group dna-lines)
  (let ((table (make-hash-table)))
    (let loop ((i 0))
      (if {i < (vector-length dna-lines)}
          (let ((id (car (vector-ref dna-lines i))))
            (hash-set! table id id)
            (loop (1+ i)))
          table))))
(define (init-group-to-ids dna-lines)
  (let ((table (make-hash-table)))
    (let loop ((i 0))
      (if {i < (vector-length dna-lines)}
          (let ((id (car (vector-ref dna-lines i))))
            (hash-set! table id (list id))
            (loop (1+ i)))
          table))))
(let ((dna-lines (list->vector (parse-file "notes/everybody_codes_e2025_q09_p3.txt"))))
  (let ((id-to-group (init-id-to-group dna-lines)) (group-to-ids (init-group-to-ids dna-lines)))
  (let child-loop ((child 0))
    (if {child < (vector-length dna-lines)}
      (let i-loop ((i 0))
        (cond
          ((eq? i child) (i-loop (1+ i)))
          ({i >= {(vector-length dna-lines) - 1}} (child-loop (1+ child)))
          (else
            (let j-loop ((j (1+ i)))
              (cond
                ((eq? j child) (j-loop (1+ j)))
                ({j >= (vector-length dna-lines)} (i-loop (1+ i)))
                (else (let* ((cl (vector-ref dna-lines child))
                             (pil (vector-ref dna-lines i))
                             (pjl (vector-ref dna-lines j))
                             (res (child-score (cdr cl) (cdr pil) (cdr pjl) 0 0)))
                  (if res
                      (let* ((i-group (hash-ref id-to-group (car pil)))
                             (j-group (hash-ref id-to-group (car pjl)))
                             (child-group (hash-ref id-to-group (car cl)))
                             (i-group-ids (hash-ref group-to-ids i-group))
                             (j-group-ids (hash-ref group-to-ids j-group))
                             (child-group-ids (hash-ref group-to-ids child-group))
                             (new-group-ids (delete-duplicates (append child-group-ids (or i-group-ids '()) (or j-group-ids '())))))
                        (map (lambda (id) (hash-set! id-to-group id child-group)) new-group-ids)
                        (hash-remove! group-to-ids i-group)
                        (hash-remove! group-to-ids j-group)
                        (hash-set! group-to-ids child-group new-group-ids)
                        (child-loop (1+ child)))
                      (j-loop (1+ j))))))))))
        (format #t "P3 Answer: ~a\n\n" (cdr (hash-fold
                (lambda (_ id-list prior)
                        (let ((group-size (length id-list))
                              (group-sum (apply + id-list)))
                          (if {group-size > (car prior)}
                              (cons group-size group-sum)
                              prior)))
                (cons 0 0)
                group-to-ids)))))))
[โ€“] hades@programming.dev 1 points 2 days ago* (last edited 2 days ago)

Rust

use std::collections::HashMap;

use bit_set::BitSet;
use itertools::{Itertools, izip};

type BitSetBase = u32;

fn is_child(child: &str, parent1: &str, parent2: &str) -> bool {
    izip!(child.chars(), parent1.chars(), parent2.chars()).all(|(c, a, b)| c == a || c == b)
}

fn similarity(a: &str, b: &str) -> usize {
    izip!(a.chars(), b.chars()).filter(|(a, b)| a == b).count()
}

fn unequality_bitset(a: &str, b: &str) -> BitSet<BitSetBase> {
    izip!(a.chars(), b.chars())
        .enumerate()
        .filter_map(|(i, (a_ch, b_ch))| if a_ch == b_ch { None } else { Some(i) })
        .collect()
}

pub fn solve_part_1(input: &str) -> String {
    let dnas = input
        .lines()
        .map(|l| {
            let (_, dna) = l.split_once(":").unwrap();
            dna
        })
        .collect::<Vec<_>>();
    let (child, parenta, parentb) = if is_child(dnas[0], dnas[1], dnas[2]) {
        (dnas[0], dnas[1], dnas[2])
    } else if is_child(dnas[1], dnas[0], dnas[2]) {
        (dnas[1], dnas[0], dnas[2])
    } else {
        (dnas[2], dnas[0], dnas[1])
    };
    (similarity(child, parenta) * similarity(child, parentb)).to_string()
}

pub fn solve_part_2(input: &str) -> String {
    let dnas = input
        .lines()
        .map(|l| {
            let (_, dna) = l.split_once(":").unwrap();
            dna
        })
        .collect::<Vec<_>>();
    let unequalities = dnas
        .iter()
        .enumerate()
        .cartesian_product(dnas.iter().enumerate())
        .map(|((a_id, a), (b_id, b))| ((a_id, b_id), unequality_bitset(a, b)))
        .collect::<HashMap<_, _>>();
    let mut total_similarities = 0;
    'outer: for (child_id, &child_string) in dnas.iter().enumerate() {
        for (parent_a_id, &parent_a_string) in dnas.iter().enumerate() {
            if parent_a_id == child_id {
                continue;
            }
            for (parent_b_id, &parent_b_string) in dnas.iter().enumerate() {
                if parent_b_id == child_id || parent_b_id == parent_a_id {
                    continue;
                }
                if unequalities[&(child_id, parent_a_id)]
                    .intersection(&unequalities[&(child_id, parent_b_id)])
                    .count()
                    == 0
                {
                    total_similarities += similarity(child_string, parent_a_string)
                        * similarity(child_string, parent_b_string);
                    continue 'outer;
                }
            }
        }
    }
    total_similarities.to_string()
}

pub fn solve_part_3(input: &str) -> String {
    let dnas = input
        .lines()
        .map(|l| {
            let (_, dna) = l.split_once(":").unwrap();
            dna
        })
        .collect::<Vec<_>>();
    let unequalities = dnas
        .iter()
        .enumerate()
        .cartesian_product(dnas.iter().enumerate())
        .map(|((a_id, a), (b_id, b))| ((a_id, b_id), unequality_bitset(a, b)))
        .collect::<HashMap<_, _>>();
    let mut parents: HashMap<usize, (usize, usize)> = HashMap::new();
    'outer: for (child_id, _) in dnas.iter().enumerate() {
        for (parent_a_id, _) in dnas.iter().enumerate() {
            if parent_a_id == child_id {
                continue;
            }
            for (parent_b_id, _) in dnas.iter().enumerate() {
                if parent_b_id == child_id || parent_b_id == parent_a_id {
                    continue;
                }
                if unequalities[&(child_id, parent_a_id)]
                    .intersection(&unequalities[&(child_id, parent_b_id)])
                    .count()
                    == 0
                {
                    parents.insert(child_id, (parent_a_id, parent_b_id));
                    continue 'outer;
                }
            }
        }
    }
    let mut family_id = (0usize..dnas.len()).collect::<Vec<_>>();
    for (child, (parent_a, parent_b)) in parents.drain() {
        let destination_family_id = family_id[child];
        let merge_families = (family_id[parent_a], family_id[parent_b]);
        for candidate_family in family_id.iter_mut() {
            if *candidate_family == merge_families.0 || *candidate_family == merge_families.1 {
                *candidate_family = destination_family_id;
            }
        }
    }
    let families = family_id
        .iter()
        .enumerate()
        .map(|(member_idx, &family_idx)| (family_idx, member_idx))
        .into_group_map();
    families
        .values()
        .max_by_key(|f| f.len())
        .unwrap()
        .iter()
        .map(|member_id| member_id + 1)
        .sum::<usize>()
        .to_string()
}
[โ€“] ystael@beehaw.org 2 points 5 days ago (1 children)

I'm sure there are 17 different graph libraries I could have used for the graph representation and connected components, but it seemed to be in the spirit of the question to write it myself. Nothing interesting about the parent search though -- it's just brute-force comparison.

(ql:quickload :str)

(defun parse-line (line)
  (let ((index-and-codes (str:split ":" line)))
    (cons (parse-integer (car index-and-codes)) (cadr index-and-codes))))

(defun read-inputs (filename)
  (let ((input-lines (uiop:read-file-lines filename)))
    (mapcar #'parse-line input-lines)))

(defun can-be-child-of? (parent1 parent2 child)
  (loop for i from 0 to (1- (length child))
        unless (or (eql (char child i) (char parent1 i))
                   (eql (char child i) (char parent2 i)))
          return nil
        finally (return t)))

(defun similarity (genome1 genome2)
  (loop for i from 0 to (1- (length genome1))
        sum (if (eql (char genome1 i) (char genome2 i)) 1 0)))

(defun main-1 (filename)
  (let ((genomes (read-inputs filename)))
    (loop for arrangement in '((1 2 3) (2 3 1) (3 1 2))
          maximize
          (destructuring-bind (parent1-index parent2-index child-index) arrangement
            (let ((parent1 (cdr (assoc parent1-index genomes)))
                  (parent2 (cdr (assoc parent2-index genomes)))
                  (child (cdr (assoc child-index genomes))))
              (if (can-be-child-of? parent1 parent2 child)
                  (* (similarity parent1 child) (similarity parent2 child))
                  0))))))

(defun find-parents (genomes child-pair)
  (loop named loop1
        for tail1 on genomes
        for parent1-pair = (car tail1)
        do (loop for parent2-pair in (cdr tail1)
                 when (and
                       (/= (car parent1-pair) (car child-pair))
                       (/= (car parent2-pair) (car child-pair))
                       (can-be-child-of? (cdr parent1-pair) (cdr parent2-pair) (cdr child-pair)))
                   do (return-from loop1 (cons (car parent1-pair) (car parent2-pair))))
        finally (return-from loop1 nil)))

(defun child-relationships (genomes)
  (mapcar #'(lambda (child-pair)
              (cons (car child-pair) (find-parents genomes child-pair)))
          genomes))

(defun main-2 (filename)
  (let* ((genomes (read-inputs filename))
         (child-relationships (child-relationships genomes)))
    (loop for child-rel in child-relationships
          sum (destructuring-bind (child-idx . parent-idxs) child-rel
                (if (null parent-idxs)
                    0
                    (let ((parent1 (cdr (assoc (car parent-idxs) genomes)))
                          (parent2 (cdr (assoc (cdr parent-idxs) genomes)))
                          (child (cdr (assoc child-idx genomes))))
                      (* (similarity parent1 child) (similarity parent2 child))))))))

(defun relationship-graph (child-relationships)
  (let ((edges (mapcan #'(lambda (child-rel)
                           (destructuring-bind (child-idx . parent-idxs) child-rel
                             (if (null parent-idxs)
                                 nil
                                 (list (cons child-idx (car parent-idxs))
                                       (cons child-idx (cdr parent-idxs))))))
                       child-relationships))
        (graph (make-hash-table)))
    (loop for edge in edges
          do (destructuring-bind (x . y) edge
               (setf (gethash x graph) (cons y (gethash x graph)))
               (setf (gethash y graph) (cons x (gethash y graph)))))
    graph))

(defun component-of (graph vertex)
  (labels ((iter (so-far)
             (let ((next (reduce #'union
                                 (mapcar #'(lambda (v) (gethash v graph)) so-far)
                                 :initial-value so-far)))
               (if (subsetp next so-far)
                   next
                   (iter next)))))
    (iter (list vertex))))

(defun all-components (graph vertices)
  (labels ((iter (so-far vertices-left)
             (if (null vertices-left)
                 so-far
                 (let ((comp (component-of graph (car vertices-left))))
                   (iter (cons comp so-far)
                         (set-difference vertices-left comp))))))
    (iter nil vertices)))

(defun main-3 (filename)
  (let* ((genomes (read-inputs filename))
         (child-relationships (child-relationships genomes))
         (relationship-graph (relationship-graph child-relationships))
         (keys (mapcar #'car child-relationships))
         (components (all-components relationship-graph keys)))
    (reduce #'+
            (car (sort components #'(lambda (c1 c2) (> (length c1) (length c2))))))))
[โ€“] hades@programming.dev 1 points 2 days ago

I don't think there's such a thing as a "spirit of the question", but you're free to set your own challenges of course :)

[โ€“] lwhjp@piefed.blahaj.zone 3 points 6 days ago

Haskell

Not particularly optimized but good enough.

import Control.Arrow ((***))  
import Data.Array (assocs)  
import Data.Function (on)  
import Data.Graph  
import Data.List  
import Data.Map (Map)  
import Data.Map qualified as Map  
import Data.Maybe  

readInput :: String -> Map Int [Char]  
readInput = Map.fromList . map ((read *** tail) . break (== ':')) . lines  

findRelations :: Map Int [Char] -> Graph  
findRelations dna =  
  buildG (1, Map.size dna)  
    . concatMap (\(x, (y, z)) -> [(x, y), (x, z)])  
    . mapMaybe (\x -> (x,) <$> findParents x)  
    $ Map.keys dna  
  where  
    findParents x =  
      find (isChild x) $  
        [(y, z) | (y : zs) <- tails $ delete x $ Map.keys dna, z <- zs]  
    isChild x (y, z) =  
      all (\(a, b, c) -> a == b || a == c) $  
        zip3 (dna Map.! x) (dna Map.! y) (dna Map.! z)  

scores :: Map Int [Char] -> Graph -> [Int]  
scores dna relations =  
  [similarity x y * similarity x z | (x, [y, z]) <- assocs relations]  
  where  
    similarity i j =  
      length . filter (uncurry (==)) $ zip (dna Map.! i) (dna Map.! j)  

part1, part2, part3 :: Map Int [Char] -> Int  
part1 = sum . (scores <*> findRelations)  
part2 = part1  
part3 = sum . maximumBy (compare `on` length) . components . findRelations  

main = do  
  readFile "everybody_codes_e2025_q09_p1.txt" >>= print . part1 . readInput  
  readFile "everybody_codes_e2025_q09_p2.txt" >>= print . part2 . readInput  
  readFile "everybody_codes_e2025_q09_p3.txt" >>= print . part3 . readInput  
[โ€“] janAkali@lemmy.sdf.org 2 points 6 days ago* (last edited 6 days ago)

Nim

Very messy bruteforce.

I've had some problems with parsing in part 2 - I didn't account for double digit numbers before dna sequences and that caused my code to work on example, but silently fail only on the real input. I've figured it out after ~30 minutes with some external help.

Part 3 runs in 700ms - not great, but not too bad either.

proc similarity(a, b: string): int =
  for i, c in a:
    if c == b[i]: inc result

proc solve_part1*(input: string): Solution =
  var sim: seq[int]

  var dnaList: seq[string]
  for line in input.splitLines():
    dnaList.add line[2..^1]

  for i in 0 .. dnaList.high:
    for j in i+1 .. dnaList.high:
      let s = similarity(dnaList[i], dnaList[j])
      sim.add s

  sim.sort()
  result := sim[^2] * sim[^1]

proc parentTest(ch, p1, p2: string): bool =
  for i, c in ch:
    if (c != p1[i]) and (c != p2[i]): return false
  true

proc simTable(dnaList: seq[string]): seq[seq[int]] =
  result = newSeqWith(dnaList.len, newseq[int](dnaList.len))
  for i in 0 .. dnaList.high:
    for j in i+1 .. dnaList.high:
      let s = similarity(dnaList[i], dnaList[j])
      result[i][j] = s
      result[j][i] = s

proc solve_part2*(input: string): Solution =
  var dnaList: seq[string]
  for line in input.splitLines():
    dnaList.add line.split(':')[1]

  let sim = simTable(dnaList)
  var indices = toseq(0..dnaList.high)
  for i, childDna in dnaList:
    var indices = indices
    indices.del i

    block doTest:
      for k in 0 .. indices.high:
        for j in k+1 .. indices.high:
          let p1 = indices[k]
          let p2 = indices[j]
          if parentTest(childDna, dnaList[p1], dnaList[p2]):
            result.intVal += sim[i][p1] * sim[i][p2]
            break doTest

proc solve_part3*(input: string): Solution =
  var dnaList: seq[string]
  for line in input.splitLines():
    dnaList.add line.split(':')[1]

  var families: seq[set[int16]]
  var indices = toseq(0..dnaList.high)
  for ch, childDna in dnaList:
    var indices = indices
    indices.del ch

    block doTest:
      for k in 0 .. indices.high:
        for j in k+1 .. indices.high:
          let p1 = indices[k]
          let p2 = indices[j]
          if parentTest(childDna, dnaList[p1], dnaList[p2]):
            families.add {ch.int16, p1.int16, p2.int16}
            break doTest

  var combined: seq[set[int16]]
  while families.len > 0:
    combined.add families.pop()
    var i = 0
    while i <= families.high:
      if (combined[^1] * families[i]).len > 0:
        combined[^1] = combined[^1] + families[i]
        families.del i
        i = 0
      else: inc i

  let maxInd = combined.mapIt(it.len).maxIndex
  result := combined[maxInd].toseq.mapIt(it.int+1).sum()

Full solution at Codeberg: solution.nim