60 lines
2.3 KiB
Haskell
60 lines
2.3 KiB
Haskell
module Commons where
|
|
|
|
import Data.List.Utils (split)
|
|
import GHC.IO.Handle (isEOF)
|
|
import qualified Data.Map.Strict as M
|
|
|
|
|
|
data Cube = Cube { z :: Int, y :: Int, x :: Int } deriving (Eq, Ord, Show)
|
|
type Brick = [Cube]
|
|
type Snapshot = M.Map (Int, Int, Int) Brick
|
|
|
|
|
|
parseBrick :: [Int] -> [Int] -> Brick
|
|
parseBrick [x1, y1, z1] [x2, y2, z2] = Cube {x = x1, y = y1, z = z1}:
|
|
if x1 < x2 then parseBrick [x1 + 1, y1, z1] [x2, y2, z2]
|
|
else if x1 > x2 then parseBrick [x1, y1, z1] [x2 + 1, y2, z2]
|
|
else if y1 < y2 then parseBrick [x1, y1 + 1, z1] [x2, y2, z2]
|
|
else if y1 > y2 then parseBrick [x1, y1, z1] [x2, y2 + 1, z2]
|
|
else if z1 < z2 then parseBrick [x1, y1, z1 + 1] [x2, y2, z2]
|
|
else if z1 > z2 then parseBrick [x1, y1, z1] [x2, y2, z2 + 1]
|
|
else []
|
|
|
|
parseLine :: String -> [((Int, Int, Int), Brick)]
|
|
parseLine line = let (start: end: _) = split "~" line
|
|
parsedStart = map read $ split "," start
|
|
parsedEnd = map read $ split "," end
|
|
brick = parseBrick parsedStart parsedEnd
|
|
in map (\ c -> ((z c, y c, x c), brick)) brick
|
|
|
|
parseSnapshot :: [((Int, Int, Int), Brick)] -> IO Snapshot
|
|
parseSnapshot otherBricks = do done <- isEOF
|
|
if done
|
|
then return $ M.fromList otherBricks
|
|
else do line <- getLine
|
|
let brick = parseLine line
|
|
parseSnapshot (otherBricks ++ brick)
|
|
|
|
parse :: IO Snapshot
|
|
parse = parseSnapshot []
|
|
|
|
|
|
getFalling :: Snapshot -> [((Int, Int, Int), Brick)]
|
|
getFalling s = M.toList $ M.filter (\ b ->
|
|
let minZ = foldl (\ mz c -> min mz (z c)) maxBound b
|
|
in foldl (\ r c -> r && minZ > 1 && M.notMember (minZ - 1, y c, x c) s) True b) s
|
|
|
|
getFell :: Snapshot -> [((Int, Int, Int), Brick)] -> [((Int, Int, Int), Brick)]
|
|
getFell s = map (\ ((zk, yk, xk), v) ->
|
|
((zk - 1, yk, xk), map (\ c -> Cube {z = z c - 1, y = y c, x = x c}) v))
|
|
|
|
getPostFall :: Snapshot -> [((Int, Int, Int), Brick)] -> [((Int, Int, Int), Brick)] -> Snapshot
|
|
getPostFall s falling fell = M.union (M.difference s $ M.fromList falling) (M.fromList fell)
|
|
|
|
fall :: Snapshot -> Snapshot
|
|
fall s =
|
|
let falling = getFalling s
|
|
fell = getFell s falling
|
|
in if null falling then s
|
|
else fall $ getPostFall s falling fell
|