import Data.List.Split import Data.List.Unique import Data.Char as Char import Data.List as List import Data.Either as Either import Debug.Trace as Trace import Data.Maybe main = do mapIn <- lines <$> getContents --let brain = Amplifier software 0 0 [] [0] --let robot = Robot mapIn [((5,1),64)] (5,1) 1 --let resultRobots = (runRobot robot ) --let reachablePoints = getNextKey resultRobots --let nextKey = head (sortBy sortLength reachablePoints) --let newMap = openGate mapIn nextKey let result = getAllKeys mapIn (15,1) [] --let aRobotLength = List.minimum( map (length . points) $ aRobots) --let aRobotWin = filter(\(Robot brain points position direction) -> length points == aRobotLength) aRobots --let winRobots = filter(\(Robot br pts pos dir) -> elem 2 (map(\(p,c) -> c) pts))resultRobots --let winRobotsLength = map(\(Robot br pts pos dir) -> length (filter(\(p,c) -> c == 1)pts))winRobots --let winRobot = (map(\(Robot br pts pos dir) -> Robot br [] pos 1) winRobots) !! 0 --let part2Robots = (runRobot winRobot) --let part2Length = map(\(Robot br pts pos dir) -> length (filter(\(p,c) -> c == 1)pts))part2Robots --putStrLn(show winRobotsLength) --putStrLn(show $ List.maximum( part2Length)) mapM putStrLn(map show mapIn) putStrLn(show result) --putStrLn(printKey nextKey) --mapM putStrLn(map show resultRobots) data Robot = Robot{ brain:: [[Char]] ,points:: [((Int,Int),Int)] ,position:: (Int,Int) ,direction :: Int } deriving Show data Key = Key { id :: Int, pos :: (Int,Int), way :: Int } deriving (Show, Eq) getAllKeys :: [[Char]] -> (Int,Int) -> [Key] -> [[Key]] getAllKeys mapIn (a,b) keys = do let robot = Robot (Trace.traceShowId(mapIn)) [((a,b),64)] (a,b) 1 let resultRobots = (runRobot robot ) let reachablKeys = (Trace.traceShowId(getNextKey (resultRobots))) if length reachablKeys /= 0 then do let nextKey = head (sortBy sortLength reachablKeys) let newMap = openGate mapIn (Trace.traceShowId(nextKey)) let newKeys = nextKey:keys getAllKeys newMap (pos nextKey) newKeys else keys sortLength :: Key -> Key -> Ordering sortLength (Key _ _ way1) (Key _ _ way2) | way1 == way2 = EQ | way1 < way2 = LT | way1 > way2 = GT openGate :: [[Char]] -> Key -> [[Char]] openGate mapIn (Key id (a,b) _) = result where result' = map( map(\c -> if c==(chr id) then '.' else c)) mapIn result = map( map(\c -> if c==(chr (id-32)) then '.' else c)) result' printKey :: Key -> [Char] printKey (Key id pos way) = (show id)++" :"++(show pos)++(show way) getBrain :: Robot -> [[Char]] getBrain (Robot brain points poisition direction) = brain getNextKey :: [Robot] -> [Key] getNextKey robots = nub minPts where kPts = map(\(Robot brain points position direction) ->(Key (snd(last points)) (fst(last points)) (length points))) robots minPts = map(\(Key id pos way) ->(Key id pos (min id))) keyWPr min x = List.minimum $ map(\(Key id pos way) -> way) $ filter(\(Key id pos way) -> id == x) keyWPr keys = filter(\(Key id pos way) -> id /= 46 && id /= 64 && between 97 id 122) kPts keyWPr = keys runRobot :: Robot -> [Robot] runRobot robot | (length move) == 0 = [robot] | (length move) == 1 = do let newRobot = stepRobot robot $ move!!0 runRobot newRobot | otherwise = do let newRobots = map(\mv -> stepRobot robot mv) move foldl (++) [] $ map(\robot -> runRobot robot) newRobots where move = getNextMove robot stepRobot :: Robot -> Int -> Robot stepRobot (Robot brain points position direction) newDirection = do let newPos = move position newDirection let statusResponse = ord ((brain!! (snd newPos))!! (fst newPos)) let newPoints = (points) ++ [(newPos, statusResponse)] if statusResponse == 35 || between 65 statusResponse 90 || between 97 statusResponse 122 then Robot brain newPoints position newDirection else Robot brain newPoints newPos newDirection between :: Int -> Int -> Int -> Bool between x y z |x <= y = y <= z |otherwise = False move :: (Int,Int) -> Int -> (Int,Int) move (x,y) direction | direction == 1 = (x,y-1) | direction == 2 = (x+1,y) | direction == 3 = (x,y+1) | direction == 4 = (x-1,y) getNextMove :: Robot -> [Int] getNextMove (Robot brain points position direction) |length points > 0 && (snd $ last points) == 2 = [] |otherwise = do filterMoves (Robot brain points position direction) [1,2,3,4] filterMoves :: Robot -> [Int] -> [Int] filterMoves robot moves = filter(\x -> checkVisit robot x && checkWall robot x) moves checkVisit :: Robot -> Int -> Bool checkVisit (Robot brain points position direction) mv = do let newPos = move position mv let visits = map(\(pos,c) -> pos) points notElem newPos visits checkWall :: Robot -> Int -> Bool checkWall (Robot brain points position direction) mv = do let newPos = (move position mv) let mvResult = ( ord ((brain!! (snd newPos))!! (fst newPos))) not (mvResult == 35 ) getList :: String -> [Int] getList = map Prelude.read . splitOn ","