Initial commit

This commit is contained in:
2022-12-06 16:09:51 -05:00
commit b53a8a48ee
32 changed files with 7450 additions and 0 deletions

28
src/Day1Lib.hs Normal file
View File

@@ -0,0 +1,28 @@
module Day1Lib
( day1,
day1Two,
convertToString
) where
import Data.List
import Data.List.Split
convertToInt :: [String] -> [Int]
convertToInt = map read
convertToString :: Int -> String
convertToString = show
day1 :: String -> Int
day1 input = foldl max 0 summedList
where
summedList = map sum splitInputAsInt
splitInputAsInt = map (map read) splitInput :: [[Int]]
splitInput = map words $ splitOn "\n\n" input
day1Two :: String -> Int
day1Two input = sum $ take 3 $ reverse $ sort summedList
where
summedList = map sum splitInputAsInt
splitInputAsInt = map (map read) splitInput :: [[Int]]
splitInput = map words $ splitOn "\n\n" input

81
src/Day2Lib.hs Normal file
View File

@@ -0,0 +1,81 @@
module Day2Lib
( day2,
day2Alternate
) where
import Data.List.Split
data RPSMove = Rock | Paper | Scissors deriving Eq
data WinState = Lose | Draw | Win
type RoundResult = (RPSMove, WinState)
scoreMove :: RPSMove -> Int
scoreMove Rock = 1
scoreMove Paper = 2
scoreMove Scissors = 3
scoreWinState :: WinState -> Int
scoreWinState Lose = 0
scoreWinState Draw = 3
scoreWinState Win = 6
scoreRound :: RoundResult -> Int
scoreRound (move, winstate) = scoreMove move + scoreWinState winstate
playRound :: (RPSMove, RPSMove) -> RoundResult
playRound (theirMove, myMove)
| theirMove == myMove = (myMove, Draw)
| theirMove == Paper && myMove == Scissors = (myMove, Win)
| theirMove == Rock && myMove == Paper = (myMove, Win)
| theirMove == Scissors && myMove == Rock = (myMove, Win)
| otherwise = (myMove, Lose)
winRound :: (RPSMove, WinState) -> RoundResult
winRound (theirMove, Win)
| theirMove == Rock = (Paper, Win)
| theirMove == Paper = (Scissors, Win)
| theirMove == Scissors = (Rock, Win)
winRound (theirMove, Lose)
| theirMove == Rock = (Scissors, Lose)
| theirMove == Paper = (Rock, Lose)
| theirMove == Scissors = (Paper, Lose)
winRound (theirMove, Draw) = (theirMove, Draw)
parseMoves :: (String, String) -> (RPSMove, RPSMove)
parseMoves (a, x) = (parseTheirMove a, parseMyMove x)
parseTheirMove :: String -> RPSMove
parseTheirMove a
| a == "A" = Rock
| a == "B" = Paper
| a == "C" = Scissors
parseMyMove :: String -> RPSMove
parseMyMove x
| x == "X" = Rock
| x == "Y" = Paper
| x == "Z" = Scissors
parseMoveAndDesiredWinState :: (String, String) -> (RPSMove, WinState)
parseMoveAndDesiredWinState (a, x) = (parseTheirMove a, parseMyDesiredWinState x)
parseMyDesiredWinState :: String -> WinState
parseMyDesiredWinState x
| x == "X" = Lose
| x == "Y" = Draw
| x == "Z" = Win
tuplify2 :: [a] -> (a,a)
tuplify2 [x,y] = (x,y)
parseInput :: String -> [(RPSMove, RPSMove)]
parseInput i = map (parseMoves) $ map tuplify2 $ map (splitOn " ") $ lines i
parseInputAlternate :: String -> [(RPSMove, WinState)]
parseInputAlternate i = map (parseMoveAndDesiredWinState) $ map tuplify2 $ map (splitOn " ") $ lines i
day2 :: String -> Int
day2 input = sum $ map scoreRound $ map playRound $ parseInput input
day2Alternate :: String -> Int
day2Alternate input = sum $ map scoreRound $ map winRound $ parseInputAlternate input

48
src/Day3Lib.hs Normal file
View File

@@ -0,0 +1,48 @@
module Day3Lib
( day3,
day3Alternate
) where
import Data.List (elemIndex, intersect, nub)
import Data.Maybe
type RucksackItems = [Char]
type RucksackItem = Char
type ElfGroup = (RucksackItems, RucksackItems, RucksackItems)
priority :: RucksackItem -> Int
priority i
| i `elem` lowercases = (fromJust $ elemIndex i lowercases) + 1
| i `elem` uppercases = (fromJust $ elemIndex i uppercases) + 27
| otherwise = 0
where
lowercases = "abcdefghijklmnopqrstuvwxyz"
uppercases = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
intersectingItems :: (RucksackItems, RucksackItems) -> RucksackItems
intersectingItems (r1, r2) = r1 `intersect` r2
parseLine :: String -> (RucksackItems, RucksackItems)
parseLine l = (take halfLength l, drop halfLength l)
where
halfLength = length l `div` 2
parseInput :: String -> [(RucksackItems, RucksackItems)]
parseInput i = map parseLine $ lines i
day3 :: String -> Int
day3 input = sum $ map priority $ concat $ map nub $ map intersectingItems $ parseInput input
parseInputAlternate :: String -> [ElfGroup]
parseInputAlternate i = breakIntoGroups $ lines i
breakIntoGroups :: [String] -> [ElfGroup]
breakIntoGroups [] = []
breakIntoGroups (a:b:c:rest) = (a, b, c) : breakIntoGroups rest
processCommonItems :: Eq a => ([a], [a], [a]) -> [a]
processCommonItems (a,b,c) = intersect c $ intersect a b
day3Alternate :: String -> Int
day3Alternate input = sum $ map priority $ concat $ map nub $ map processCommonItems $ parseInputAlternate input

