Skip to content

Latest commit

 

History

History

Day_03

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

Day 03

🎒

Well, here's an easy one! Just finding a common item between some lists!

module Main where

import Data.Char
import Data.List

getPriority :: Char -> Int
getPriority c
    | 'a' <= c && c <= 'z' = ord c - ord 'a' + 1
    | otherwise            = ord c - ord 'A' + 27

splitMiddle :: [a] -> ([a], [a])
splitMiddle l = splitAt (length l `div` 2) l

chunk :: Int -> [a] -> [[a]]
chunk n = takeWhile (not . null) . map (take n) . iterate (drop n)

priorityOfCommonItem :: (String, String) -> Int
priorityOfCommonItem = getPriority . head . uncurry intersect

priorityOfBadge :: [String] -> Int
priorityOfBadge = getPriority . head . foldl1 intersect

main = do
    input <- lines <$> readFile "input"
    print $ sum $ map (priorityOfCommonItem . splitMiddle) input
    print $ sum $ map priorityOfBadge $ chunk 3 input

I don't think I really need to explain the first two functions, they're quite self-explanatory in my opinion.

So let's first dive into the most obscure one: chunk!

chunk :: Int -> [a] -> [[a]]
chunk n = takeWhile (not . null) . map (take n) . iterate (drop n)

Well, I have a confession to make, I didn't write that one 😿 I took it from the Haskell Wiki However I can still explain it!

First of all, what it does is that it splits a list into chunks of size n. For example chunk 3 [1, 2, 3, 4, 5, 6] == [[1, 2, 3], [4, 5, 6]].

The way it does that is by iterating the list by dropping n elements each time. So the first iteration is [1, 2, 3, 4, 5, 6], the second is [4, 5, 6] and the next ones are [].

Then for each iteration it takes the first n elements, so the first iteration will give [1, 2, 3], the second [4, 5, 6] and the next ones [].

Then finally, we take all these until we find an empty list, giving us [[1, 2, 3], [4, 5, 6]], because the first iteration gives [1, 2, 3], the second [4, 5, 6] and the third [].

Okay, now that this function has been explained, let's go onto the next one:

priorityOfCommonItem :: (String, String) -> Int
priorityOfCommonItem = getPriority . head . uncurry intersect

This takes a tuple containing two parts of a string (the two halves of the rucksack) It is going to first compute the intersection (in the mathematical sense of these two parts. Notice that the uncurry function here is to transform intersect :: [a] -> [a] -> [a] into intersect ([a], [a]) -> [a]. Then we just retrieve the first (and only) element of this intersection and we get its priority.

priorityOfBadge :: [String] -> Int
priorityOfBadge = getPriority . head . foldl1 intersect

This one is almost identical, except it doesn't take a tuple but a list of string. In fact, it is so identical that I could have refactored my code to only have this function (by having splitMiddle returning a list instead of a couple for example), but whatever. Here foldl1 is going to compute the intersection of the first two strings, then the intersection of that intersection and the third string (and so on and so forth if there were more strings, which isn't the case).

And finally the main function is nothing too fancy

main = do
    input <- lines <$> readFile "input"
    print $ sum $ map (priorityOfCommonItem . splitMiddle) input
    print $ sum $ map priorityOfBadge $ chunk 3 input

We get the lines of the input, for each line we split in the middle and get the priority of the common item, before summing all these priorities and printing it for part1, and for part2 we do the same thing but instead of splitting each line in the middle we group them in groups of 3.