[โ€“] 3 points 1 year ago* (last edited 1 year ago) (2 children)

Scheme/Guile

Guile doesn't seem to come with a bag implementation :(. Not a big deal, a linear scan works about as well.

(use-modules (ice-9 rdelim))
(use-modules (srfi srfi-1))

(define (parse-file file-name)
  (let* ((p (open-input-file file-name))
        (comma-split (string-split (read-line p) #\,))
        (number-list (map string->number comma-split)))
    number-list))

(let* ((crates (parse-file "notes/everybody_codes_e2025_q03_p1.txt"))
       (dedup-crates (delete-duplicates crates)))
  (format #t "P1 Answer: ~a\n\n" (apply + dedup-crates)))


(let* ((crates (parse-file "notes/everybody_codes_e2025_q03_p2.txt"))
       (dedup-crates (delete-duplicates crates))
       (sorted-crates (sort dedup-crates <)))
  (format #t "P2 Answer: ~a\n\n" (apply + (take sorted-crates 20))))


(let* ((crates (parse-file "notes/everybody_codes_e2025_q03_p3.txt"))
       (sorted-crates (sort crates <))
       (largest-set-size (let loop ((count 0) (l sorted-crates) (c #f) (max-count 0))
         (if (nil? l)
             max-count
             (let* ((new-c (car l))
                    (new-count (if (equal? new-c c) (+ count 1) 1)))
               (loop new-count (cdr l) new-c (max new-count max-count)))))))
  (format #t "P3 Answer: ~a\n\n" largest-set-size))
  • source
  • [โ€“] 3 points 1 year ago

    I was stuck for a while on part 2. I thought I was running into precision errors, so I re-implemented everything using exact integers, then I re-implemented it in python, but it turns out that I just had an off-by-1 error. It just so happened that my off-by-1 algorithm gave the correct solution to the sample input. Part 3 takes a while to compute.

    Scheme/Guile

    (use-modules (ice-9 rdelim))
    (use-modules (ice-9 format))
    (use-modules (srfi srfi-1))
    
    (define (parse-file file-name)
      (let* ((p (open-input-file file-name))
            (equality-split (string-split (read-line p) #\=))
            (complex-str (list-ref equality-split 1))
            (complex-parts (map string->number (string-split
              (substring/read-only complex-str 1 (- (string-length complex-str) 1))
              #\,))))
        (+ (car complex-parts) (* 0+1i (list-ref complex-parts 1)))))
    (define (special-div numerator divisor)
      (+
        (truncate/ (real-part numerator) (real-part divisor))
        (* 0+1i (truncate/ (imag-part numerator) (imag-part divisor)))))
    
    (let* ((A (parse-file "notes/everybody_codes_e2025_q02_p1.txt")))
      (format #t "Input is ~a\n" A)
      (let loop ((R 0+0i) (i 3))
        (if (> i 0)
          (loop (+ (special-div (* R R) 10+10i) A) (- i 1))
          (format #t "P1 Answer: [~d,~a]\n\n"
                  (inexact->exact (real-part R))
                  (inexact->exact (imag-part R))))))
    
    
    (define (check-engrave-range value)
      (and-map (lambda (x) (<= (abs x) 1000000)) (list (real-part value) (imag-part value))))
    (define (check-engrave point)
      (let loop ((result 0+0i) (i 100))
        (if (and (> i 0) (check-engrave-range result))
          (loop (+ point (special-div (* result result) 100000+100000i)) (- i 1))
          (check-engrave-range result))))
    (let* ((origin (parse-file "notes/everybody_codes_e2025_q02_p2.txt"))
           (engrave-count 0))
      (format #t "Input is ~a\n" origin)
      (let loop ((i 0))
        (if (<= i 1000) (begin
          (let loop ((j 0))
            (if (<= j 1000) (begin
              (set! engrave-count (+ engrave-count
                                     (if (check-engrave (+ origin (+ i (* j 0+1i)))) 1 0)))
              (loop (+ j 10)))))
          (loop (+ i 10)))))
      (format #t "P2 answer: ~a\n\n" engrave-count))
    
    
    (let* ((origin (parse-file "notes/everybody_codes_e2025_q02_p3.txt"))
           (engrave-count 0))
      (format #t "Input is ~a\n" origin)
      (let loop ((i 0))
        (if (<= i 1000) (begin
          (let loop ((j 0))
            (if (<= j 1000) (begin
              (set! engrave-count
                    (+ engrave-count (if (check-engrave (+ origin (+ i (* j 0+1i)))) 1 0)))
              (loop (+ j 1)))))
          (loop (+ i 1)))))
      (format #t "P3 answer: ~a\n\n" engrave-count))
    
  • source
  • [โ€“] 3 points 1 year 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  
    
  • source
  • [โ€“] 2 points 1 year ago

    I liked this one!

    import Control.Arrow  
    import Control.Monad  
    import Data.List  
    import Data.Ratio  
    
    simpleTrain = uncurry (%) . (head &&& last) . map read  
    
    compoundTrain input =  
      let a = read $ head input  
          z = read $ last input  
          gs =  
            map  
              ( uncurry (%)  
                  . (read *** read . tail)  
                  . break (== '|')  
              )  
              $ (tail . init) input  
       in foldl' (/) (a % z) gs  
    
    part1, part2, part3 :: [String] -> Integer  
    part1 = floor . (2025 *) . simpleTrain  
    part2 = ceiling . (10000000000000 /) . simpleTrain  
    part3 = floor . (100 *) . compoundTrain  
    
    main =  
      forM_  
        [ ("everybody_codes_e2025_q04_p1.txt", part1),  
          ("everybody_codes_e2025_q04_p2.txt", part2),  
          ("everybody_codes_e2025_q04_p3.txt", part3)  
        ]  
        $ \(input, solve) -> readFile input >>= print . solve . lines  
    
  • source
  • [โ€“] 2 points 1 year ago

    I thought this was going to be the knapsack problem, but no.

    import Control.Monad  
    import Data.List.Split  
    import qualified Data.Set as Set  
    import qualified Data.Multiset as MSet  
    
    part1, part2, part3 :: [Int] -> Int  
    part1 = sum . Set.fromList  
    part2 = sum . Set.take 20 . Set.fromList  
    part3 = maximum . MSet.toCountMap . MSet.fromList  
    
    main =  
      forM_  
        [ ("everybody_codes_e2025_q03_p1.txt", part1),  
          ("everybody_codes_e2025_q03_p2.txt", part2),  
          ("everybody_codes_e2025_q03_p3.txt", part3)  
        ]  
        $ \(input, solve) ->  
          readFile input >>= print . solve . map read . splitOn ","  
    
  • source
  • [โ€“] 2 points 1 year ago

    I was scared of a hard combinatorial puzzle day, but this was a breeze.

    {-# LANGUAGE TupleSections #-}
    module Main (main) where
    import Control.Monad ((<$!>))
    import qualified Data.Text.IO as TextIO
    import Data.Text (Text)
    import qualified Data.Text as Text
    import qualified Data.IntSet as IntSet
    import Control.Arrow ((>>>))
    import qualified Data.List as List
    import qualified Data.IntMap as IntMap
    
    part1 :: [IntSet.Key] -> IntSet.Key
    part1 = IntSet.fromList
      >>> IntSet.foldl (+) 0
    
    part2 :: [IntSet.Key] -> IntSet.Key
    part2 = IntSet.fromList
      >>> IntSet.toAscList
      >>> take 20
      >>> sum
    
    part3 :: [IntMap.Key] -> Int
    part3 = List.map (, 1)
      >>> IntMap.fromListWith (+)
      >>> IntMap.toList
      >>> List.map snd
      >>> maximum
    
    main :: IO ()
    main = do
      sizes <- map (read . Text.unpack) . Text.split (== ',') <$!> TextIO.getLine
      print $ part1 sizes
      print $ part2 sizes
      print $ part3 sizes
    
  • source
  • [โ€“] 4 points 1 year ago* (last edited 1 year ago)

    I'm still learning Scheme, but I wrote a solution in Guile:

    (use-modules (ice-9 rdelim ))
    (use-modules (srfi srfi-1))
    
    (define (parse-step s) (*
        (if (eq? (string-ref s 0) #\L) -1 1)
        (string->number (substring s 1))
    ))
    (define (parse-file file-name) (begin
      (define p (open-input-file file-name))
      (define names-str (read-line p))
      (read-line p)
      (define steps-str (read-line p))
      (cons
        (string-split names-str #\,)
        (map parse-step (string-split steps-str #\,))
      )
    ))
    
    (let* ((parsed-file (parse-file "notes/everybody_codes_e2025_q01_p1.txt"))
           (names (car parsed-file))
           (steps (cdr parsed-file)))
      (format #t "names: ~a\nsteps: ~a\n" names steps)
      (define name-count (length names))
      (define chosen-name-idx (reduce
        (lambda (pos change) (max 0 (min (- name-count 1) (+ pos change))))
        0 steps))
      (format #t "P1: Chosen name(~a): ~a\n\n" chosen-name-idx (list-ref names chosen-name-idx))
    )
    
    (let* ((parsed-file (parse-file "notes/everybody_codes_e2025_q01_p2.txt"))
           (names (car parsed-file))
           (steps (cdr parsed-file)))
      (format #t "names: ~a\nsteps: ~a\n" names steps)
      (define name-count (length names))
      (define chosen-name-idx (modulo (reduce + 0 steps) name-count))
      (format #t "P2: Chosen name(~a): ~a\n\n" chosen-name-idx (list-ref names chosen-name-idx))
    )
    
    (let* ((parsed-file (parse-file "notes/everybody_codes_e2025_q01_p3.txt"))
           (names (list->vector (car parsed-file)))
           (steps (cdr parsed-file)))
      (format #t "names: ~a\nsteps: ~a\n" names steps)
      (define name-count (vector-length names))
      (for-each (lambda (s)
          (define head (vector-ref names 0))
          (define swap-pos (modulo s name-count))
          (vector-set! names 0 (vector-ref names swap-pos))
          (vector-set! names swap-pos head)
        ) steps)
      (format #t "P3: Chosen name: ~a\n" (vector-ref names 0))
    )
    
  • source
  • [โ€“] 3 points 1 year ago* (last edited 1 year ago)

    I struggled for a long time because I had nearly the correct results. I had to switch div with quot.

    This puzzle was fun. If you have a visualization, it's even cooler. (It's a fractal)

    Haskell Code

    {-# LANGUAGE LambdaCase #-}
    {-# LANGUAGE PatternSynonyms #-}
    {-# OPTIONS_GHC -Wall #-}
    module Main (main) where
    import Text.Read (ReadPrec, Read (readPrec))
    import Data.Functor ((<&>))
    import Data.Text (pattern (:<), Text)
    import qualified Data.Text as Text
    import qualified Data.Text.IO as TextIO
    import Control.Monad ((<$!>))
    import Control.Arrow ((<<<))
    
    newtype Complex = Complex (Int, Int)
    
    instance Read Complex where
      readPrec :: ReadPrec Complex
      readPrec = readPrec <&> \case
        [a, b] -> Complex (a, b)
        _ -> undefined
    
    instance Show Complex where
      show :: Complex -> String
      show (Complex (a, b))= show [a, b]
    
    readAEquals :: Text -> Complex
    readAEquals ('A' :< '=':< rest) = read $ Text.unpack rest
    readAEquals _ = undefined
    
    
    -- >>> Complex (1, 1) `add` Complex (2, 2)
    -- [3,3]
    
    add :: Complex -> Complex -> Complex
    (Complex (x1, y1)) `add` (Complex (x2, y2)) = Complex (x1 + x2, y1 + y2)
    
    -- >>> Complex (2, 5) `times` Complex (5, 7)
    -- [-25,-11]
    
    times :: Complex -> Complex -> Complex
    (Complex (x1, y1)) `times` (Complex (x2, y2)) = Complex (x1 * x2 - y1 * y2, x1 * y2 + x2 * y1)
    
    dividedBy :: Complex -> Complex -> Complex
    (Complex (x1, y1)) `dividedBy` (Complex (x2, y2)) = Complex (x1 `quot` x2, y1 `quot` y2)
    
    step :: Complex -> Complex -> Complex
    step a r = let
     r1 = r `times` r
     r2 = r1 `dividedBy` Complex (10, 10)
     r3 = r2 `add` a
     in r3
    
    zero :: Complex
    zero = Complex (0, 0)
    
    part1 :: Complex -> Complex
    part1 a = iterate (step a) (Complex (0, 0)) !! 3
    
    shouldBeEngraved :: Complex -> Bool
    shouldBeEngraved complexPoint = let
    
      cycleStep :: Complex -> Complex -> Complex
      cycleStep point r = let
        r2 = r `times` r
        r3 = r2 `dividedBy` Complex (100000, 100000)
        in point `add` r3
    
      inRange x = x <= 1000000 && x >= -1000000
    
    
      in all (\ (Complex (x, y)) -> inRange x && inRange y)
        <<< take 101
        <<< iterate (cycleStep complexPoint)
        $ zero
    
    -- >>> shouldBeEngraved $ Complex (35630,-64880)
    -- True
    -- >>> shouldBeEngraved $ Complex (35460, -64910)
    -- False
    -- >>> shouldBeEngraved $ Complex (35630, -64830)
    -- False
    
    part2 :: Complex -> Int
    part2 (Complex (xA, yA)) = let
    
        xB = xA + 1000
        yB = yA + 1000
    
      in length . filter shouldBeEngraved $ do
        x <- [xA, xA+10.. xB]
        y <- [yA, yA+10.. yB]
        pure $ Complex (x, y)
    
    part3 :: Complex -> Int
    part3 (Complex (xA, yA)) = length . filter shouldBeEngraved $ do
      x <- [xA..xA+1000]
      y <- [yA..yA+1000]
      pure $ Complex (x, y)
    
    -- >>> [0, 10..100]
    -- [0,10,20,30,40,50,60,70,80,90,100]
    
    main :: IO ()
    main = do
      a <- readAEquals <$!> TextIO.getContents
      print $ part1 a
      print $ part2 a
      print $ part3 a
    

    My girlfriend is learning python, we are taking on the challenges together, today I may upload her solution:

    python

    A=[-3344,68783]
    R = [0, 0]
    B= [A[0]+1000, A[1]+1000]
    pointsengraved = 0
    cycleright = 0
    
    
    for i in range(A[1], B[1]+1):
        for j in range(A[0], B[0]+1):
            for k in range(100):
                R = [int(R[0] * R[0] - R[1] * R[1]), int(R[0] * R[1] + R[1] * R[0])]
                R = [int(R[0] / 100000), int(R[1] / 100000)]
                R = [int(R[0] + j), int(R[1] + i)]
                if -1000000>R[0] or R[0]>1000000 or -1000000>R[1] or R[1]>1000000:
                    #print(".", end="")
                    break
                cycleright += 1
            if cycleright == 100:
                pointsengraved += 1
                #print("+", end="")
            cycleright = 0
            R = [0, 0]
        #print()
    
    print(pointsengraved)
    

    The commented out print statements produce an ascii map of the set, which can be cool to view at the right font size.

  • source
  • [โ€“] 3 points 1 year 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

  • source
  • [โ€“] 2 points 1 year ago

    Nim

    For part 3 I parse gears as tuples, with regular gears having same value on both ends e.g.

    3|5 -> (3, 5)
    3   -> (3, 3)
    
    proc parseGears(input: string): seq[int] =
      for line in input.splitLines():
        result.add parseInt(line)
    
    proc parseNestedGears(input: string): seq[(int, int)] =
      for line in input.splitLines():
        let nested = line.split('|').mapIt(it.parseInt)
        result.add:
          if nested.len == 1: (nested[0], nested[0])
          else: (nested[0], nested[1])
    
    proc solve_part1*(input: string): Solution =
      let gears = parseGears(input)
      result := 2025 * gears[0] div gears[^1]
    
    proc solve_part2*(input: string): Solution =
      let gears = parseGears(input)
      result := ceil(10000000000000'f64 / (gears[0] / gears[^1])).int
    
    proc solve_part3*(input: string): Solution =
      let gears = parseNestedGears(input)
      let ratios = (0..gears.high-1).mapIt(gears[it][1] / gears[it+1][0])
      result := int(100 * ratios.prod)
    

    Full solution at Codeberg: solution.nim

  • source
  • [โ€“] 2 points 1 year ago* (last edited 1 year ago)

    Nim

    Reading this day's quest I was at first a bit confused what it asks me to calculate. Took me about a minute to realize that most of descriptions are not important and actual calculations for each part are very simple:

    part 1 wants sum of each unique crate size
    part 2 is same but only 20 smallest
    part 3 is max count, because you can always put most crates in one large set and you only need 1 extra set per duplicate crate

    proc solve_part1*(input: string): Solution =
      var seen: set[int16]
      for num in input.split(','):
        let num = parseInt(num).int16
        if num in seen: continue
        else:
          seen.incl num
          result.intVal += num
    
    proc solve_part2*(input: string): Solution =
      var set = input.split(',').mapIt(parseInt(it).uint16)
      set.sort()
      result := set.deduplicate(isSorted=true)[0..<20].sum()
    
    proc solve_part3*(input: string): Solution =
      var cnt = input.split(',').toCountTable()
      result := cnt.largest.val
    

    Full solution at Codeberg: solution.nim

  • source
  • [โ€“] [S] 3 points 1 year 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()
    }
    
  • source
  • [โ€“] [S] 2 points 1 year ago

    Rust

    use num::{BigInt, Integer};
    
    pub fn solve_part_1(input: &str) -> String {
        let gears: Vec<i64> = input.trim().lines().map(|g| g.parse().unwrap()).collect();
        (2025 * gears[0] / gears.last().unwrap()).to_string()
    }
    
    pub fn solve_part_2(input: &str) -> String {
        let gears: Vec<i64> = input.trim().lines().map(|g| g.parse().unwrap()).collect();
        let res = (BigInt::parse_bytes(b"10000000000000", 10).unwrap() * gears.last().unwrap())
            .div_ceil(&(BigInt::ZERO + gears[0]));
        res.to_string()
    }
    
    pub fn solve_part_3(input: &str) -> String {
        let mut lines = input.trim().lines();
        let first_gear = BigInt::parse_bytes(lines.next().unwrap().as_bytes(), 10).unwrap();
        let mut nominator: BigInt = first_gear * 100;
        let mut denominator: BigInt = BigInt::ZERO + 1;
        for line in lines {
            let mut split = line.split("|");
            denominator *= BigInt::parse_bytes(split.next().unwrap().as_bytes(), 10).unwrap();
            match split.next() {
                Some(size) => {
                    nominator *= BigInt::parse_bytes(size.as_bytes(), 10).unwrap();
                }
                None => {
                    break;
                }
            }
        }
        (nominator / denominator).to_string()
    }
    
  • source
  • [โ€“] [S] 2 points 1 year ago

    Rust

    pub fn solve_part_1(input: &str) -> String {
        let mut crates: Vec<i64> = input.split(",").map(|s| s.parse().unwrap()).collect();
        crates.sort();
        let mut monotonic_subsequence = vec![crates[0]];
        for size in crates.into_iter().skip(1) {
            if size == *monotonic_subsequence.last().unwrap() {
                continue;
            }
            monotonic_subsequence.push(size);
        }
        monotonic_subsequence.iter().sum::<i64>().to_string()
    }
    
    pub fn solve_part_2(input: &str) -> String {
        let mut crates: Vec<i64> = input.split(",").map(|s| s.parse().unwrap()).collect();
        crates.sort();
        let mut monotonic_subsequence = vec![crates[0]];
        for size in crates.into_iter().skip(1) {
            if size == *monotonic_subsequence.last().unwrap() {
                continue;
            }
            monotonic_subsequence.push(size);
            if monotonic_subsequence.len() >= 20 {
                break;
            }
        }
        monotonic_subsequence.iter().sum::<i64>().to_string()
    }
    
    pub fn solve_part_3(input: &str) -> String {
        let mut crates: Vec<i64> = input.split(",").map(|s| s.parse().unwrap()).collect();
        crates.sort();
        let mut monotonic_subsequences = vec![vec![crates[0]]];
        for size in crates.into_iter().skip(1) {
            let updateable_sequence = monotonic_subsequences
                .iter_mut()
                .find(|v| *v.last().unwrap() < size);
            match updateable_sequence {
                Some(v) => {
                    v.push(size);
                }
                None => {
                    monotonic_subsequences.push(vec![size]);
                }
            }
        }
        monotonic_subsequences.len().to_string()
    }
    
  • source
  • [โ€“] 4 points 1 year ago

    my boring python solution:

    from pathlib import Path
    
    
    def main():
        input = Path('input.txt').read_text().split('\n')
        names = input[0].split(',')
        instructions = input[-1].split(',')
        print(names,instructions)
        index = 0
        for instruction in instructions:
            dir = instruction[0]
            number = int(instruction[1:])
            if dir == 'L':
                index -= number
                if index < 0:
                    index = 0
            else:
                index += number
                if index > len(names) - 1:
                    index = len(names) - 1
        print(names[index])
        index = 0
        for instruction in instructions:
            dir = instruction[0]
            number = int(instruction[1:])
            if dir == 'L':
                index -= number
            else:
                index += number
        print(names[index%(len(names))])
        indexes = list(range(len(names)))
        for instruction in instructions:
            dir = instruction[0]
            number = (int(instruction[1:]) if dir == 'R' else -int(instruction[1:])) % len(names)
            indexes[0], indexes[number] = indexes[number], indexes[0]
        print(names[indexes[0]])
    
    if __name__ == "__main__":
        main()
    

    I probably should read all 3 files though. I'll hone it out later.

  • source
  • [โ€“] 3 points 1 year ago

    For quest 2, I decided to implement my own Complex type and operators, because I expected parts 2 and 3 to have something unconventional, but alas it's just a regular Mandelbrot fractal.

    Nim again, Nim forever:

    type Complex = tuple[x,y: int]
    proc `$`(c: Complex): string = &"[{c.x},{c.y}]"
    proc `+`(a,b: Complex): Complex = (a.x+b.x, a.y+b.y)
    proc `-`(a,b: Complex): Complex = (a.x-b.x, a.y-b.y)
    proc `*`(a,b: Complex): Complex = (a.x*b.x - a.y*b.y, a.x*b.y + a.y*b.x)
    proc `/`(a,b: Complex): Complex = (a.x div b.x, a.y div b.y)
    proc `/`(a: Complex, b: int): Complex = (a.x div b, a.y div b)
    
    proc parseInput(input: string): Complex =
      let parts = input.split({'=','[',',',']'})
      (parseInt(parts[2]), parseInt(parts[3]))
    
    proc isStable(point: Complex, iter: int): bool =
      var num: Complex
      for _ in 1..iter:
        num = (num * num) / 100_000 + point
        if num.x notin -1_000_000 .. 1_000_000 or
           num.y notin -1_000_000 .. 1_000_000:
          return false
      true
    
    proc solve_part1*(input: string): Solution =
      let start = parseInput(input)
      var point: Complex
      for _ in 1..3:
        point = (point * point) / 10 + start
      result := $point
    
    proc solve_part2*(input: string): Solution =
      let start = parseInput(input)
      for y in 0..100:
        for x in 0..100:
          let point: Complex = (start.x + 10 * x, start.y + 10 * y)
          if point.isStable(iter=100): inc result.intVal
    
    proc solve_part3*(input: string): Solution =
      let start = parseInput(input)
      for y in 0..1000:
        for x in 0..1000:
          let point: Complex = (start.x + x, start.y + y)
          if point.isStable(iter=100): inc result.intVal
    

    Full solution at Codeberg: solution.nim

  • source
  • [โ€“] 4 points 1 year ago

    So far I really like the difficulty level of EC, compared to AOC.
    Quest 1 is pretty straightforward:

    • part 1: add offsets to index then clamp it between 0..limit e.g. min(limit, max(0, index))
    • part 2: add offsets and then modulo to figure out last index
    • part 3: swap first element with element at index mod names.len until done =)

    My solution in Nim:

    proc parseInput(input: string): tuple[names: seq[string], values: seq[int]] =
      let lines = input.splitLines()
      result.names = lines[0].split(',')
    
      let values = lines[2].multiReplace({"R":"","L":"-"})
      for num in values.split(','):
        result.values.add parseInt(num)
    
    proc solve_part1*(input: string): Solution =
      let (names, values) = parseInput(input)
      var pos = 0
      for value in values:
        pos = min(names.high, max(0, pos + value))
      result := names[pos]
    
    proc solve_part2*(input: string): Solution =
      let (names, values) = parseInput(input)
      let pos = values.sum()
      result := names[pos.euclMod(names.len)]
    
    proc solve_part3*(input: string): Solution =
      var (names, values) = parseInput(input)
      for value in values:
        swap(names[0], names[euclMod(value, names.len)])
      result := names[0]
    

    Full solution at Codeberg: solution.nim

  • source
  • FSharp
    (my first submission failed, so I may not be as detailed here)

    I laugh at how long this took me. At first I got stuck due to checked arithmetic. In FSharp you open the Checked module and you get the overloaded operators for primitives, whereas CSharp is checked {a + b}. I also mistakenly used distinct on part 3 when I didn't need it.

    I did appreciate the choose functions of functional programming, which returns all of the Some values of a sequence of options. The PSeq library let me use 70% of the cpu on the calculations :)

    module EveryBodyCodes2025.Quest02  
    
    open System.IO  
    open System.Text.RegularExpressions  
    open FSharp.Collections.ParallelSeq  
    open Checked  
    
    [<Struct>]  
    type ComplexNumber = 
        {X: int64; Y: int64}  
        static member (+) (a: ComplexNumber, b: ComplexNumber) = {X = a.X + b.X; Y = a.Y + b.Y }  
        static member (*) (a: ComplexNumber, b: ComplexNumber) = { X = ( a.X * b.X ) - (a.Y * b.Y); Y = (a.X * b.Y) + (a.Y * b.X) }  
        static member (/) (a: ComplexNumber, b: ComplexNumber) = { X = a.X / b.X; Y = a.Y / b.Y }  
        override this.ToString() = $"[{this.X},{this.Y}]"  
        
    let parseInput file =  
        File.ReadAllLines file  
        |> fun lines ->  
            let reg = Regex(@"A=\[(-?\d+),(-?\d+)\]")  
            let res = reg.Match(lines[0])  
            match res.Success with  
            | true ->  
                let x = int64 res.Groups[1].Value  
                let y = int64 res.Groups[2].Value  
                {X = x; Y = y}  
            | false -> failwith "failed to parse"  
            
        
    let part1 () =  
        parseInput "Inputs/Quest02/Q02_P01.txt"  
        |> fun a ->  
            [1..3]  
            |> List.fold (fun acc _ -> (acc * acc) / { X = 10; Y = 10 } + a ) {X = 0; Y = 0}  
    
    let cycle p =  
        let rec iterate current iteration =  
            if iteration = 100 then Some current  
            else  
                let next = (current * current) / {X = 100_000; Y = 100_000 } + p  
                if abs(next.X) > 1_000_000 || abs(next.Y) > 1_000_000 then  
                    None  
                else iterate next (iteration + 1)  
        iterate { X = 0; Y = 0 } 0  
    
    let part2 () =  
        parseInput "Inputs/Quest02/Q02_P02.txt"  
        |> fun a ->  
            seq {  
                for y in 0..100 do  
                for x in 0..100 do  
                    yield {X = a.X + (int64 x * 10L); Y = a.Y + (int64 y * 10L)}  
            }  
            |> PSeq.choose cycle  
            |> PSeq.distinct  
            |> PSeq.length  
    
    let part3 () =  
        parseInput "Inputs/Quest02/Q02_P03.txt"  
        |> fun a ->  
            seq {  
                for y in 0..1000 do  
                for x in 0..1000 do  
                    yield {X = a.X + (int64 x); Y = a.Y + (int64 y)}  
            }  
            |> PSeq.choose cycle  
            |> PSeq.length  
    
  • source
  • [โ€“] 2 points 1 year ago* (2 children)

    Here's the right challenge. This was a lot simpler than the tracking of cycles.

    type Direction = Left | Right  
    type Instruction = { Direction: Direction; Distance: int }  
    let parseInstruction (segment:string) =  
        let direction = if segment[0] = 'L' then Left else Right  
        let distance = Int32.Parse(segment.AsSpan().Slice(1)) 
        { Direction = direction; Distance = distance }  
        
    let inline normalize (array: 'a array) idx =  
        if idx >= 0 then idx % array.Length else array.Length - (-idx % array.Length)  
        
    let inline nextIndex instruction idx =  
        match instruction.Direction with  
        | Left -> idx - instruction.Distance  
        | Right -> idx + instruction.Distance  
        
    let readFile file =  
        File.ReadAllLines(file)  
        |> fun lines ->  
            let names = lines.[0] |> _.Split(",", StringSplitOptions.RemoveEmptyEntries ||| StringSplitOptions.TrimEntries)  
            let instructions =  
                lines[2]  
                |> _.Split(",", StringSplitOptions.RemoveEmptyEntries ||| StringSplitOptions.TrimEntries)  
                |> Array.map parseInstruction  
            (names, instructions)  
    
    let part1File file : string =  
            readFile file  
            |> fun (names, instructions) ->  
                    instructions  
                    |> Seq.fold (fun (idx, _) instruction  ->  
                            let next = nextIndex instruction idx |> fun v -> Math.Clamp(v, 0, names.Length - 1)  
    
                            match Array.tryItem next names with  
                            | Some n -> next, n  
                            | None -> failwith "off array"  
                        ) (0, "")  
            |> snd  
            
    let part1 () = part1File "Inputs/Quest01/Q01_P01.txt"  
    
    let part2File file =  
        readFile file  
        |> fun (names, instructions) ->  
            instructions  
            |> Seq.fold (fun (idx, _) instruction ->  
                 let next =  nextIndex instruction idx |> normalize names  
                 match Array.tryItem next names with  
                            | Some n -> next, n  
                            | None -> failwith "off array"  
                        ) (0, "")  
            |> snd  
    
    let part2() = part2File "Inputs/Quest01/Q01_P02.txt"  
    
    let part3File file =  
        readFile file  
        |> fun (names, instructions) ->  
            // need to preserve a reference to an array that can be mutated  
            let mutable nameArray = names  
            instructions  
            |> Seq.fold (fun _ instruction ->  
                // for this challenge the index doesn't matter  
                let next = match instruction.Direction with  
                            | Left -> names.Length - instruction.Distance  
                            | Right -> instruction.Distance  
                            |> normalize names  
                match Array.tryItem next nameArray with  
                            | Some n ->  
                                nameArray <- arraySwap 0 next names  
                                0, n  
                            | None -> failwith "off array"  
                ) (0, "")  
            |> snd  
    
    let part3() = part3File  "Inputs/Quest01/Q01_P03.txt"  
    
  • source
  • parent
  • context
  • view more: โ€น prev next โ€บ