13 Haskell

Part I Assignment

Haskell (GHC) is installed by sudo apt install haskell-platform -y. Then the interpreter is run by ghci.

A script test.hs is compiled by ghc -o test test.hs and then run as usual ./test.

The write-compile-run is then accomplished with ghc -o 13 13.hs && ./13.

This is actually pretty elegant solution in my opinion.

Part I Assignment

-- we need isDigit function
import Data.Char

-- convert text into a list of lines
process_text :: String -> [String]
-- lines is built-in function
process_text text = lines text

-- read integer from a string and convert it to integer
-- return also the rest of the string
readn :: (String, String) -> (Int, String)
readn (s,[]) = (read s :: Int, [])
readn (s,(x:xs)) =
    if isDigit x
    then readn (s ++ [x], xs)
    else (read s :: Int, x:xs)

-- extract integer from string, wrap it into [] and
-- put back into the string
wrapn :: String -> String
wrapn [] = []
wrapn x =
    let (n, rest) = readn ([], x)
    in "[" ++ show n ++ "]" ++ rest

-- split list of lines into pairs of strings
-- omit empty lines
parse :: [String] -> [(String,String)]
parse [] = []
parse (x:y:xs) =
    if x == []
    then parse (y : xs)
    else [(x, y)] ++ parse xs

-- compare two strings
-- right order => 1 wrong order => 0
cmpp :: (String, String) -> Int
cmpp ([],[]) = 1
cmpp ([x],[]) = 0
cmpp ([],[y]) = 1
cmpp (x:xs,y:ys) =
    if isDigit x && isDigit y -- both numbers
    -- both first chars are digits, compare the numbers
    then let (ln, lr) = readn ([], x:xs)
             (rn, rr) = readn ([], y:ys)
         in
         if ln < rn
         -- left number < right number
         then 1
         else if ln == rn
              -- left number == right number
              then cmpp (lr, rr)
              -- left number > right number
              else 0
    else if x == y
         -- the same symbol, continue with the suffix
         then cmpp (xs, ys)
         else if [x] == "]"
              -- left is shorter
              then 1
              else if [y] == "]"
                   -- right is shorter
                   then 0
                   else if isDigit x && [y] == "["
                        -- wrap left number into brackets and continue
                        then cmpp (wrapn (x:xs), y:ys)
                        else if isDigit y && [x] == "["
                             -- wrap right number into brackets and continue
                             then cmpp (x:xs, wrapn (y:ys))
                             else 1

-- if the packets are in the right order
-- multiply the packet number (second argument) with the compare result
-- and increase the sum (first argument)
packet_list :: Int -> Int -> [(String,String)] -> Int
packet_list a b [] = a
packet_list a b (x:xs) = packet_list (a + b * (cmpp x)) (b + 1) xs

-- main
main :: IO ()
main = do
   content <- readFile "13.input"
   -- first get lines, then split into packet pairs
   -- then process the list and finally print the result
   print $ packet_list 0 1 (parse $ process_text content)

Part II

import Data.List
import Data.Char
import Data.Maybe

process_text :: String -> [String]
process_text text = lines text

readn :: (String, String) -> (Int, String)
readn (s,[]) = (read s :: Int, [])
readn (s,(x:xs)) =
    if isDigit x
    then readn (s ++ [x], xs)
    else (read s :: Int, x:xs)

wrapn :: String -> String
wrapn [] = []
wrapn x =
    let (n, rest) = readn ([], x)
    in "[" ++ show n ++ "]" ++ rest

-- now we convert lines into a list of packets
parse :: [String] -> [String]
parse [] = []
parse (x:xs) =
    if x == []
    then parse xs
    else [x] ++ parse xs

-- now this function returns Ordering (LT and GT)
-- so we can use it to sort array
cmpp :: String -> String -> Ordering
cmpp [] [] = LT
cmpp [x] [] = GT
cmpp [] [y] = LT
cmpp (x:xs) (y:ys) =
    if isDigit x && isDigit y
    then let (ln, lr) = readn ([], x:xs)
             (rn, rr) = readn ([], y:ys)
         in
         if ln < rn
         then LT
         else if ln == rn
              then cmpp lr rr
              else GT
    else if x == y
         then cmpp xs ys
         else if [x] == "]"
              then LT
              else if [y] == "]"
                   then GT
                   else if isDigit x && [y] == "["
                        then cmpp (wrapn (x:xs)) (y:ys)
                        else if isDigit y && [x] == "["
                             then cmpp (x:xs) (wrapn (y:ys))
                             else LT

-- helper function to convert Maybe Int a to a + 1
indInt :: Maybe Int -> Int
indInt x = (fromMaybe 1 x) + 1

main :: IO ()
main = do
    content <- readFile "13.input"
    -- parse lines into a pa
    let packets = sortBy cmpp
            (["[[2]]"] ++ ["[[6]]"] ++ parse (process_text content))
        in
        print $
        (indInt (elemIndex "[[2]]" packets)) * (indInt (elemIndex "[[6]]" packets))

Materials

What I learned

published: 2022-12-13
last modified: 2023-01-21

https://vit.baisa.cz/notes/code/advent-of-code-2022/13/