{-# LANGUAGE ConstraintKinds, DataKinds, TypeFamilies, TypeOperators, DeriveFunctor #-} {-# LANGUAGE FlexibleContexts, ScopedTypeVariables, BangPatterns, ViewPatterns #-} import System.IO import System.IO.Unsafe import System.Timeout import Control.Applicative import Control.Arrow (first, second, (&&&), (***)) import Control.DeepSeq import Control.Monad import Control.Monad.Reader import Control.Monad.State import Control.Monad.Writer import Control.Monad.Trans.Reader (ReaderT) import Control.Monad.Trans.State (StateT) import Control.Monad.Trans.Writer (WriterT) import Control.Monad.Trans.List --import Control.Comonad import Data.Function import Data.List import Data.Maybe import Data.Tuple import Debug.Trace import Data.Proxy import GHC.TypeLits main :: IO () main = do hSetBuffering stdout NoBuffering -- DO NOT REMOVE phrase <- toRunes <$> getLine sols <- mapM (\f -> timeout 400000 $ return $!! f phrase) [solveIt_1, solveIt_2] putStrLn $ minimumBy (compare `on` length) $ catMaybes sols tryIt :: ([Rune] -> String) -> String -> IO Bool tryIt solveIt phrase = (result == phrase) <$ mapM_ print [program, result, show steps] where program = solveIt $ toRunes phrase (result, Sum steps) = fst $ simulate program initZones {------ Version 2 ------} newtype Rune = Rune Char deriving (Eq,Show) solveIt_2 :: [Rune] -> String solveIt_2 = execBilbo . solveIt_2' solveIt_2' :: [Rune] -> Bilbo () solveIt_2' [] = return () solveIt_2' [r] = putRune r solveIt_2' rs@(a:b:_) = bestM *> solveIt_2' bestRst where diff = distance a b ls = (a:) $ map snd $ takeWhile ((== diff) . fst) $ zipWith (\h t -> (distance h t, t)) rs (tail rs) rst1 = drop (length ls) rs moves1 = [ (bindVar $ \v -> selectRune v a *> triggerRune v *> repFn (length ls - 1) (changeRune v a b *> triggerRune v), rst1) | repFn <- [replicateM_, loopExpr] ] moves2 = [ (withRunes xs (\vs -> loopExpr n $ mapM_ triggerRune vs), ys) | (xs, n, ys) <- repeats rs, n > 2 * length xs, length xs < 16 ] moves3 = [ (putPattern p, ys) | (p, ys) <- unsafeTimeoutList 100000 $ patterns rs ] moves4 = [ (loopExpr n (solveIt_2' xs), ys) | (xs, n, ys) <- repeats rs ] score (m, rst) = length (execBilbo m) + (10 * length rst) candidates = [ (score c, c) | c@(_, rst) <- moves1 ++ moves2 ++ moves3 ++ moves4, rst /= rs ] (_, (bestM, bestRst)) = minimumBy (compare `on` fst) candidates -- If at any point it takes more than `t` microseconds to compute the next -- element of the list (to WHNF), abandon the calculation and terminate -- the list. Note that this causes the length of the result to depend -- on timing and system load. Marked "unsafe" for a reason! unsafeTimeoutList :: Int -> [a] -> [a] unsafeTimeoutList t xs = unsafePerformIO $ do r <- timeout t $ return $! case xs of [] -> [] (a:as) -> a `seq` (a:as) return $ case r of Nothing -> [] Just [] -> [] Just (a:as) -> (a : unsafeTimeoutList t as) type MonadBilbo m = (Applicative m, MonadWriter String m, MonadState Int m, MonadReader Int m) type Bilbo a = ReaderT Int (StateT Int (Writer String)) a execBilbo :: Bilbo a -> String execBilbo m = execWriter $ runStateT (runReaderT m 0) 0 bindVar :: MonadBilbo m => (Int -> m a) -> m a bindVar f = ask >>= \v -> local (+1) (f v) atVar :: MonadBilbo m => Int -> String -> m () atVar v cs = moveTo v *> tell cs loopAtVar :: MonadBilbo m => Int -> m a -> m a loopAtVar v m = atVar v "[" *> m <* atVar v "]" loopExpr :: MonadBilbo m => Int -> m () -> m () loopExpr 0 m = return () loopExpr 1 m = m loopExpr n m | n >= 27 = bindVar $ \lv -> do selectRune lv $ toEnum $ n `mod` 26 when (n `mod` 26 == 0) $ decRune lv loopExpr (1 + ((n - 1) `div` 26)) $ do loopAtVar lv (m *> decRune lv) decRune lv loopExpr n m | n >= 10 && n <= 17 = bindVar $ \lv -> do selectRune lv $ toEnum (2 * n `mod` 27) loopAtVar lv (m *> decRune lv *> decRune lv) loopExpr n m = bindVar $ \lv -> do selectRune lv $ toEnum n loopAtVar lv (m *> decRune lv) moveLeft, moveRight :: MonadBilbo m => m () moveLeft = modify (\n -> (n + 26) `mod` 27) *> tell "<" moveRight = modify (\n -> (n + 28) `mod` 27) *> tell ">" clearAllRunes :: MonadBilbo m => m () clearAllRunes = put 0 *> tell "[-]+++[>[-]>[-]<<[->>+<<]>>--]" moveTo :: MonadBilbo m => Int -> m () moveTo v = get >>= moveFrom where moveFrom n | v >= n = replicateM_ (v - n) moveRight | otherwise = replicateM_ (n - v) moveLeft clearRune, incRune, decRune, triggerRune :: MonadBilbo m => Int -> m () clearRune v = atVar v "[-]" incRune v = atVar v "+" decRune v = atVar v "-" triggerRune v = atVar v "." putRune :: MonadBilbo m => Rune -> m () putRune r = bindVar $ \v -> selectRune v r *> triggerRune v selectRune :: MonadBilbo m => Int -> Rune -> m () selectRune v r = clearRune v *> changeRune v (Rune ' ') r withRunes :: MonadBilbo m => [Rune] -> ([Int] -> m a) -> m a withRunes [] f = f [] withRunes (r:rs) f = bindVar $ \v -> selectRune v r *> withRunes rs (f . (v:)) changeRune :: MonadBilbo m => Int -> Rune -> Rune -> m () changeRune v a b = if d1 < d2 then replicateM_ d1 (decRune v) else replicateM_ d2 (incRune v) where d1 = ((fromEnum a - fromEnum b) + 27) `mod` 27 d2 = ((fromEnum b - fromEnum a) + 27) `mod` 27 simulate :: String -> Zones -> ((String, Sum Int), Zones) simulate cs zs = swap $ runWriter $ execStateT (go cs) zs where go [] = return () go (c:cs) = tell ("", Sum 1) >> case c of '.' -> do { Rune r <- extractC <$> get; tell ([r], Sum 0); go cs } '<' -> modify rotateRight >> go cs '>' -> modify rotateLeft >> go cs '+' -> modify (modifyC succ) >> go cs '-' -> modify (modifyC pred) >> go cs '[' -> let (inside, after) = splitBrackets cs in fix $ \loop -> get >>= \zs' -> if extractC zs' /= Rune ' ' then go inside >> tell ("", Sum 1) >> loop else go after splitBrackets :: String -> (String, String) splitBrackets cs = go cs 0 where go [] _ = error "splitBrackets: unmatched brackets" go (']':cs) 0 = ("", cs) go (c:cs) n = first (c:) $ go cs $ case c of { '[' -> n+1; ']' -> n-1; _ -> n } moveRune :: MonadBilbo m => Int -> [Int] -> m () moveRune s ts = mapM_ clearRune ts *> loopAtVar s (mapM_ incRune ts *> decRune s) copyRune :: MonadBilbo m => Int -> [Int] -> m () copyRune s ts = bindVar $ \v -> moveRune s (v:ts) *> moveRune v [s] switch :: MonadBilbo m => Int -> [m ()] -> m () switch cv [] = clearRune cv switch cv ms = bindVar $ \fv -> clearRune fv *> incRune fv *> go fv ms where go fv [m] = m *> decRune fv *> clearRune cv go fv (m:ms) = loopAtVar cv (decRune cv *> go fv ms) *> loopAtVar fv (m *> decRune fv) ifThenElse :: MonadBilbo m => Int -> m () -> m () -> m () ifThenElse cv t f = switch cv [f, t] ifThen :: MonadBilbo m => Int -> m a -> m a ifThen cv t = loopAtVar cv (t <* clearRune cv) whileLoop :: MonadBilbo m => (Int -> m ()) -> m () -> m () whileLoop fc m = bindVar $ \v -> selectRune v (toEnum 1) *> loopAtVar v (bindVar $ \v2 -> fc v2 *> ifThenElse v2 m (decRune v)) instance Enum Rune where toEnum 0 = Rune ' ' toEnum n | n >= 1 && n <= 26 = Rune (toEnum (fromEnum 'A' + (n - 1))) | otherwise = error $ "Rune.toEnum: bad argument: " ++ show n fromEnum (Rune ' ') = 0 fromEnum (Rune c) = fromEnum c - fromEnum 'A' + 1 succ (Rune ' ') = Rune 'A' succ (Rune 'Z') = Rune ' ' succ (Rune c ) = Rune (succ c) pred (Rune ' ') = Rune 'Z' pred (Rune 'A') = Rune ' ' pred (Rune c ) = Rune (pred c) toRunes :: String -> [Rune] toRunes = map Rune fromRunes :: [Rune] -> String fromRunes = map (\(Rune c) -> c) {------ Version 1 ------} type Zones = Circular 30 Rune initZones :: Zones initZones = pure $ Rune ' ' solveIt_1 :: [Rune] -> String solveIt_1 phrase = loop phrase initZones loop :: [Rune] -> Zones -> String loop [] _ = "" loop phrase zones = moves' where diff = if null (tail phrase) then "" else incDecMoves (phrase !! 0) (phrase !! 1) seqn = head phrase : map snd (takeWhile ((== diff) . fst) (zipWith (\h t -> (incDecMoves h t, t)) phrase (tail phrase))) (moves, zones') = findNearest (head seqn) zones (rep, (setup, step, count)) = if length seqn >= 15 then (3, setCounter (extractC $ rotateRight zones') $ length seqn `div` 3) else (1, setCounter (extractC $ rotateRight zones') $ length seqn) inner = concat $ replicate rep ('.':diff) loopMoves = ">" ++ setup ++ "[<" ++ inner ++ ">" ++ step ++ "]" zones'' = modifyC (const $ Rune ' ') $ rotateRight $ snd $ simulate (concat (replicate (count * rep) diff)) zones' moves' = if length loopMoves < count * rep + (count * rep - 1) * length diff then moves ++ loopMoves ++ loop (drop (count * rep) phrase) zones'' else moves ++ "." ++ loop (tail phrase) zones' findNearest :: Rune -> Zones -> (String, Zones) findNearest r zones = first (tail . reverse) $ head $ filter ((== r) . extractC . snd) bfs where bfs = ("$", zones) : concatMap next bfs next :: (String, Zones) -> [(String, Zones)] next (ms, zs) = case head ms of '$' -> [ left, right, inc, dec ] '<' -> [ left, inc, dec ] '>' -> [ right, inc, dec ] '+' -> [ inc ] '-' -> [ dec ] where left = ('<':ms, rotateLeft zs) right = ('>':ms, rotateRight zs) inc = ('+':ms, modifyC succ zs) dec = ('-':ms, modifyC pred zs) setCounter :: Rune -> Int -> (String, String, Int) setCounter c n | n > 26 = setCounter c 26 | otherwise = minimumBy (compare `on` (\(x, y, _) -> length x + length y)) $ catMaybes $ [ (,,) <$> (incDecMoves c <$> fromCounter n '+') <*> pure "+" <*> pure n , (,,) <$> (incDecMoves c <$> fromCounter n '-') <*> pure "-" <*> pure n , (,,) <$> (incDecMoves c <$> fromCounter (2 * n2) '+') <*> pure "++" <*> pure n2 , (,,) <$> (incDecMoves c <$> fromCounter (2 * n2) '-') <*> pure "--" <*> pure n2 ] where n2 = if even n then n else n - 1 fromCounter :: Int -> Char -> Maybe Rune fromCounter 0 _ = Just $ toEnum 0 fromCounter n _ | n < 1 || n > 26 = Nothing fromCounter n '+' = Just $ toEnum (27 - n) fromCounter n '-' = Just $ toEnum n distance :: Rune -> Rune -> (Int, Char) distance a b = if d1 < d2 then (d1, '-') else (d2, '+') where d1 = ((fromEnum a - fromEnum b) + 27) `mod` 27 d2 = ((fromEnum b - fromEnum a) + 27) `mod` 27 incDecMoves :: Rune -> Rune -> String incDecMoves from to = uncurry replicate $ distance from to {----- Fixed-size cyclic lists -----} data Circular (n :: Nat) a = Cycle [a] a [a] deriving (Eq,Show,Functor) rotateLeft, rotateRight :: (KnownNat n, 1 <= n) => Circular n a -> Circular n a rotateLeft (Cycle l x []) = let (x':r') = reverse l in Cycle [x] x' r' rotateLeft (Cycle l x (x':r')) = Cycle (x:l) x' r' rotateRight (Cycle [] x r) = let (x':l') = reverse r in Cycle l' x' [x] rotateRight (Cycle (x':l') x r) = Cycle l' x' (x:r) extractC :: (KnownNat n, 1 <= n) => Circular n a -> a extractC (Cycle _ x _) = x deleteC :: (KnownNat n, 1 <= n) => Circular (n+1) a -> (a, Circular n a) deleteC (Cycle (x':l') x r) = (x, Cycle l' x' r) deleteC (Cycle [] x (x':r')) = (x, Cycle [] x' r') insertC :: (KnownNat n, 1 <= n) => a -> Circular n a -> Circular (n+1) a insertC x' (Cycle l x r) = Cycle l x' (x:r) modifyC :: (KnownNat n, 1 <= n) => (a -> a) -> Circular n a -> Circular n a modifyC f (Cycle l x r) = Cycle l (f x) r fillC :: (KnownNat n, 1 <= n) => [a] -> Circular n a -> Circular n a fillC xs (Cycle l x r) = Cycle [] x' r' where (x':r') = zipWith ($) (map const xs ++ repeat id) ((x:r) ++ reverse l) instance (KnownNat n, 1 <= n) => Applicative (Circular n) where pure x = Cycle [] x $ replicate (fromInteger (natVal (Proxy :: Proxy n)) - 1) x (Cycle fl f fr) <*> (Cycle xl x xr) = Cycle [] (f x) (zipWith ($) fr' xr') where fr' = fr ++ reverse fl xr' = xr ++ reverse xl {- instance (KnownNat n, 1 <= n) => Comonad (Circular n) where extract c = extractC c duplicate c = Cycle [] c (take (fromInteger (natVal (Proxy :: Proxy n)) - 1) cs) where cs = map rotateLeft (c:cs) -} {--------- Utility Functions --------} repeats :: Eq a => [a] -> [([a], Int, [a])] repeats as = go (length as `div` 2) where go 0 = [] go n = let (xs, ys) = splitAt n as m = length (takeWhile id (zipWith (==) (cycle xs) as)) `div` n in if m >= 2 then (xs, m, drop (m * length xs) as) : go (n - 1) else go (n - 1) patterns :: [Rune] -> [([[[Rune]]], [Rune])] patterns [] = [] patterns xs = do n <- [1 .. length xs `div` 2] let (cs, xs') = chunksOf n xs take 1 $ do ns <- takeWhile ((<= 4) . length) $ addsUpTo n let cs' = map (fst . splitAts ns) cs let m = countSame $ takeWhile (all isJust) $ map (map fullSeq) cs' guard $ m >= 2 let (cs'', ncs) = splitAt m cs' let xs'' = concat (map concat ncs) ++ xs' let seqHeads = transpose $ map (map head) cs'' let m' = let ms = m : map seqLength (zipWith take ms seqHeads) in last ms guard $ m' >= 2 let (cs''', ncs') = splitAt m' cs'' let xs''' = concat (map concat ncs') ++ xs'' return (cs''', xs''') putPattern :: MonadBilbo m => [[[Rune]]] -> m () putPattern ps = withRunes (map head $ head ps) $ \vs -> do loopExpr (length ps) $ do forM_ (zip vs (zip (ps!!0) (ps!!1))) $ \(v, (rs, (c:_))) -> do triggerRune v loopExpr (length rs - 1) $ do changeRune v (rs!!0) (rs!!1) triggerRune v changeRune v (last rs) c chunksOf :: Int -> [a] -> ([[a]], [a]) chunksOf n xs = if length as < n then ([], as) else first (as:) (chunksOf n bs) where (as, bs) = splitAt n xs seqLength :: [Rune] -> Int seqLength [] = 0 seqLength xs = (1+) $ countSame $ zipWith minusRune (tail xs) xs countSame :: Eq a => [a] -> Int countSame [] = 0 countSame [x] = 1 countSame (x:y:xs) = if x == y then 1 + countSame (y:xs) else 1 fullSeq :: [Rune] -> Maybe Int fullSeq xs = if seqLength xs == length xs then Just (seqDiff xs) else Nothing seqDiff :: [Rune] -> Int seqDiff [] = 0 seqDiff [x] = 0 seqDiff (x:y:_) = y `minusRune` x minusRune :: Rune -> Rune -> Int a `minusRune` b = (27 + fromEnum a - fromEnum b) `mod` 27 -- All the list of numbers in [1..m] which add up to m, including permutations. -- Sorted shortest to longest, then lexographically descending. -- addsUpTo 3 ==> [[3],[2,1],[1,2],[1,1,1]] addsUpTo :: (Integral a) => a -> [[a]] addsUpTo m = concat [ go m n | n <- [1..m] ] where go m 1 = [[m]] go m n | n > 1 = concat [ map (x:) (go (m-x) (n-1)) | x <- [m-(n-1),m-n..1] ] splitAts :: [Int] -> [a] -> ([[a]], [a]) splitAts [] xs = ([], xs) splitAts (a:as) xs = (bs : ds, xs') where (bs,cs) = splitAt a xs (ds,xs') = splitAts as cs sublists :: [a] -> [[[a]]] sublists xs = map (fst . flip splitAts xs) $ addsUpTo (length xs) dropLast, takeLast :: Int -> [a] -> [a] dropLast n xs = zipWith const xs (drop n xs) takeLast n xs = foldl' (\xs' _ -> tail xs') xs (drop n xs) lengthGT :: Int -> [a] -> Bool lengthGT n xs = (n < 0) || not (null (drop n xs)) accum :: (a -> b -> a) -> a -> [b] -> [a] accum _ _ [] = [] accum f a (b:bs) = let a' = f a b in seq a' (a' : accum f a' bs) accum1 :: (a -> a -> a) -> [a] -> [a] accum1 f [] = [] accum1 f (a:as) = a : accum f a as cumsum, cumproduct :: Num a => [a] -> [a] cumsum = accum1 (+) cumproduct = accum1 (*)