Advent of Code

Advent of Code 2021 Solutions in Haskell: Day 1

Ritvij Srivastava

--

Recently, I had the chance to peek through the wonderful world of functional programming — I started learning Haskell! And what better way to practice Haskell, than to take part in the yearly Christmas programming event — Advent of Code.

Advent of Code is an Advent calendar of small programming puzzles for a variety of skill sets and skill levels that can be solved in any programming language you like. People use them as a speed contest, interview prep, company training, university coursework, practice problems, or to challenge each other.

Advent of Code lasts 25 days. Each day consists of two parts. Solving part 1 unlocks part 2.

So, without further ado, lets start!

Lets Start GIF by Peloton (https://giphy.com/gifs/onepeloton-peloton-rad-lopez-dynzluggitvMJ6P45c)

Note: All my code can be found here.

Part 1

Count the number of times a depth measurement increases from the previous measurement.

Basically, we have to count the number of values that are greater than their immediate left value in the list.

Lets say that the input is: [10,3,4,2,8,20,23]

Here the answer will be : 4, since 4>3 , 8>2, 20>8, and 23>20.

Lets start by thinking about the function declaration. We need to take in a List of integers and return an integer. So, we get:

valsLargerThanPreviousVal :: [Int] -> Int

Now, what information do we need to decide whether we should consider the current value or not ? We just need the previous value in the list.

We can pattern match on the list to achieve that:

valsLargerThanPreviousVal (x : y : ys)
| y > x = 1 + valsLargerThanPreviousVal (y : ys)
| otherwise = valsLargerThanPreviousVal (y : ys)

Adding base cases for empty list and list with a single element we get:

valsLargerThanPreviousVal :: [Int] -> Int
valsLargerThanPreviousVal [] = 0
valsLargerThanPreviousVal [_x] = 0
valsLargerThanPreviousVal (x : y : ys)
| y > x = 1 + valsLargerThanPreviousVal (y : ys)
| otherwise = valsLargerThanPreviousVal (y : ys)

The only thing left now is to convert take input from file, convert it into a list of integers and pass it to the above function to get the final result.

I used getContents from System.IO package to take input from the file. My main looks like this:

main :: IO ()
main = do
fileContents <- getContents
let list = (\x -> read x :: Int) <$> words fileContents
let resultForPartOne = valsLargerThanPreviousVal list
print resultForPartOne

Combining everything together, I get:

valsLargerThanPreviousVal :: [Int] -> Int
valsLargerThanPreviousVal [] = 0
valsLargerThanPreviousVal [_x] = 0
valsLargerThanPreviousVal (x : y : ys)
| y > x = 1 + valsLargerThanPreviousVal (y : ys)
| otherwise = valsLargerThanPreviousVal (y : ys)
main :: IO ()
main = do
fileContents <- getContents
let list = (\x -> read x :: Int) <$> words fileContents
let resultForPartOne = valsLargerThanPreviousVal list
print resultForPartOne

Yeah! We are done with Part 1. Lets move on to Part 2.

Part 2

Count the number of times the sum of measurements in this sliding window increases from the previous sum, where the sliding window size is 3.

Lets take an example to understand the problem.

Suppose our input is [3,2,1,5,7,4,1]. Now, lets see what the group of three elements will look like:

We get 5 groups of 3 elements each. They are:
A = [3,2,1] = 3 + 2 + 1 = 6
B = [2,1,5] = 2 + 1 + 5 = 8
C = [1,5,7] = 1 + 5 + 7 = 13
D = [5,7,4] = 5 + 7 + 4 = 16
E = [7,4,1] = 7 + 4 + 1 = 12

We can make another list out of the groups we got: [6,8,13,16,12]. Now, we can see that the questions has boiled down to Count the number of elements that are larger than the value on their immediate left.

Here, the answer would be 3, as, 8>6, 13>8, and 16>13.

Since we have already solved how to count number of elements that are larger than the value on their immediate left in Part 1, all we really need to do is alter a given list into a list where each element represents a sum of three consecutive elements in a sliding window.

We can again make use of pattern matching to achieve this:

alterList :: [Int] -> [Int]
alterList (x : y : z : zs) = (x + y + z) : alterList (y : z : zs)

Adding in base cases, we get:

alterList :: [Int] -> [Int]
alterList [] = []
alterList [_x] = []
alterList [_x, _y] = []
alterList (x : y : z : zs) = (x + y + z) : alterList (y : z : zs)

Now, all we need to do is combine this with our solution from Part 1.

Our final code is:

valsLargerThanPreviousVal :: [Int] -> Int
valsLargerThanPreviousVal [] = 0
valsLargerThanPreviousVal [_x] = 0
valsLargerThanPreviousVal (x : y : ys)
| y > x = 1 + valsLargerThanPreviousVal (y : ys)
| otherwise = valsLargerThanPreviousVal (y : ys)
alterList :: [Int] -> [Int]
alterList [] = []
alterList [_x] = []
alterList [_x, _y] = []
alterList (x : y : z : zs) = (x + y + z) : alterList (y : z : zs)
main :: IO ()
main = do
fileContents <- getContents
let list = (\x -> read x :: Int) <$> words fileContents

let alteredList = alterList list
let resultForPartTwo = valsLargerThanPreviousVal alteredList
print resultForPartTwo

Voila! We are done with Day 1!

We did it GIF by chuber channel (https://giphy.com/gifs/chuber-turtle-franklin-we-did-it-l3vR1Orv4xDgXX7Z6)

--

--

Ritvij Srivastava

I am a budding developer interested in all things tech. Currently exploring the beautiful world of functional programming through Haskell.