this post was submitted on 09 Nov 2025
9 points (84.6% liked)

Advent Of Code

1122 readers
2 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 5: Fishbone Order

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

Uiua

[Works on test data, but fails on Part 3 live data: my checksum is "Correct length and correct first character". Posting for the record while I think about it more.] Turned out to be trailing zeros.

F ← (
  β†˜1βŠ™[0_0_0] # Create empty row
  ∧(
    ◑≑([βŠƒ(β†§βŠƒ(=0βŠ’β—Œ|>βŠ™βŠ‘β‚)|=0βŠ‘β‚β—Œ|β†§βŠƒ(=0βŠ£β—Œ|<βŠ™βŠ‘β‚))]) # Will it fit in each row?
    βŠŸβŠ™(⊒⊚)βŸœβŠ‘βŠ’βŠšβŠΈβ‰‘/β†₯                             # Find first non-zero row and col.
    (βœβŠ‘β‹…βˆ˜)βŠ™βŠƒβ‹…βˆ˜βˆ˜                                # insert
    β₯(ΛœβŠ‚0_0_0)¬≍0_0_0⊸⊣                        # Add padding row back
  )
  β†˜Β―1
)
S     ← β‹•/$"__"⊑1⍉
Part₁ ← S F
Partβ‚‚ ← -βŠƒ/↧/β†₯≑(S F)

Part₃ ← (
  ⬚0βŠΈβ‰‘(βŠ‚βŠƒ(S|≑(Β°βŠ₯β‚β‚€β–½βŠΈ>β‚€β‡Œ))F) # All sword stats
  β‰‘βŠ‚βŠ™(βŠ’β‰)             # Append sword IDs
  βŠβŠΈβ–                 # Sort
  /+Γ—+1Β°βŠβ‰‘βŠ£           # Checksum
)

Part₁ [58 5 3 7 8 9 10 4 5 7 8 8]
Partβ‚‚ [[2 7 9 9 3 8 3 8 8 6 8] [5 2 9 3 8 3 9 5 2 1 4]]
Part₃ [[1 7 1 9 1 6 9 8 3 7 2]
       [2 7 1 9 1 6 9 8 3 7 2]]
[–] hades@programming.dev 3 points 1 week ago

Rust

use itertools::Itertools;

type Fishbone = Vec<(i64, Option<i64>, Option<i64>)>;

fn parse_fishbone(quality_str: &str) -> Fishbone {
    let mut fishbone: Fishbone = vec![];
    'outer: for num in quality_str.split(",").map(|x| x.parse().unwrap()) {
        for e in fishbone.iter_mut() {
            if num < e.0 && e.1.is_none() {
                e.1 = Some(num);
                continue 'outer;
            }
            if num > e.0 && e.2.is_none() {
                e.2 = Some(num);
                continue 'outer;
            }
        }
        fishbone.push((num, None, None));
    }
    fishbone
}

fn compute_quality(fishbone: &Fishbone) -> i64 {
    fishbone
        .iter()
        .map(|(c, _, _)| c.to_string())
        .join("")
        .parse()
        .unwrap()
}

pub fn solve_part_1(input: &str) -> String {
    let (_, data) = input.split_once(":").unwrap();
    compute_quality(&parse_fishbone(data)).to_string()
}

pub fn solve_part_2(input: &str) -> String {
    let mut worst_quality = i64::MAX;
    let mut best_quality = i64::MIN;
    for sword in input.lines() {
        let (_, data) = sword.split_once(":").unwrap();
        let quality = compute_quality(&parse_fishbone(data));
        worst_quality = worst_quality.min(quality);
        best_quality = best_quality.max(quality);
    }
    (best_quality - worst_quality).to_string()
}

