🏡 index : ~doyle/aoc.git

#!/usr/bin/env nix-shell
#!nix-shell --pure -i "runghc -- -i../" -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ ordered-containers ])"

import Aoc (readAndParseStdin)
import Data.Char (ord)
import qualified Data.Map as Map
import qualified Data.Map.Ordered as OM
import Text.Parsec (char, choice, digit, many, many1, noneOf, parse, sepBy)
import Text.Parsec.String (Parser)

main = do
  input <- readAndParseStdin parseInput
  print $ part1 input
  print $ part2 input

part1 :: [String] -> Int
part1 input = sum $ map hash input

part2 :: [String] -> Int
part2 input = calcScore $ foldl folder (Map.fromList (zip [0 ..] $ replicate 256 OM.empty)) input
  where
    calcScore :: Map.Map Int (OM.OMap String Int) -> Int
    calcScore = Map.foldrWithKey (\i v acc -> acc + ((i + 1) * sum (zipWith (*) [1 ..] (map snd $ OM.assocs v)))) 0
    folder :: Map.Map Int (OM.OMap String Int) -> String -> Map.Map Int (OM.OMap String Int)
    folder acc input =
      let (label, add, focalLength) = parsePart2Input input
          hashedLabel = hash label
          updateBox = OM.alter (\_ -> if add then Just focalLength else Nothing) label
       in Map.adjust updateBox hashedLabel acc

hash :: String -> Int
hash = foldl (\acc c -> ((acc + ord c) * 17) `rem` 256) 0

parsePart2Input :: String -> (String, Bool, Int)
parsePart2Input input = case parse doParse "" input of
  Left parseError -> error $ show parseError
  Right doc -> doc
  where
    doParse = do
      label <- many1 (noneOf "-=")
      operation <-
        choice
          [ True <$ char '=',
            False <$ char '-'
          ]
      focalLength <- read <$> many digit
      return (label, operation, focalLength)

parseInput :: Parser [String]
parseInput = many1 (noneOf ",\n") `sepBy` char ','