Hi, I’m Amy.

✨ New 🏳️‍⚧️ improved ♀️ version 👩‍❤️‍👩 out 🏳️‍🌈 now! 🎊

I live in Japan. Talk to me about Haskell, Scheme, and Linux.

日本語も通じます。

  • 4 Posts
  • 41 Comments
Joined 2 months ago
cake
Cake day: October 17th, 2025

help-circle
  • Haskell

    IntSet was the wrong first choice for part 2 :3

    import Control.Arrow  
    import Data.Foldable  
    import Data.Ix  
    
    readInput :: [Char] -> ([(Int, Int)], [Int])  
    readInput =  
      (map readRange *** (map read . tail))  
        . break (== "")  
        . lines  
      where  
        readRange = (read *** (read . tail)) . break (== '-')  
    
    part1 (ranges, ids) = length $ filter (\id -> any (`inRange` id) ranges) ids  
    
    part2 (ranges, _) = sum $ map rangeSize $ foldl' addRange [] ranges  
      where  
        addRange [] x = [x]  
        addRange (r : rs) x  
          | touching r x = addRange rs $ merge r x  
          | otherwise = r : addRange rs x  
        touching (a, b) (c, d) = not (b < c - 1 || a > d + 1)  
        merge (a, b) (c, d) = (min a c, max b d)  
    
    main = do  
      input <- readInput <$> readFile "input05"  
      print $ part1 input  
      print $ part2 input  
    

  • Haskell

    Very simple, this one.

    import Data.List  
    import Data.Set qualified as Set  
    
    readInput s =  
      Set.fromDistinctAscList  
        [ (i, j) :: (Int, Int)  
          | (i, l) <- zip [0 ..] (lines s),  
            (j, c) <- zip [0 ..] l,  
            c == '@'  
        ]  
    
    accessible ps = Set.filter ((< 4) . adjacent) ps  
      where  
        adjacent (i, j) =  
          length . filter (`Set.member` ps) $  
            [ (i + di, j + dj)  
              | di <- [-1 .. 1],  
                dj <- [-1 .. 1],  
                (di, dj) /= (0, 0)  
            ]  
    
    main = do  
      input <- readInput <$> readFile "input04"  
      let removed =  
            (`unfoldr` input) $  
              \ps ->  
                case accessible ps of  
                  d  
                    | Set.null d -> Nothing  
                    | otherwise -> Just (Set.size d, ps Set.\\ d)  
      print $ head removed  
      print $ sum removed  
    

  • Version 2. I realized last night that my initial approach was way more complicated than it needed to be…

    import Data.List
    import Data.Semigroup
    
    maxJolt :: Int -> [Char] -> Int
    maxJolt r xs = read $ go r (length xs) xs
      where
        go r n xs =
          (\(Arg x xs) -> x : xs) . maximum $
            do
              (n', x : xs') <- zip (reverse [r .. n]) (tails xs)
              return . Arg x $ if r == 1 then [] else go (r - 1) (n' - 1) xs'
    
    main = do
      input <- lines <$> readFile "input03"
      mapM_ (print . sum . (`map` input) . maxJolt) [2, 12]
    

  • Haskell

    Yay, dynamic programming!

    import Data.Map qualified as Map  
    
    maxJolt :: Int -> [Char] -> Int  
    maxJolt r xs = read $ maximize r 0  
      where  
        n = length xs  
        maximize =  
          (curry . (Map.!) . Map.fromList . (zip <*> map (uncurry go)))  
            [(k, o) | k <- [1 .. r], o <- [r - k .. n - k]]  
        go k o =  
          maximum $ do  
            (x, o') <- drop o $ zip xs [1 .. n - (k - 1)]  
            return . (x :) $ if k == 1 then [] else maximize (k - 1) o'  
    
    main = do  
      input <- lines <$> readFile "input03"  
      mapM_ (print . sum . (`map` input) . maxJolt) [2, 12]  
    

  • Haskell

    Not much time for challenges right now sadly :/

    import Data.Bifunctor  
    import Data.IntSet qualified as IntSet  
    import Data.List.Split  
    
    repeats bound (from, to) = IntSet.elems $ IntSet.unions $ map go [2 .. bound l2]  
      where  
        l1 = length (show from)  
        l2 = length (show to)  
        go n =  
          let l = max 1 $ l1 `quot` n  
              start = if n > l1 then 10 ^ (l - 1) else read . take l $ show from  
           in IntSet.fromList  
                . takeWhile (<= to)  
                . dropWhile (< from)  
                . map (read . concat . replicate n . show)  
                $ enumFrom start  
    
    main = do  
      input <-  
        map (bimap read (read . tail) . break (== '-')) . splitOn ","  
          <$> readFile "input02"  
      let go bound = sum $ concatMap (repeats bound) input  
      print $ go (const 2)  
      print $ go id  
    


  • This is a bit of a cop-out answer, but the effects of HRT vary hugely based on the person. The “relief” you are feeling might be placebo, might be due to biochemical dysphoria, or a bit of both.

    When I was on injections, I definitely felt a bit crappy at the end of the week, and a lot better about 30 minutes after my dose.

    When I was getting my dose for patches worked out, I felt what I can only describe as “testosterone anxiety”, which persisted as I slowly bumped up my dose over a couple of weeks and eventually went away when I got up to three patches. (Now I’m on spiro and back down to two, and things are fine). This was a different feeling to injections wearing off.

    Now the only thing I notice is that I get four days of being really tired and bitchy pretty consistently every 25 days or so. I’m not going to speculate what that is, but since I’m on a very stable dose of two patches every two days, I don’t think it’s due to dosage.


  • Haskell

    Hmm. I’m still not very happy with part 3: it’s a bit slow and messy. Doing state over the list monad for memoization doesn’t work well, so I’m enumerating all possible configurations first and taking advantage of laziness.

    import Control.Monad  
    import Data.Bifunctor  
    import Data.Ix  
    import Data.List  
    import Data.Map (Map)  
    import Data.Map qualified as Map  
    import Data.Maybe  
    import Data.Set.Monad (Set)  
    import Data.Set.Monad qualified as Set  
    import Data.Tuple  
    
    type Pos = (Int, Int)  
    
    readInput :: String -> ((Pos, Pos), Pos, Set Pos, Set Pos)  
    readInput s =  
      let grid =  
            Map.fromList  
              [ ((i, j), c)  
                | (i, cs) <- zip [0 ..] $ lines s,  
                  (j, c) <- zip [0 ..] cs  
              ]  
       in ( ((0, 0), fst $ Map.findMax grid),  
            fst $ fromJust $ find ((== 'D') . snd) $ Map.assocs grid,  
            Set.fromList $ Map.keys (Map.filter (== 'S') grid),  
            Set.fromList $ Map.keys (Map.filter (== '#') grid)  
          )  
    
    moveDragon (i, j) = Set.mapMonotonic (bimap (+ i) (+ j)) offsets  
      where  
        offsets = Set.fromList ([id, swap] <*> ((,) <$> [-1, 1] <*> [-2, 2]))  
    
    dragonMoves bounds =  
      iterate (Set.filter (inRange bounds) . (>>= moveDragon)) . Set.singleton  
    
    part1 n (bounds, start, sheep, _) =  
      (!! n)  
        . map (Set.size . Set.intersection sheep)  
        . scanl1 Set.union  
        $ dragonMoves bounds start  
    
    part2 n (bounds, dragonStart, sheepStart, hideouts) =  
      (!! n)  
        . map ((Set.size sheepStart -) . Set.size)  
        . scanl'  
          ( \sheep eaten ->  
              (Set.\\ eaten)  
                . Set.mapMonotonic (first (+ 1))  
                . (Set.\\ eaten)  
                $ sheep  
          )  
          sheepStart  
        . map (Set.\\ hideouts)  
        $ (tail $ dragonMoves bounds dragonStart)  
    
    part3 (bounds, dragonStart, sheepStart, hideouts) =  
      count (dragonStart, sheepStart)  
      where  
        sheepStartByColumn = Map.fromList $ map swap $ Set.elems sheepStart  
        sheepConfigs =  
          map  
            ( (Set.fromList . catMaybes)  
                . zipWith (\j -> fmap (,j)) (Map.keys sheepStartByColumn)  
            )  
            . mapM  
              ( ((Nothing :) . map Just)  
                  . (`enumFromTo` (fst $ snd bounds))  
              )  
            $ Map.elems sheepStartByColumn  
        count =  
          ((Map.!) . Map.fromList . map ((,) <*> go))  
            ((,) <$> range bounds <*> sheepConfigs)  
        go (dragon, sheep)  
          | null sheep = 1  
          | otherwise =  
              (sum . map count) $ do  
                let movableSheep =  
                      filter (\(_, p) -> p /= dragon || Set.member p hideouts) $  
                        map (\(i, j) -> ((i, j), (i + 1, j))) $  
                          Set.elems sheep  
                    sheepMoves =  
                      if null movableSheep  
                        then [sheep]  
                        else do  
                          (p1, p2) <- movableSheep  
                          return $ Set.insert p2 $ Set.delete p1 sheep  
                sheep' <- sheepMoves  
                guard $ all (inRange bounds) sheep'  
                dragon' <- Set.elems $ moveDragon dragon  
                guard $ inRange bounds dragon'  
                let eaten = Set.singleton dragon' Set.\\ hideouts  
                return (dragon', sheep' Set.\\ eaten)  
    
    main = do  
      readFile "everybody_codes_e2025_q10_p1.txt" >>= print . part1 4 . readInput  
      readFile "everybody_codes_e2025_q10_p2.txt" >>= print . part2 20 . readInput  
      readFile "everybody_codes_e2025_q10_p3.txt" >>= print . part3 . readInput  
    




  • Haskell

    Not particularly optimized but good enough.

    import Control.Arrow ((***))  
    import Data.Array (assocs)  
    import Data.Function (on)  
    import Data.Graph  
    import Data.List  
    import Data.Map (Map)  
    import Data.Map qualified as Map  
    import Data.Maybe  
    
    readInput :: String -> Map Int [Char]  
    readInput = Map.fromList . map ((read *** tail) . break (== ':')) . lines  
    
    findRelations :: Map Int [Char] -> Graph  
    findRelations dna =  
      buildG (1, Map.size dna)  
        . concatMap (\(x, (y, z)) -> [(x, y), (x, z)])  
        . mapMaybe (\x -> (x,) <$> findParents x)  
        $ Map.keys dna  
      where  
        findParents x =  
          find (isChild x) $  
            [(y, z) | (y : zs) <- tails $ delete x $ Map.keys dna, z <- zs]  
        isChild x (y, z) =  
          all (\(a, b, c) -> a == b || a == c) $  
            zip3 (dna Map.! x) (dna Map.! y) (dna Map.! z)  
    
    scores :: Map Int [Char] -> Graph -> [Int]  
    scores dna relations =  
      [similarity x y * similarity x z | (x, [y, z]) <- assocs relations]  
      where  
        similarity i j =  
          length . filter (uncurry (==)) $ zip (dna Map.! i) (dna Map.! j)  
    
    part1, part2, part3 :: Map Int [Char] -> Int  
    part1 = sum . (scores <*> findRelations)  
    part2 = part1  
    part3 = sum . maximumBy (compare `on` length) . components . findRelations  
    
    main = do  
      readFile "everybody_codes_e2025_q09_p1.txt" >>= print . part1 . readInput  
      readFile "everybody_codes_e2025_q09_p2.txt" >>= print . part2 . readInput  
      readFile "everybody_codes_e2025_q09_p3.txt" >>= print . part3 . readInput  
    


  • Haskell

    Woo! I got on the leaderboard at last. I don’t think I’ve seen a problem like this one before, but fortunately it wasn’t as tricky as it seemed at first glance.

    import Control.Monad  
    import Data.List  
    import Data.List.Split  
    import Data.Tuple  
    
    readInput :: String -> [(Int, Int)]  
    readInput = map fixOrder . (zip <*> tail) . map read . splitOn ","  
      where  
        fixOrder (x, y)  
          | x > y = (y, x)  
          | otherwise = (x, y)  
    
    crosses (a, b) (c, d) =  
      not (a == c || a == d || b == c || b == d)  
        && ((a < c && c < b) /= (a < d && d < b))  
    
    part1 n = length . filter ((== n `quot` 2) . uncurry (-) . swap)  
    
    part2 n = sum . (zipWith countKnots <*> inits)  
      where  
        countKnots x strings = length $ filter (crosses x) strings  
    
    part3 n strings =  
      maximum [countCuts (a, b) | a <- [1 .. n - 1], b <- [a + 1 .. n]]  
      where  
        countCuts x = length $ filter (\s -> x == s || x `crosses` s) strings  
    
    main =  
      forM_  
        [ ("everybody_codes_e2025_q08_p1.txt", part1 32),  
          ("everybody_codes_e2025_q08_p2.txt", part2 256),  
          ("everybody_codes_e2025_q08_p3.txt", part3 256)  
        ]  
        $ \(input, solve) -> readFile input >>= print . solve . readInput  
    


  • Haskell

    A nice dynamic programming problem in part 3.

    import Data.List  
    import Data.List.Split  
    import Data.Map.Lazy qualified as Map  
    import Data.Maybe  
    
    readInput s =  
      let (names : _ : rules) = lines s  
       in (splitOn "," names, map readRule rules)  
      where  
        readRule s =  
          let [[c], post] = splitOn " > " s  
           in (c, map head $ splitOn "," post)  
    
    validBy rules name = all (`check` name) rules  
      where  
        check (c, cs) = all (`elem` cs) . following c  
        following c s = [b | (a : b : _) <- tails s, a == c]  
    
    part1 (names, rules) = fromJust $ find (validBy rules) names  
    
    part2 (names, rules) =  
      sum $ map fst $ filter (validBy rules . snd) $ zip [1 ..] names  
    
    part3 (names, rules) =  
      sum . map go . filter (validBy rules) $ dedup names  
      where  
        dedup xs =  
          filter (\x -> not $ any (\y -> x /= y && y `isPrefixOf` x) xs) xs  
        go n = count (length n) (last n)  
        gen 11 _ = 1  
        gen len c =  
          (if len >= 7 then (1 +) else id)  
            . maybe 0 (sum . map (count (len + 1)))  
            $ lookup c rules  
        count =  
          curry . (Map.!) . Map.fromList $  
            [ ((k, c), gen k c)  
              | k <- [1 .. 11],  
                c <- map fst rules ++ concatMap snd rules  
            ]  
    
    main = do  
      readFile "everybody_codes_e2025_q07_p1.txt" >>= putStrLn . part1 . readInput  
      readFile "everybody_codes_e2025_q07_p2.txt" >>= print . part2 . readInput  
      readFile "everybody_codes_e2025_q07_p3.txt" >>= print . part3 . readInput  
    

  • Haskell

    It took me an embarrassingly long time to figure out what was going on with this one.

    You could go a bit faster by splitting the list into beginning/middle/end parts, but I like the simplicity of this approach.

    import Control.Monad (forM_)  
    import Data.Char (toUpper)  
    import Data.IntMap.Strict qualified as IntMap  
    import Data.List (elemIndices)  
    import Data.Map qualified as Map  
    
    {-  
      f is a function which, given a lookup function and an index  
      returns the number of mentors for the novice at that position.  
      The lookup function returns the number of knights up to but  
      not including a specified position.  
    -}  
    countMentorsWith f input = Map.fromList [(c, go c) | c <- "abc"]  
      where  
        go c =  
          let knights = elemIndices (toUpper c) input  
              counts = IntMap.fromDistinctAscList $ zip knights [1 ..]  
              preceding = maybe 0 snd . (`IntMap.lookupLT` counts)  
           in sum $ map (f preceding) $ elemIndices c input  
    
    part1 = (Map.! 'a') . countMentorsWith id  
    
    part2 = sum . countMentorsWith id  
    
    part3 d r = sum . countMentorsWith nearby . concat . replicate r  
      where  
        nearby lookup i = lookup (i + d + 1) - lookup (i - d)  
    
    main =  
      forM_  
        [ ("everybody_codes_e2025_q06_p1.txt", part1),  
          ("everybody_codes_e2025_q06_p2.txt", part2),  
          ("everybody_codes_e2025_q06_p3.txt", part3 1000 1000)  
        ]  
        $ \(input, solve) -> readFile input >>= print . solve  
    




  • 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