It’s that time of year again.
You can try out the solutions here .
Day 1
module Main where
import Common (parseFile)
import Control.Applicative ((<|>))
import Data.List (foldl', scanl)
import Data.Text qualified as T
import Data.Void (Void)
import Text.Megaparsec
import Text.Megaparsec.Char
import Text.Megaparsec.Char.Lexer qualified as Lex
import Text.Printf
data Dir = L | R
deriving (Show, Eq)
data Rot = Rot Dir Int
deriving (Show, Eq)
type Parser = Parsec Void T.Text
dirSign :: Dir -> Int
dirSign L = -1
dirSign R = 1
parseRot :: Parser Rot
parseRot = Rot <$> (L <$ char 'L' <|> R <$ char 'R') <*> Lex.decimal
solve1 :: [Rot] -> Int
solve1 rots = length $ filter (== 0) $ scanl update 50 rots
where
update pos (Rot d x) = (pos + (dirSign d) * x) `mod` 100
solve2 :: [Rot] -> Int
solve2 rots = snd $ foldl' expand (50, 0) rots
where
expand (pos, count) (Rot d x) =
let steps = [pos + dirSign d * n | n <- [1 .. x]]
in ((pos + dirSign d * x) `mod` 100, count + length (filter (\p -> p `mod` 100 == 0) steps))
main :: IO ()
main = do
sampleRots <- parseFile (parseRot `sepEndBy` newline) "input/day01_sample.txt"
rots <- parseFile (parseRot `sepEndBy` newline) "input/day01.txt"
printf "Part 1 Sample answer: %s\n" (show $ solve1 sampleRots)
printf "Part 1 Final answer: %s\n" (show $ solve1 rots)
printf "Part 2 Sample answer: %s\n" (show $ solve2 sampleRots)
printf "Part 2 Final answer: %s\n" (show $ solve2 rots)
Yes, I know part 2’s solution is inefficient—it’s prettier this way.
Day 2
module Main where
import Common
import Text.Megaparsec
import Text.Megaparsec.Char
import Text.Megaparsec.Char.Lexer qualified as Lex
import Text.Printf
parseIds :: Parser [(Int, Int)]
parseIds =
pair `sepBy` char ',' <* optional eol <* eof
where
pair = (,) <$> Lex.decimal <* char '-' <*> Lex.decimal
isInvalid1 :: String -> Bool
isInvalid1 s = (take halfLen s) == (drop halfLen s)
where
halfLen = length s `div` 2
isInvalid2 :: String -> Bool
isInvalid2 s =
any allEqual [1 .. length s `div` 2]
where
allEqual k =
case partition k s of
[] -> False
(x : xs) -> all (== x) xs
partition :: Int -> String -> [String]
partition n s
| n <= 0 = []
| length s `mod` n /= 0 = []
| otherwise = go s
where
go [] = []
go xs = take n xs : go (drop n xs)
solve :: (String -> Bool) -> [(Int, Int)] -> Int
solve valid =
sum
. filter (valid . show)
. concatMap (uncurry enumFromTo)
main = do
sampleIds <- parseFile parseIds "./input/day02_sample.txt"
ids <- parseFile parseIds "./input/day02.txt"
printf "Sample Part 1: %d\n" $ solve isInvalid1 sampleIds
printf "Part 1: %d\n" $ solve isInvalid1 ids
printf "Sample Part 2: %d\n" $ solve isInvalid2 sampleIds
printf "Part 2: %d\n" $ solve isInvalid2 ids
Chef’s kiss.