pub fn solve_part_3(input: &str) -> String {
    let mut swords: Vec<_> = input
        .lines()
        .map(|def| {
            let (id, data) = def.split_once(":").unwrap();
            let fishbone = parse_fishbone(data);
            (id.parse::<i64>().unwrap(), fishbone)
        })
        .collect();
    swords.sort_by(|a, b| {
        let cmp = compute_quality(&a.1).cmp(&compute_quality(&b.1));
        if !matches!(cmp, std::cmp::Ordering::Equal) {
            return cmp;
        }
        for (a_seg, b_seg) in a.1.iter().zip(b.1.iter()) {
            let a_val = match a_seg {
                (a, Some(b), Some(c)) => format!("{b}{a}{c}"),
                (a, Some(b), None) => format!("{b}{a}"),
                (a, None, Some(c)) => format!("{a}{c}"),
                (a, None, None) => format!("{a}"),
            };
            let b_val = match b_seg {
                (a, Some(b), Some(c)) => format!("{b}{a}{c}"),
                (a, Some(b), None) => format!("{b}{a}"),
                (a, None, Some(c)) => format!("{a}{c}"),
                (a, None, None) => format!("{a}"),
            };
            let cmp = a_val.parse::<i64>().unwrap().cmp(&b_val.parse().unwrap());
            if !matches!(cmp, std::cmp::Ordering::Equal) {
                return cmp;
            }
        }
        a.0.cmp(&b.0)
    });
    swords.reverse();
    swords
        .into_iter()
        .enumerate()
        .map(|(pos, (id, _))| id * (pos as i64 + 1))
        .sum::<i64>()
        .to_string()
}
[–] janAkali@lemmy.sdf.org 3 points 1 week ago* (last edited 1 week ago)

Nim

Nothing fancy. Simple iterative tree construction and sort, using the std/algorithm and a custom < operator on types.

type
  LeafNode = ref object
    value: int
  Node = ref object
    value: int
    left, right: LeafNode
    center: Node
  Sword = object
    id, quality: int
    levels: seq[int]
    fishbone: Node

proc add(node: var Node, val: int) =
  var curNode = node
  while not curNode.isNil:
    if val < curNode.value and curNode.left.isNil:
      curNode.left = LeafNode(value: val)
      return
    elif val > curNode.value and curNode.right.isNil:
      curNode.right = LeafNode(value: val)
      return
    elif curNode.center.isNil:
      curNode.center = Node(value: val)
      return
    else: curNode = curNode.center
  node = Node(value: val)

proc calcQuality(sword: Sword): int =
  var res = ""
  var curNode = sword.fishbone
  while not curNode.isNil:
    res &= $curNode.value
    curNode = curNode.center
  return parseInt(res)

proc getLevels(s: Sword): seq[int] =
  var curNode = s.fishbone
  while not curNode.isNil:
    var strVal = ""
    strVal &= (if curNode.left.isNil:  "" else: $curNode.left.value)
    strVal &= $curNode.value
    strVal &= (if curNode.right.isNil: "" else: $curNode.right.value)
    result.add parseInt(strVal)
    curNode = curNode.center

proc `<`(s1, s2: seq[int]): bool =
  for i in 0..min(s1.high, s2.high):
    if s1[i] != s2[i]: return s1[i] < s2[i]
  s1.len < s2.len

proc `<`(s1, s2: Sword): bool =
  if s1.quality != s2.quality: s1.quality < s2.quality
  elif s1.levels != s2.levels: s1.levels < s2.levels
  else: s1.id < s2.id

proc parseSwords(input: string): seq[Sword] =
  for line in input.splitLines:
    let numbers = line.split({':',','}).mapIt(parseInt(it))
    var node= Node(value: numbers[1])
    for num in numbers.toOpenArray(2, numbers.high):
      node.add num
    result &= Sword(id: numbers[0], fishbone: node)

proc solve_part1*(input: string): Solution =
  let swords = parseSwords(input)
  result := swords[0].calcQuality()

proc solve_part2*(input: string): Solution =
  let qualities = parseSwords(input).mapIt(it.calcQuality())
  result := qualities.max - qualities.min

proc solve_part3*(input: string): Solution =
  var swords = parseSwords(input)
  for i in 0..swords.high:
    swords[i].levels = swords[i].getLevels()
    swords[i].quality = swords[i].calcQuality()
  swords.sort(Descending)
  for pos, id in swords.mapit(it.id):
    result.intVal += (pos+1) * id

Full solution at Codeberg: solution.nim

[–] lwhjp@piefed.blahaj.zone 3 points 1 week ago

I forgot that "weekdays" for a US website means something different for me here in UTC+9.

This was surprisingly fiddly, but I think I managed to do it reasonably neatly.

import Control.Arrow  
import Data.Foldable  
import Data.List (sortBy)  
import Data.List.Split  
import Data.Maybe  
import Data.Ord  

data Fishbone  
  = Fishbone (Maybe Int) Int (Maybe Int) Fishbone  
  | Empty  
  deriving (Eq)  

instance Ord Fishbone where  
  compare = comparing numbers  

readInput :: String -> [(Int, Fishbone)]  
readInput = map readSword . lines  
  where  
    readSword = (read *** build) . break (== ':')  
    build = foldl' insert Empty . map read . splitOn "," . tail  