45
src/Day4Lib.hs Normal file
View File

@@ -0,0 +1,45 @@
module Day4Lib
( day4,
day4Alternate
) where
import Data.List (intersect, isSubsequenceOf)
import Data.List.Split (splitOn)
type SectionId = Int
type Range = (SectionId, SectionId)
type RangePair = (Range, Range)
checkRangePairForPartialOverlap :: RangePair -> Bool
checkRangePairForPartialOverlap (r1, r2) = intersection
where
processedR1 = processRange r1
processedR2 = processRange r2
-- Get the intersection, convert it into a bool based on if it's empty, then negate the bool
intersection = not $ null $ processedR1 `intersect` processedR2
checkRangePairForFullOverlap :: RangePair -> Bool
checkRangePairForFullOverlap (r1, r2) = processedR1 `isSubsequenceOf` processedR2 || processedR2 `isSubsequenceOf` processedR1
where
processedR1 = processRange r1
processedR2 = processRange r2
processRange :: Range -> [SectionId]
processRange (s, e) = [s..e]
tuplify2 :: [a] -> (a,a)
tuplify2 [x,y] = (x, y)
parseInput :: String -> [RangePair]
parseInput i = map tuplify2 $ map (map tuplify2) $ map (map (map readAsInt)) $ map (map splitOnDash) $ map splitOnComma $ lines i
where
inputAsLines = lines i
splitOnComma = splitOn ","
splitOnDash = splitOn "-"
readAsInt r = read r :: Int
day4 :: String -> Int
day4 input = sum $ map fromEnum $ map checkRangePairForFullOverlap $ parseInput input
day4Alternate :: String -> Int
day4Alternate input = sum $ map fromEnum $ map checkRangePairForPartialOverlap $ parseInput input

102
src/Day5Lib.hs Normal file
View File

@@ -0,0 +1,102 @@
module Day5Lib where
import Data.List (transpose)
import Data.List.Split
import Data.Char (isAlpha, isDigit)
type Crate = Char
type CrateStack = [Char]
type Quantity = Int
type CrateStackId = Int
type Instruction = (Quantity, CrateStackId, CrateStackId)
type Problem = ([CrateStack], [Instruction])
applyInstruction :: [CrateStack] -> Instruction -> [CrateStack]
applyInstruction cs (0, csi1, csi2) = cs
applyInstruction cs (q, csi1, csi2) = applyInstruction finalList (q - 1, csi1, csi2)
where
prior1 = fst $ splitAt (csi1 - 1) cs
post1 = snd $ splitAt (csi1) cs
-- Decrement by one to account for zero indexing
newCsi1 = tail (cs !! (csi1 - 1))
movedCrate = head (cs !! (csi1 - 1))
removedList = prior1 ++ (newCsi1 : post1)
prior2 = fst $ splitAt (csi2 - 1) removedList
post2 = snd $ splitAt (csi2) removedList
newCsi2 = movedCrate : (cs !! (csi2 - 1))
finalList = prior2 ++ (newCsi2 : post2)
applyInstructionAlternate :: [CrateStack] -> Instruction -> [CrateStack]
applyInstructionAlternate cs (q, csi1, csi2) = finalList
where
prior1 = fst $ splitAt (csi1 - 1) cs
post1 = snd $ splitAt (csi1) cs
-- Decrement by one to account for zero indexing
newCsi1 = drop q (cs !! (csi1 - 1))
movedCrates = take q (cs !! (csi1 - 1))
removedList = prior1 ++ (newCsi1 : post1)
prior2 = fst $ splitAt (csi2 - 1) removedList
post2 = snd $ splitAt (csi2) removedList
newCsi2 = movedCrates ++ (removedList !! (csi2 - 1))
finalList = prior2 ++ (newCsi2 : post2)
solveProblem :: Problem -> [CrateStack]
solveProblem (cs, []) = cs
solveProblem (cs, (ins:inss)) = solveProblem ((applyInstruction cs ins), inss)
solveProblemAlternate :: Problem -> [CrateStack]
solveProblemAlternate (cs, []) = cs
solveProblemAlternate (cs, (ins:inss)) = solveProblemAlternate ((applyInstructionAlternate cs ins), inss)
getTopCrates :: [CrateStack] -> String
getTopCrates cs = map myBespokeHead cs
-- better head, because I'm better lmao
myBespokeHead :: [Char] -> Char
myBespokeHead [] = ' '
myBespokeHead (x:_) = x
processInstruction :: String -> Instruction
processInstruction i = tuplify3 $ map convertToInt filteredString
where
-- Filter the string to only numeric characters, which compose the instruction and appear in order
filteredString = filter (all isDigit) $ words i
stringIsNumber :: String -> Bool
stringIsNumber s = all isDigit s
convertToInt :: String -> Int
convertToInt s = read s :: Int
tuplify3 :: [a] -> (a,a,a)
tuplify3 [x,y,z] = (x, y, z)
processCrates :: String -> [CrateStack]
processCrates i = map (filter isAlpha) $ filter validLine $ transpose $ lines i
where
-- After transposing this function is used to filter for valid lines
validLine x = not $ null $ filter isDigit x
parseInput :: String -> Problem
parseInput i = (processCrates crates, map processInstruction instructions)
where
splitInput = splitOn "\n\n" i
crates = head splitInput
instructions = concat $ map lines $ tail splitInput
day5 :: String -> String
day5 input = getTopCrates solvedState
where
parsedInput = parseInput input
solvedState = solveProblem parsedInput
day5Alternate :: String -> String
day5Alternate input = getTopCrates solvedState
where
parsedInput = parseInput input
solvedState = solveProblemAlternate parsedInput