summaryrefslogtreecommitdiff
path: root/2020/11.hs
blob: 2d573be83802315cbfa5996c131acf0b9a7a99e2 (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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
{-# LANGUAGE LambdaCase #-}
module Main where

import Data.List (find, findIndex, intercalate)
import Data.Maybe (catMaybes, fromJust)
import qualified Data.Vector as V
import qualified Data.Vector.Storable as U
import Data.Word (Word8)
import Foreign.Ptr (Ptr, castPtr)
import Foreign.Storable (Storable(..))

import Input


data Cell = Floor | Seat | Occupied
  deriving (Show, Eq, Enum)

instance Storable Cell where
    sizeOf _ = sizeOf (undefined :: Word8)
    alignment _ = alignment (undefined :: Word8)
    peek p = toEnum . fromIntegral <$> peek (castPtr p :: Ptr Word8)
    poke p x = poke (castPtr p :: Ptr Word8) (fromIntegral (fromEnum x))

parseCell :: Char -> Cell
parseCell '.' = Floor
parseCell 'L' = Seat
parseCell '#' = Occupied
parseCell _ = error "Cannot parse cell"

showCell :: Cell -> Char
showCell Floor = '.'
showCell Seat = 'L'
showCell Occupied = '#'

data Grid = Grid Int Int (U.Vector Cell)
  deriving (Show, Eq)

parseGrid :: [String] -> Grid
parseGrid lns =
    let w = length (head lns)
        h = length lns
    in Grid w h (U.fromListN (w * h) (concatMap (map parseCell) lns))

showGrid :: Grid -> String
showGrid (Grid w h v) =
    intercalate "\n" [[showCell (v U.! (w * y + x)) | x <- [0..w-1]]
                     | y <- [0..h-1]]

numOccupied :: Grid -> Int
numOccupied (Grid _ _ v) = sum (map (fromEnum . (== Occupied)) (U.toList v))

evolve :: Grid -> ([Cell] -> Maybe Int) -> Int -> Grid -> Grid
evolve (Grid w h template) nbFunc scareCount =
    \(Grid _ _ v) ->
        let newCellAt x y =
                let neighbours = [v U.! (w * y' + x') | (x', y') <- nbIndicesArr V.! (w * y + x)]
                    filledNbs = sum (map (fromEnum . (== Occupied)) neighbours)
                in case v U.! (w * y + x) of
                     Floor -> Floor
                     Seat | filledNbs == 0 -> Occupied
                          | otherwise -> Seat
                     Occupied | filledNbs >= scareCount -> Seat
                              | otherwise -> Occupied
        in Grid w h (U.generate (w * h) (\i -> newCellAt (i `rem` w) (i `quot` w)))
  where
    inBounds (x, y) = 0 <= x && x < w && 0 <= y && y < h
    nbIndices x y = catMaybes
                      [(sight !!) <$> nbFunc [template U.! (w * y' + x') | (x', y') <- sight]
                      | dy <- [-1..1], dx <- [-1..1]
                      , dx /= 0 || dy /= 0
                      , let sight = takeWhile inBounds [(x+i*dx, y+i*dy) | i <- [1..]]]
    nbIndicesArr = V.generate (w * h) (\i -> nbIndices (i `rem` w) (i `quot` w))


equilibriumFor :: Eq a => (a -> a) -> a -> a
equilibriumFor f x = fst (fromJust (find (uncurry (==)) (zip xs (tail xs))))
  where xs = iterate f x

main :: IO ()
main = do
    input <- getInput 11
    let initGrid = parseGrid input
        evolve1 = evolve initGrid (\case Seat:_ -> Just 0 ; _ -> Nothing) 4
        evolve2 = evolve initGrid (findIndex (== Seat)) 5
    print (numOccupied (equilibriumFor evolve1 initGrid))
    print (numOccupied (equilibriumFor evolve2 initGrid))