aboutsummaryrefslogtreecommitdiff
path: root/2020/11-Seating_System/first-arrays.hs
blob: fac81518c342785a42c97b186ae497904497ea4f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
module Main (main) where
import Control.Monad (when)
import Data.Array qualified as A
import Data.List (foldl')
import Data.Maybe (catMaybes)
import System.Exit (die)

exampleExpectedOutput = 37

type Seating = A.Array (Int, Int) (Maybe Bool)

inputToArrayAssociations :: String -> [((Int, Int), Maybe Bool)]
inputToArrayAssociations = snd . foldl'  nextSeat  ((0, 0), [])
  where
    nextSeat :: ((Int, Int), [((Int, Int), Maybe Bool)]) -> Char -> ((Int, Int), [((Int, Int), Maybe Bool)])
    nextSeat ((x, y), acc) '\n' = ((0, y+1), acc)
    nextSeat ((x, y), acc) '.' = ((x+1, y), acc ++ [((x, y), Nothing)])
    nextSeat ((x, y), acc) 'L' = ((x+1, y), acc ++ [((x, y), Just False)])

parseInput :: String -> IO Seating
parseInput filename = do
  input <- readFile filename
  let ls = lines input
      height = (length ls) - 1
      width = (length (ls !! 0)) - 1
  return $ A.array  ((0,0), (width, height))  (inputToArrayAssociations input)

compute :: Seating -> Int
compute seating
  | seating == seating' = length . filter id . catMaybes $ A.elems seating
  | otherwise = compute seating'
  where
    (width, height) = snd $ A.bounds seating
    seating' :: Seating
    seating' = seating A.// (foldl' next [] (A.assocs seating))
    next :: [((Int, Int), Maybe Bool)] -> ((Int, Int), Maybe Bool) -> [((Int, Int), Maybe Bool)]
    next acc ((x, y), Just False) = ((x, y), Just $ around (x, y) == []) : acc
    next acc ((x, y), Just True) = ((x, y), Just $ length (around (x,y)) < 4) : acc
    next acc _ = acc
    around :: (Int, Int) -> [Bool]
    around (x, y) = filter id . catMaybes $ map lookup [(x-1, y-1), (x, y-1), (x+1, y-1), (x-1, y), (x+1, y), (x-1, y+1), (x, y+1), (x+1, y+1)]
    lookup :: (Int, Int) -> Maybe Bool
    lookup (x, y)
      | x < 0 || y < 0 || x > width || y > height = Nothing
      | otherwise = seating A.! (x, y)

main :: IO ()
main = do
  example <- parseInput "example"
  let exampleOutput = compute example
  when  (exampleOutput /= exampleExpectedOutput)  (die $ "example failed: got " ++ show exampleOutput ++ " instead of " ++ show exampleExpectedOutput)
  input <- parseInput "input"
  print $ compute input