66 lines
2.1 KiB
Haskell
66 lines
2.1 KiB
Haskell
import Common hiding (Grid)
|
|
import Data.List (foldl', elemIndices)
|
|
import Data.Maybe (mapMaybe, catMaybes)
|
|
|
|
data Direction = North | West | East | South
|
|
deriving (Eq, Show, Enum)
|
|
|
|
parse :: String -> [Pos]
|
|
parse s = concat $ zipWith (\y l -> map fst $ filter (\(pos, t) -> t == '#') $ zipWith (\x t -> ((x, y), t)) [0..] l) [0..] $ lines s
|
|
|
|
directionOrder = [North, South, West, East]
|
|
|
|
consideredPositions North = [(0, -1), (-1, -1), (1, -1)]
|
|
consideredPositions South = [(0, 1), (-1, 1), (1, 1)]
|
|
consideredPositions West = [(-1, 0), (-1, -1), (-1, 1)]
|
|
consideredPositions East = [(1, 0), (1, -1), (1, 1)]
|
|
|
|
directPosition North = (0, -1)
|
|
directPosition South = (0, 1)
|
|
directPosition West = (-1, 0)
|
|
directPosition East = (1, 0)
|
|
|
|
removeDuplicatesBy :: (Eq b) => (a -> b) -> [a] -> [a]
|
|
removeDuplicatesBy f l = filter (\e -> length (f e `elemIndices` l') == 1) l
|
|
where l' = map f l
|
|
|
|
removeDuplicates :: (Eq a) => [a] -> [a]
|
|
removeDuplicates = removeDuplicatesBy id
|
|
|
|
countEmpty :: [Pos] -> Int
|
|
countEmpty elves = sum $ map (\y -> sum $ map (\x -> if (x, y) `elem` elves then 0 else 1) [minX .. maxX]) [minY .. maxY]
|
|
where
|
|
minX = minimum $ map fst elves
|
|
maxX = maximum $ map fst elves
|
|
minY = minimum $ map snd elves
|
|
maxY = maximum $ map snd elves
|
|
|
|
unwrap :: (a, Maybe b) -> Maybe (a, b)
|
|
unwrap (a, Just b) = Just (a, b)
|
|
unwrap (a, Nothing) = Nothing
|
|
|
|
step :: [Pos] -> Int -> [Pos]
|
|
step elves round = movedElves
|
|
where
|
|
movedElves = map snd newElves ++ filter (`notElem` map fst newElves) elves
|
|
|
|
newElves = removeDuplicatesBy snd $ mapMaybe unwrap $ zip elves $ map getElfPosition elves
|
|
|
|
getElfPosition :: Pos -> Maybe Pos
|
|
getElfPosition pos
|
|
| (not $ or positions) || (and positions) = Nothing
|
|
| otherwise = Just firstPos
|
|
where
|
|
firstPos = (pos `addPos`) $ directPosition firstDir
|
|
firstDir = fst $ head $ filter snd $ zip order positions
|
|
positions = map (all ((`notElem` elves) . (pos `addPos`)) . consideredPositions) order
|
|
|
|
order = map ((cycle directionOrder !!) . flip (-) 1 . (+ round)) [0..3]
|
|
|
|
roundsN = 10
|
|
|
|
main = interact $
|
|
show
|
|
. countEmpty
|
|
. (\elves -> foldl' step elves [1 .. roundsN])
|
|
. parse |