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/

  • Amy@piefed.blahaj.zone
    link
    fedilink
    English
    arrow-up
    1
    ·
    11 hours 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  
    
  • janAkali@lemmy.sdf.org
    link
    fedilink
    arrow-up
    2
    ·
    edit-2
    22 hours 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

  • hades@programming.devOPM
    link
    fedilink
    arrow-up
    3
    ·
    1 day 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()
    }