diff --git a/.vscode/settings.json b/.vscode/settings.json index 4bf771e..a270e0d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,9 @@ { "cSpell.words": [ + "Bifunctor", + "bimap", "Collatz", + "Combinators", "concat", "coprime", "elems", diff --git a/src/advent_of_code/2023/1.hs b/src/advent_of_code/2023/1.hs new file mode 100644 index 0000000..6742a06 --- /dev/null +++ b/src/advent_of_code/2023/1.hs @@ -0,0 +1,133 @@ +-- https://adventofcode.com/2023/day/1 + +import Data.Bifunctor (bimap) +import Data.Maybe (fromMaybe) +import Text.ParserCombinators.ReadP (ReadP, choice, many, readP_to_S, + string, (<++)) + +main :: IO () +main = do + entries <- lines <$> readFile "src/advent_of_code/2023/input1" + print "Advent of Code 2022 - Day 4" + -- print entries + + print ("Part 1: " <> show (solveP1 entries)) + print ("Part 2: " <> show (solveP2 entries)) + + +-- +-- Part 1 +-- + +solveP1 :: [String] -> Int +solveP1 = sum . map solveP1Line + +solveP1Line :: String -> Int +solveP1Line = sumPairS . bimap head last . doubleString . filter isNumP1 . tokenized tokensP1 + +tokensP1 :: ReadP String +tokensP1 = parseNumberChars <++ parseChars + +-- +-- Part 2 +-- + +solveP2 :: [String] -> Int +solveP2 = sum . map solveP2Line + +solveP2Line :: String -> Int +solveP2Line = sumPairS . bimap a b . doubleString + where + a = head . condense . fromLeft + b = last . condense . fromRight + -- Need to parse from both sides + -- "oneight" should return 18, not 11 + + condense = map stringToNum . filter isNumP2 + fromLeft = tokenized tokensP2 + fromRight = map reverse . reverse . tokenized tokensP2' . reverse + +tokensP2 :: ReadP String +tokensP2 = parseNumberWords <++ parseNumberChars <++ parseChars + +tokensP2' :: ReadP String +tokensP2' = parseNumberWords' <++ parseNumberChars <++ parseChars + +solveP2LineBad :: String -> Int +solveP2LineBad = sumPairS . bimap head last . doubleString . map stringToNum . filter isNumP2 . tokenized tokensP2 + +-- +-- Utility functions +-- + +-- Doing this because I want to use a bifunctor +doubleString :: str -> (str, str) +doubleString str = (str, str) + +sumPairS :: (String, String) -> Int +sumPairS (x,y) = read (x ++ y) + +stringToNum :: String -> String +stringToNum str = fromMaybe str (lookup str $ zip numberWords numberChars) + +-- +-- Boilerplate equality checks +-- Now condensed +-- + +isNumP1 :: String -> Bool +isNumP1 = flip elem numberChars + +isNumP2 :: String -> Bool +isNumP2 = flip elem (numberChars ++ numberWords) + +-- +-- Parsing for question +-- + +numberWords :: [String] +numberWords = ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"] + +numberChars :: [String] +numberChars = map show [0..9] + +letterChars :: String +letterChars = ['a'..'z'] + +parseNumberWords :: ReadP String +parseNumberWords = choice $ map string numberWords + +parseNumberWords' :: ReadP String +parseNumberWords' = choice $ map (string . reverse) numberWords + +parseNumberChars :: ReadP String +parseNumberChars = choice $ map string numberChars + +parseChars :: ReadP String +parseChars = choice $ map (\c -> string [c]) letterChars + +tokenized :: ReadP a -> String -> [a] +tokenized tokenSet = fst . last . readP_to_S (many tokenSet) + +-- +-- Examples +-- + +example1 :: [String] +example1 = + [ "1abc2" + , "pqr3stu8vwx" + , "a1b2c3d4e5f" + , "treb7uchet" + ] + +example2 :: [String] +example2 = + [ "two1nine" + , "eightwothree" + , "abcone2threexyz" + , "xtwone3four" + , "4nineeightseven2" + , "zoneight234" + , "7pqrstsixteen" + ]