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/
You must log in or # to comment.
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 . readInputNim
Nothing fancy. Simple iterative tree construction and sort, using the
std/algorithmand 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) * idFull solution at Codeberg: solution.nim
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() }