insert bone x =  
  case bone of  
    (Fishbone l c r next)  
      | isNothing l && x < c -> Fishbone (Just x) c r next  
      | isNothing r && x > c -> Fishbone l c (Just x) next  
      | otherwise -> Fishbone l c r $ insert next x  
    Empty -> Fishbone Nothing x Nothing Empty  

spine (Fishbone _ c _ next) = c : spine next  
spine Empty = []  

numbers :: Fishbone -> [Int]  
numbers (Fishbone l c r next) =  
  (read $ concatMap show $ catMaybes [l, Just c, r])  
    : numbers next  
numbers Empty = []  

quality :: Fishbone -> Int  
quality = read . concatMap show . spine  

part1, part2, part3 :: [(Int, Fishbone)] -> Int  
part1 = quality . snd . head  
part2 = uncurry (-) . (maximum &&& minimum) . map (quality . snd)  
part3 = sum . zipWith (*) [1 ..] . map fst . sortBy (flip compareSwords)  
  where  
    compareSwords =  
      comparing (quality . snd)  
        <> comparing snd  
        <> comparing fst  

main =  
  forM_  
    [ ("everybody_codes_e2025_q05_p1.txt", part1),  
      ("everybody_codes_e2025_q05_p2.txt", part2),  
      ("everybody_codes_e2025_q05_p3.txt", part3)  
    ]  
    $ \(input, solve) -> readFile input >>= print . solve . readInput  
[–] vole@lemmy.world 2 points 1 week ago* (last edited 1 week ago)

What a fiddlybit. I needed records to save me from list-hell on this one.

Scheme/Guile

(import (rnrs io ports (6))
        (rnrs records syntactic))

