Spyke
advent_of_code·Advent Of Codebyhades

🦆 Everybody.Codes 2025 Quest 1 Solutions 🦆

Quest 1: Whispers in the Shell

  • 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/

View original on programming.dev
Amy
piefed.blahaj.zone

Ooh, challenges! Here we go!

I haven't really written any Haskell since last year's AoC, and boy am I rusty.

import Control.Monad  
import Data.List  
import Data.List.Split  
import Data.Vector qualified as V  

readInput s =  
  let [names, _, moves] = splitOn "," <$> lines s  
   in (names, map readMove moves)  
  where  
    readMove (d : s) =  
      let n = read s :: Int  
       in case d of  
            'L' -> -n  
            'R' -> n  

addWith f = (f .) . (+)  

part1 names moves =  
  names !! foldl' (addWith $ clamp (length names)) 0 moves  
  where  
    clamp n x  
      | x < 0 = 0  
      | x >= n = n - 1  
      | otherwise = x  

part2 names moves =  names !! (sum moves `mod` length names)  

part3 names moves =  
  V.head  
    . foldl' exchange (V.fromList names)  
    $ map (`mod` length names) moves  
  where  
    exchange v k = v V.// [(0, v V.! k), (k, V.head v)]  

main =  
  forM_  
    [ ("everybody_codes_e2025_q01_p1.txt", part1),  
      ("everybody_codes_e2025_q01_p2.txt", part2),  
      ("everybody_codes_e2025_q01_p3.txt", part3)  
    ]  
    $ \(input, solve) ->  
      readFile input >>= putStrLn . uncurry solve . readInput  
5

. means function composition in Haskell. (f .) . (+) is just an obscure way to write \x y -> f (x + y). It's not recommended to use this point-free style in production code, but it is sometimes fun to experiment with for challenges like this.

3

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

4

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.

4

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))
)
4

edit: I did the wrong quest

I wasn't very happy with myself on this one. Part 1 wound up being simple enough, but part 2 took me for a loop. My disappointment was that I asked an AI for a hint, but instead it coded up a solution revolved around checking cycle dependency.

I tried to at least redo it in a functional way, since I'm writing this in FSharp, but I ran into a bunch of hiccups around indexes and index tracking. That always costs me so much time. This took me forever and I'm near the bottom of the rankings haha.

Overall, I wasn't terribly happy with how I did on this one. Oh, I wound up switching all of the types to BigInteger because I was lost as to what was happening. It turned out to be indexes and orderings, but I thought that maybe something was getting truncated.

    /// Map from (prev, curr) pair to position  
    type PositionMap = Map<BigInteger * BigInteger, BigInteger * BigInteger>  
    let rec findCycle (pairToNextPair: PositionMap) startPair currentPair acc =  
        if currentPair = startPair && List.length acc > 0 then  
            Some (List.rev acc)  
        else  
            match Map.tryFind currentPair pairToNextPair with  
            | None -> None  
            | Some nextPair -> 
                findCycle pairToNextPair startPair nextPair (snd currentPair :: acc)  

    let rec eni2' score (n:BigInteger) (exp:BigInteger) (m:BigInteger) (pairMap: PositionMap) (scores:BigInteger list) iter =  
        if iter > exp then scores |> List.rev |> List.skip (max 0 (List.length scores - 5)) |> toBigInt  
        else  
            let newScore = (score * n) % m  
            let key = (score, newScore)  
            
            match Map.tryFind key pairMap with  
            | Some _ -> 
                match findCycle pairMap key key [] with  
                | Some cycle ->  
                    let remaining = int64 (exp - iter)  
                    let cycleValues = cycle  
                    let cycleLength = List.length cycleValues |> int64  
                    let scoresLength = List.length scores |> int64  
                    let totalLength = scoresLength + 1L + remaining // scores + newScore + remaining  
                    
                    let needCount = min 5L totalLength  
                    let startPos = max 0L (totalLength - needCount)  
                    
                    let scoresReversed = List.rev scores  
                    
                    let final5 = 
                        [startPos..totalLength - 1L]  
                        |> List.map (fun pos ->  
                            if pos < scoresLength then  
                                // Position is in scores (scores is reversed, so oldest is at end)  
                                scoresReversed.[int pos]  
                            elif pos = scoresLength then  
                                // Position is newScore  
                                newScore  
                            else  
                                let cycleOffset = pos - scoresLength  
                                let cyclePos = cycleOffset % cycleLength  
                                cycleValues.[int cyclePos]  
                        )  
                    
                    // final 5 comes out in reverse order  
                    final5 |> List.rev |> toBigInt  
                | None -> 
                    eni2' newScore n exp m (Map.add key ((newScore, (newScore * n) % m)) pairMap) (newScore::scores) (iter + BigInteger 1)  
            | None ->  
                let nextPair = (newScore, (newScore * n) % m)  
                eni2' newScore n exp m (Map.add key nextPair pairMap) (newScore::scores) (iter +  BigInteger 1)  

    let eni2 (n) (exp) (m) = eni2' (BigInteger 1) n exp m Map.empty [] (BigInteger 1)  

    let part2 (input: IDictionary<string, BigInteger> array)=  
        input  
        |> Array.map (fun line ->  
            //printfn $"""running line {line.AsEnumerable() |> Seq.map(fun kv -> kv.Key + "=" + kv.Value.ToString()) |> fun x -> String.Join(", ", x)}"""  
            let a = eni2 line["A"] line["X"] line["M"]  
            let b = eni2 line["B"] line["Y"] line["M"]  
            let c = eni2 line["C"] line["Z"] line["M"]  
            let ret = a + b + c  
            //printfn $"found {ret}"  
            ret  
        ) |> Seq.max  

    let part2Answer = 
        File.ReadAllLines "Inputs/Q01_P02.txt"  
        |> part1Lines  
        |> part2  
