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/
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)) )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.
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..limite.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.lenuntil 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
- part 1: add offsets to index then clamp it between
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 . readInputWhat do the
.s mean inaddWith f = (f .) . (+)?.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.
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 |> part2Hmmm, I am certain I did the wrong one? I did quest 1, I’m pretty sure
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"
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.
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 < inputorrunhaskell 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




