Advent of Code 2019 Repository
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

55 lines
2.5 KiB

import System.Environment
import Data.Char
import Data.List
import Data.List.Split
import Data.Maybe
import qualified Data.Set as Set
import qualified Data.Map.Strict as Map
parse :: [String] -> Map.Map (Int, Int) Char
parse strings = Map.fromList [((y, x), char) | (y,row) <- zip [0..] strings, (x,char) <- zip [0..] row ]
cardinal :: (Int, Int) -> [(Int, Int)]
cardinal (y, x) = [(y,x+1),(y,x-1),(y+1,x),(y-1,x)]
findPortal :: Map.Map (Int, Int) Char -> Char -> [(Int, Int)]
findPortal m c = intercalate [] $ map (\k -> filter (\newK -> (Map.findWithDefault '#' newK m) == '.') $ surrounding k) a
where
surrounding l = cardinal l ++ (cardinal $ head $ filter (\newL -> (\i -> isUpper i || isLower i) (Map.findWithDefault '#' newL m)) $ cardinal l)
a = Map.keys $ Map.filter (==c) m
getPortals :: Map.Map (Int, Int) Char -> Map.Map Char [(Int, Int)]
getPortals m = Map.fromList $ map (\c -> (c, findPortal m c)) $ nub $ Map.elems $ Map.filter (\i -> isUpper i || isLower i) m
enterPortal :: Map.Map Char [(Int, Int)] -> Char -> (Int, Int) -> (Int, Int)
enterPortal portals c loc = if found == [] then loc else head found
where found = filter (/=loc) $ portals Map.! c
step :: Map.Map (Int, Int) Char -> Map.Map Char [(Int, Int)] -> ((Int, Bool), ((Int, Int), Set.Set (Int, Int))) -> [((Int, Bool), ((Int, Int), Set.Set (Int, Int)))]
step m portals ((steps, done), (loc, been)) =
foldl (\t (y,x) -> do
let here = Map.findWithDefault '#' (y,x) m
t ++ (if Set.member (y,x) been then []
else if here == '.' then [((steps+1,done),((y,x), Set.insert (y,x) been))]
else if (\i -> isUpper i || isLower i)here then
if here == 'Z' then [((steps,True),(loc,been))]
else do
let (newY,newX) = enterPortal portals here loc
[((steps+1,done),((newY,newX), Set.insert (newY,newX) been))]
else [])) [] (cardinal loc)
stepAll :: Map.Map (Int, Int) Char -> Map.Map Char [(Int, Int)] -> [((Int, Bool), ((Int, Int), Set.Set (Int, Int)))] -> Int
stepAll m portals (((steps, done), (loc, been)):rest) = if any (\((_, done),_) -> done) nextSteps then fst $ fst $ head $ nextSteps
else stepAll m portals (rest ++ nextSteps)
where
fullBeen = foldl (\t (_, (_, b)) -> Set.union t b) been rest
nextSteps = step m portals ((steps, done), (loc, fullBeen))
main = do
input <- readFile "input"
let parsed = parse $ lines input
let start = head $ findPortal parsed 'A'
let portals = getPortals parsed
-- print portals
print $ stepAll parsed portals [((0, False), (start, Set.singleton start))]