3

I'm very curious about F# since I've never used it or seen it anywhere before but I'm afraid I'm too tired to read it right now. Thank you for posting, I hope I'll remember to come back tomorrow.

2

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"  
2
lemmy.world

Thank you for this update. Now that problwm and solution fit, I can understand whats going on in your code :]

2

I coded this along with my girlfriend who's learning python, but not motivated to share her solution. The program reads from stdin, because I usually invoke it like so: runhaskell Main.hs < input or runhaskell Main.hs < example. I think this is quite handy because I don't have to change the source code to check the example input again.

I struggled with Part 3, where I suddenly forgot I could've simply used mod, which I ended up doing anyway. I immediately recognized that Part 3 needs Mutable Arrays if I care to avoid Index hell, which is not what I wanted to with Haskell but oh well.

{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE PatternSynonyms #-}
module Main (main) where

import qualified Data.Text as Text
import qualified Data.Text.IO as TextIO
import Control.Monad ((<$!>), forM_)
import Data.Text (Text, pattern (:<))
import qualified Data.List as List
import qualified Data.Array.MArray as MutableArray
import Control.Monad.ST (runST, ST)
import Data.Array.ST (STArray)

commaSepLine :: IO [Text.Text]
commaSepLine = Text.split (== ',') <$!> TextIO.getLine

readInstruction :: Text -> Int
readInstruction ('R' :< n) = read . Text.unpack $ n
readInstruction ('L' :< n) = negate . read . Text.unpack $ n
readInstruction _ = undefined

myName :: (Foldable t, Ord b, Enum b, Num b) => b -> t b -> b
myName maxPosition = List.foldl' (\ pos offset -> min (pred maxPosition) . max 0 $ pos + offset) 0

parentName1 :: [Int] -> Int
parentName1 = List.sum

newSTArray :: [e] -> ST s (STArray s Int e)
newSTArray xs = MutableArray.newListArray (0, length xs - 1) xs

swap :: (MutableArray.MArray a e m, MutableArray.Ix i) => a i e -> i -> i -> m ()
swap array i0 i1 = do
  e0 <- MutableArray.readArray array i0
  e1 <- MutableArray.readArray array i1
  MutableArray.writeArray array i0 e1
  MutableArray.writeArray array i1 e0

parentName2 :: [Text] -> [Int] -> Text
parentName2 nameList instructions = runST $ do
  names <- newSTArray nameList
  arrayLength <- succ . snd <$> MutableArray.getBounds names
  forM_ instructions $ \ offset -> do
    let arrayOffset = offset `mod` arrayLength
    swap names 0 arrayOffset
  MutableArray.readArray names 0

main :: IO ()
main = do
  names <- commaSepLine
  _ <- TextIO.getLine
  instructions <- fmap readInstruction <$> commaSepLine

  let namesLength = length names
  print $ names !! myName namesLength instructions
  print . (names !!) . (`mod` namesLength) $ parentName1 instructions
  print $ parentName2 names instructions
2
lemmy.world

Short and simple Uiua solution :-)

Data ← {"Vyrdax,Drakzyph,Fyrryn,Elarzris"
        "R3,L2,R3,L1"}
Prep    ← ⊜□⊸≠@,°□⊣⟜(+1/+˜=@,°□⊢)
Present ← °□˜⊡⊜□⊸≠@,°□⊢Data⊙◌
Part₁   ← ∧(↧⊙(⊸-₁)↥0+⍥¯=@L⊙⋕°⊂°□)⊙0
Part₂   ← ∧(˜◿⊙⟜∘+⍥¯=@L⊙⋕°⊂°□)⊙0
Present Part₁ Prep Data
Present Part₂ Prep Data
Data ← {"Vyrdax,Drakzyph,Fyrryn,Elarzris"
        "R3,L2,R3,L3"}
∩(⊜□⊸≠@,)∩°□°⊟⇌Data
°□⊢∧(⍜⊏⇌⊂0◿(⧻⊃⋅∘⊙∘)⍥¯=@L⊙⋕°⊂°□)

-> "Fyrryn", "Elarzris", "Drakzyph"

1
hadesreply
programming.dev

ah, uiua, the only thing that makes me wish Unicode hadn't been invented :)

2

You reached the end