(define (parse-file file-name)
  (let* ((lines (string-split (string-trim-both (call-with-input-file file-name get-string-all)) #\newline)))
    (map (lambda (line)
       (let* ((colon-split (string-split line #\:))
             (segments (map string->number (string-split (cadr colon-split) #\,)))
             (label (string->number (car colon-split))))
        (cons label segments)))
       lines)))

(define-record-type fishbone-segment (fields middle left right))
(define (construct-fishbone fishbone segments)
  (if (null? segments)
      fishbone
      (let ((fishbone (add-fishbone-segment fishbone (car segments))))
        (construct-fishbone fishbone (cdr segments)))))
(define (add-fishbone-segment fishbone segment)
  (if (null? fishbone)
      (list (make-fishbone-segment segment #f #f))
      (let* ((fish-head (car fishbone))
             (fish-middle (fishbone-segment-middle fish-head))
             (fish-left (fishbone-segment-left fish-head))
             (fish-right (fishbone-segment-right fish-head)))
        (cond
          ((and (< segment fish-middle) (not fish-left)) (cons (make-fishbone-segment fish-middle segment fish-right) (cdr fishbone)))
          ((and (> segment fish-middle) (not fish-right)) (cons (make-fishbone-segment fish-middle fish-left segment) (cdr fishbone)))
          (else (cons fish-head (add-fishbone-segment (cdr fishbone) segment)))))))
(define (score-fishbone fishbone)
  (string->number (string-join (map (compose number->string fishbone-segment-middle) fishbone) "")))

(define-record-type sword (fields id fishbone quality))
(define (parse-swords file-name)
  (map (lambda (sword-line)
          (let ((fishbone (construct-fishbone '() (cdr sword-line))))
            (make-sword (car sword-line) fishbone (score-fishbone fishbone))))
    (parse-file file-name)))

(format #t "P1 Answer: ~a\n\n" (sword-quality (car (parse-swords "notes/everybody_codes_e2025_q05_p1.txt"))))

(let* ((swords (parse-swords "notes/everybody_codes_e2025_q05_p2.txt"))
       (sword-scores (map sword-quality swords)))
  (format #t "P2 Answer: ~a\n\n" (- (apply max sword-scores) (apply min sword-scores))))


(define (segment-score segment)
  (string->number
    (string-join
     (map (lambda (n) (if (eqv? #f n) "" (number->string n)))
      (list (fishbone-segment-left segment) (fishbone-segment-middle segment) (fishbone-segment-right segment)))
     "")))
(define (sort-fishbones a b)
  (if (null? a) '()
  (let ((line-score-a (segment-score (car a)))
        (line-score-b (segment-score (car b))))
    (cond
      ((> line-score-a line-score-b) #t)
      ((< line-score-a line-score-b) #f)
      (else (sort-fishbones (cdr a) (cdr b)))))))
(define (sort-swords a b)
  (cond
    ((> (sword-quality a) (sword-quality b)) #t)
    ((< (sword-quality a) (sword-quality b)) #f)
    (else (let ((fb-sort (sort-fishbones (sword-fishbone a) (sword-fishbone b))))
      (cond
        ((null? fb-sort) (> (sword-id a) (sword-id b)))
        (else fb-sort))))))
(let* ((swords (parse-swords "notes/everybody_codes_e2025_q05_p3.txt"))
       (sorted-swords (sort swords sort-swords))
       (swords-length (length swords)))
  (let loop ((i 1) (total 0) (sorted-swords sorted-swords))
    (if (<= i swords-length)
        (loop (1+ i) (+ total (* i (sword-id (car sorted-swords)))) (cdr sorted-swords))
        (format #t "P2 Answer: ~a\n\n" total))))
[–] ystael@beehaw.org 2 points 1 week ago

Does anybody miss old-school Lisp mutable data structures where you're kinda a functional language but you still have to worry about the difference between values and object identity? I'm not sure anybody does, but this is a retrocomputing house.

(ql:quickload :str)

(defun parse-line (line)
  (let* ((id-and-nums (str:split ":" line))
         (id (parse-integer (car id-and-nums)))
         (nums (mapcar #'parse-integer (str:split "," (cadr id-and-nums)))))
    (cons id nums)))

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

(defun fishbone-push (fb n)
  (if (null fb)
      (list (vector nil n nil))
      (let ((rib (car fb)))
        (cond ((and (null (aref rib 0))
                    (< n (aref rib 1)))
               (setf (aref rib 0) n)
               fb)
              ((and (null (aref rib 2))
                    (> n (aref rib 1)))
               (setf (aref rib 2) n)
               fb)
              ((null (cdr fb))
               (nconc fb (fishbone-push (cdr fb) n)))
              (t
               (fishbone-push (cdr fb) n)
               fb)))))

(defun make-fishbone (ns)
  (reduce #'fishbone-push ns :initial-value nil))

(defun quality (ns)
  (let ((fb (make-fishbone ns)))
    (parse-integer (apply #'str:concat
                          (mapcar #'(lambda (rib) (write-to-string (aref rib 1)))
                                  fb)))))

(defun main-1 (filename)
  (quality (cdar (read-inputs filename))))

(defun main-2 (filename)
  (let ((qualities (mapcar #'quality (mapcar #'cdr (read-inputs filename)))))
    (- (apply #'max qualities) (apply #'min qualities))))

(defun complex-quality (idx ns)
  (let* ((fb (make-fishbone ns))
         (quality
           (parse-integer (apply #'str:concat
                                 (mapcar #'(lambda (rib) (write-to-string (aref rib 1)))
                                         fb))))
         (rib-qualities
           (mapcar #'(lambda (rib)
                       (parse-integer
                        (apply #'str:concat
                               (mapcar #'write-to-string
                                       (remove-if #'null (coerce rib 'list))))))
                   fb)))
    (list (cons :quality quality)
          (cons :rib-qualities rib-qualities)
          (cons :index idx))))

(defun list> (ns1 ns2)
  (cond ((null ns1) nil)
        ((null ns2) t)
        ((> (car ns1) (car ns2)) t)
        ((< (car ns1) (car ns2)) nil)
        (t (list> (cdr ns1) (cdr ns2)))))

(defun cq> (cq1 cq2)
  (let ((q1 (cdr (assoc :quality cq1)))
        (q2 (cdr (assoc :quality cq2))))
    (cond ((> q1 q2) t)
          ((< q1 q2) nil)
          (t
           (let ((rq1 (cdr (assoc :rib-qualities cq1)))
                 (rq2 (cdr (assoc :rib-qualities cq2))))
             (cond ((list> rq1 rq2) t)
                   ((list> rq2 rq1) nil)
                   (t
                    (> (cdr (assoc :index cq1))
                       (cdr (assoc :index cq2))))))))))

(defun checksum (idxs)
  (loop for idx in idxs
        for n from 1 to (length idxs)
        sum (* idx n)))

(defun main-3 (filename)
  (let ((inputs (read-inputs filename))
        (sword-qualities (make-hash-table)))
    (loop for idx-ns in inputs
          do (setf (gethash (car idx-ns) sword-qualities)
                   (complex-quality (car idx-ns) (cdr idx-ns))))
    (let ((sorted-idxs
            (sort (mapcar #'car inputs)
                  #'(lambda (idx1 idx2)
                      (cq> (gethash idx1 sword-qualities)
                           (gethash idx2 sword-qualities))))))
      (checksum sorted-idxs))))