Advent of Code - adventofcode.com

Posted on
Page
of 21
  • decided to actually do it this year. doing it in haskell to brush up after 2 years of almost exclusively doing js/ts and python. posting results in https://github.com/ZooeyMiller/advent-of-code-2021

    day1 code is messy as heck sadly...

  • Is today the beginnings of our assembly-like language then?

  • Day 2 done without too much pain once I got familiar with techniques for parsing the input.
    https://gist.github.com/dmckennell/86ac3155367122502be2643f4b021edf

    Nice to see such a broad range of languages this year. Enjoying reading through and understanding the approaches of others.

  • For day 1, there's quite an elegant solution using zipWith. Note that you don't actually have to chunk things up, as pointed out, since the windows overlap you only ever have to care about the start and end points (which can be achieved by zipping together the input list and the input list offset by the window length):

    module Main where
    
    inp :: String -> IO [Int]
    inp path = do
      c <- readFile path
      return $ map read (lines c)
    
    solve :: Int -> [Int] -> Int
    solve n xs = length . filter id $ zipWith (<) xs (drop n xs)
    
    main :: IO ()
    main = do
      vals <- inp "day01.input"
      print $ solve 1 vals
      print $ solve 3 vals
    

    FWIW, an idiomatic way of chunking a list would be:

    import Data.List (tails)
    
    chunk :: Int -> [a] -> [[a]]
    chunk n xs = foldr (zipWith (:)) (repeat []) (take n (tails xs))
    

    Why does this work? Suppose you have two lists [1, 2, 3, 4] and [2, 3, 4]. If you zip them together you get [(1, 2), (2, 3), (3, 4)] which is chunking by two. tails produces all tails of a list tails [1, 2, 3, 4] = [[1, 2, 3, 4], [2, 3, 4], [3, 4], [4], []]. So if we take n we will get the right inputs for zipping together to produce a window of length n. We can't use zip though, because that needs two lists. So foldr (zipWith (:)) (repeat []) is generalising zip to n inputs and producing a list of lists as outputs (rather than a list of tuples)...

  • Ahhh very nice. Thanks for the explanation!

  • Here's my haskell part 2. I noticed you had a comment where you had an issue using foldr, but things work with foldl. The reason for this is that foldr is for a right-associative operator. But the operator as defined in the problem (for part 2) is left associative (if you think about this, it's because you're starting at the beginning of the list and updating the depth based on the current aim). So you can either do foldr op ... (reverse instructions) or foldl op ... instructions. If you're then doing foldl, it's better to use foldl' (from Data.Foldable) as you're more efficient since you collapse as you go, rather than consing all the computation at once. Since foldl doesn't work on infinite lists anyway, this is always safe to do.

    module Main where
    import Data.Foldable (foldl')
    
    data Move = F Int | D Int
      deriving (Eq, Show)
    
    parse :: String -> Maybe Move
    parse ('f':xs) = Just . F $ read (drop 7 xs)
    parse ('d':xs) = Just . D $ read (drop 4 xs)
    parse ('u':xs) = Just . D $ -read (drop 2 xs)
    parse _ = Nothing
    
    inp :: String -> IO (Maybe [Move])
    inp path = do
      c <- readFile path
      return $ mapM parse (lines c)
    
    part1 :: Maybe [Move] -> Int
    part1 Nothing = 0
    part1 (Just xs) = h * d
      where (h, d) = foldl' (\(h, d) x ->
                               case x of
                                 F n -> (h + n, d)
                                 D n -> (h, d + n))
                     (0, 0) xs
    
    part2 :: Maybe [Move] -> Int
    part2 Nothing = 0
    part2 (Just xs) = h * d
      where (h, d, _) = foldl' (\(h, d, a) x ->
                                  case x of
                                    F n -> (h + n, d + (a * n), a)
                                    D n -> (h, d, a + n))
                        (0, 0, 0) xs
    
    main :: IO ()
    main = do
      instructions <- inp "../inputs/2021/day02.input"
      print $ part1 instructions
      print $ part2 instructions
    
  • Thanks @Greenbank appreciate the feedback.

    So obvious when you explain it like that!

    Edit: Day 2 now done.

  • Bit more of a challenge to produce something nice and readable today... (with Python lists of lists anyway)

  • Stupid lack of error tracking meant going round in circles. Realised it made one of my binary numbers for Day 3 Part 2 off by one 🤦‍♂️ That was a frustrating runaround!

  • Hehe

  • Took me longer than I would have liked today. Pushing Python solutions to GitHub:
    https://github.com/willjrh/Advent-of-code-21

  • def bin2num(binary):
        # print([x * 2 ** ind for ind, x in enumerate(reversed(binary))])
        return np.sum([x * 2 ** ind for ind, x in enumerate(reversed(binary))])
    

    You can probably make use of something like int("".join(x), base=2)

  • Thanks, I actually did notice this from looking at other solutions!
    Now starting day 4, I will have a little more time to think about a nice solution today, without that awful burden of work.

  • For the first time ever, I actually had the solution to the second part after completing the first part. Probably just means I overdid part 1 though.

  • I got fed up trying to make day 3 nice: https://github.com/rhowe/aoc/blob/main/03/3part2.jsonnet

  • Bit of a bastard today. Here's my solution - didn't spend any time making it nice and screwed myself a bit with the hash method because I forgot you had to keep track of the unmarked numbers

    https://github.com/jyates13/aoc2021/blob/main/d4/d4.py

  • I did it broadly the way you did first time round, and then thought harder about how to check for when a card wins, and did this: https://gist.github.com/wence-/500e1bc4633192ac6fee67433aedd974#file-day04-py

  • Brain isn't working today. Not sure I cba with part 2: https://github.com/rhowe/aoc/blob/main/04/4part1.jsonnet

  • Very clever!

    I was going to do a double hashmap (hash x to point to all boards containing x, then for each board containing x, hash x to position of x in board y) to get it quicker if necessary, so I don't have to check if each board contains x, then that'd probably be as quick as it ought to be O(n) lookups I think? But the hashmap can't match your min/max thing to find time to solve - I always have to check the whole row/column.

  • I did it a similar way (after an initial naive version), although I only solve each board once and keep track of the minimum/maximum (and correpsonding 'scores') as I read in each board.

    Thinking was this:-

    • Read in the numbers and have a map between position and number (and vice versa)
    • You have to solve every board to know whether it will be the first or the last
    • A board is just 10 sets of 5 values corresponding to the 5 rows and 5 columns, plus the full set of numbers in that board (which you'll need to
    • In the 10 sets of 5 numbers, instead of storing the actual numbers, store the positions that they appear in the list (through the map created when reading in the list of input numbers)
    • In each of these 10 sets of 5 numbers, find the maximum of these
    • And then the 'solve time' for this board is the minimum of these 10 maximum values
    • You can then loop through the set of all 25 numbers in the board and sum the values which appear after the 'solve time' for this board (e.g. the sum of the uncalled numbers)
    • Keep track of the minimum and maximum 'solve time' as I process the boards (and keep the corresponding final scores) - output these values at the end
  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Advent of Code - adventofcode.com

Posted by Avatar for Drakien @Drakien

Actions