62 lines
1.8 KiB
Haskell
62 lines
1.8 KiB
Haskell
import Data.Function (fix)
|
|
import Data.List (find)
|
|
import Data.Maybe (isJust)
|
|
import Data.Array
|
|
import Debug.Trace
|
|
|
|
import System.IO.Unsafe
|
|
|
|
type State = Array (Int, Int) Char
|
|
|
|
dirs :: [(Int, Int)]
|
|
dirs = [(x, y) | x <- [-1..1], y <- [-1..1], (x, y) /= (0, 0)]
|
|
|
|
traverseDirection :: State -> (Int, Int) -> (Int, Int) -> Maybe Char
|
|
traverseDirection state from (dx, dy) =
|
|
find (const True) $ filter g $ map (state !) $ filter (inRange (bounds state)) lineCoords
|
|
where
|
|
g c = c /= '.'
|
|
lineCoords = iterate (\(x, y) -> (x + dx, y + dy)) from
|
|
|
|
adjacentOccupied :: State -> (Int, Int) -> Int
|
|
adjacentOccupied state from =
|
|
length . filter occupied $ map f dirs
|
|
where
|
|
occupied (Just '#') = True
|
|
occupied _ = False
|
|
f = traverseDirection state from
|
|
|
|
nextChar :: State -> (Int, Int) -> Char -> Char
|
|
nextChar state from c =
|
|
let n = adjacentOccupied state from in
|
|
if c == 'L' && (traceShow ("from=", from, "c=", c, "n=", n) n) == 0 then '#' else
|
|
if c == '#' && n >= 5 then 'L' else c
|
|
|
|
nextState :: State -> State
|
|
nextState state = state // map f (assocs (traceShow state state))
|
|
where f (idx, ch) = (idx, nextChar state idx ch)
|
|
|
|
advanceState :: (State -> State) -> State -> State
|
|
advanceState rec state = let next = nextState state in
|
|
if state == next then state
|
|
else rec next
|
|
|
|
countOccupied :: State -> Int
|
|
countOccupied = length . filter (== '#') . elems
|
|
|
|
convertToState :: [String] -> State
|
|
convertToState lines =
|
|
let lr = length lines in
|
|
let lc = length (lines !! 0) in
|
|
listArray ((0, 0), (lr - 1, lc - 1)) (concat lines)
|
|
|
|
solution2 :: [String] -> Int
|
|
solution2 = countOccupied . fix advanceState . convertToState
|
|
|
|
st = unsafePerformIO $ convertToState <$> lines <$> readFile "11ex.txt"
|
|
|
|
main :: IO Int
|
|
main = do
|
|
input <- lines <$> readFile "11ex.txt"
|
|
return (solution2 input)
|